diff --git a/common.props b/common.props
index 5bdca0a..073c53d 100644
--- a/common.props
+++ b/common.props
@@ -1,8 +1,9 @@
latest
-
+ enable
enable
+ CS1591
diff --git a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs
index a2acc56..704199d 100644
--- a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs
+++ b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs
@@ -29,7 +29,6 @@ public class InterfaceForwardApiModule : FakeModule
services.AddFakeSwaggerGen()
.AddFakeExceptionFilter()
.AddFakeValidationActionFilter()
- .AddFakeUnitOfWorkActionFilter()
.AddFakeAspNetCoreAuditing();
services.AddCors(options =>
diff --git a/src/InterfaceForward.Api/appsettings.json b/src/InterfaceForward.Api/appsettings.json
index 0a4208d..f1a689d 100644
--- a/src/InterfaceForward.Api/appsettings.json
+++ b/src/InterfaceForward.Api/appsettings.json
@@ -27,11 +27,19 @@
"http://localhost:8888"
]
},
- "ConnectionStrings":{
+ "ConnectionStrings": {
"Default": "server=baget.xiaolipro.cn;database=interface_forward;uid=dev;pwd=itd!@#123;charset=utf8mb4;"
},
"FeiShuNotice": {
"Title": "InterfaceForward-Dev",
"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"
+ }
}
}
diff --git a/src/InterfaceForward.Application.Contracts/Dtos/Interface/SaveParameterMapsInput.cs b/src/InterfaceForward.Application.Contracts/Dtos/Interface/SaveParameterMapsInput.cs
index f419311..9df9a97 100644
--- a/src/InterfaceForward.Application.Contracts/Dtos/Interface/SaveParameterMapsInput.cs
+++ b/src/InterfaceForward.Application.Contracts/Dtos/Interface/SaveParameterMapsInput.cs
@@ -39,6 +39,11 @@ public class MapItem
///
public string PostfixScript { get; set; }
+ ///
+ /// 自由映射(支持不对等映射)
+ ///
+ public bool FreeMap { get; set; }
+
///
/// 入参映射明细
///
diff --git a/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs b/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs
index 2453001..ab61fb7 100644
--- a/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs
+++ b/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs
@@ -9,27 +9,27 @@ public class ForwardCoreContext
///
/// 目标服务商接口
///
- public InterfaceDto TargetInterface { get; set; }
+ public InterfaceDto TargetInterface { get; set; } = null!;
///
/// 服务商接口固定字段列表
///
- public List FixedFieldList { get; set; }
+ public List FixedFieldList { get; set; } = [];
///
/// 服务商账户字段列表
///
- public List AccountFieldList { get; set; }
+ public List AccountFieldList { get; set; } = [];
///
/// 服务商接口返回配置列表
///
- public List ReturnConfigList { get; set; }
+ public List ReturnConfigList { get; set; } = [];
///
/// 表单字段列表
///
- public List FormFieldList { get; set; }
+ public List FormFieldList { get; set; } = [];
///
/// 入参映射列表(树形结构)
@@ -44,12 +44,12 @@ public class ForwardCoreContext
///
/// 请求前置脚本
///
- public string PrefixScript { get; set; }
+ public string? PrefixScript { get; set; }
///
/// 请求后置脚本
///
- public string PostfixScript { get; set; }
+ public string? PostfixScript { get; set; }
public bool AddHeader(string key, string value)
{
@@ -87,27 +87,27 @@ public class ForwardCoreContext
///
/// 业务系统传参json
///
- public JToken OriginalInterfaceInput { get; set; }
+ public JToken OriginalInterfaceInput { get; set; } = null!;
///
/// 业务系统传参映射后的json
///
- public JToken OriginalInterfaceMappedInput { get; set; }
+ public JToken OriginalInterfaceMappedInput { get; set; } = null!;
///
/// 目标接口返回的json
///
- public JToken TargetInterfaceOutput { get; set; }
+ public JToken TargetInterfaceOutput { get; set; } = null!;
///
/// 请求服务商接口真实传参字符串
///
- public string TargetInterfaceInput { get; set; }
+ public string TargetInterfaceInput { get; set; } = null!;
///
/// 服务商日志
///
- public RequestLogDto ServiceProviderRequestLog { get; set; }
+ public RequestLogDto ServiceProviderRequestLog { get; set; } = null!;
///
/// 专用feature
diff --git a/src/InterfaceForward.Application.Contracts/ForwardCore/InterfaceRelayForwardReplayEvent.cs b/src/InterfaceForward.Application.Contracts/ForwardCore/InterfaceRelayForwardReplayEvent.cs
index 9617709..4ad88fa 100644
--- a/src/InterfaceForward.Application.Contracts/ForwardCore/InterfaceRelayForwardReplayEvent.cs
+++ b/src/InterfaceForward.Application.Contracts/ForwardCore/InterfaceRelayForwardReplayEvent.cs
@@ -25,7 +25,7 @@ public class InterfaceRelayForwardReplayEvent
///
/// 原报文
///
- public string OriginalMessage { get; set; }
+ public string? OriginalMessage { get; set; }
///
/// 附加数据
diff --git a/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs b/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs
index 6fb0ec7..a7206c7 100644
--- a/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs
+++ b/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs
@@ -1,14 +1,15 @@
-using System.Text;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
-using InterfaceForward.Application.Contracts;
using InterfaceForward.Application.Contracts.ForwardCore;
using InterfaceForward.Application.Helpers;
using InterfaceForward.Application.OSS;
using InterfaceForward.Domain.Shared;
-using InterfaceForward.Domain.Shared.Dtos;
using InterfaceForward.Domain.Shared.Enum;
+using Microsoft.AspNetCore.Components;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -19,16 +20,11 @@ namespace InterfaceForward.Application.ForwardCore;
///
public class DefaultForwardFlow : IForwardFlow
{
- public IHttpClientFactory ClientFactory { get; set; }
+ private static readonly ConcurrentDictionary DelegateDic = new();
+ [Inject] public IHttpClientFactory ClientFactory { get; set; } = null!;
public virtual Task InParamRestructure(ForwardCoreContext context)
{
- if (context.OriginalInterfaceInput is JArray)
- {
- // 虚构系统入参数组路径
- VirtualizePath(context.InParamTreeList);
- }
-
var res = JsonHelper.Restructure(context.OriginalInterfaceInput, context.InParamTreeList,
context.TargetInterface.MappedParamType);
@@ -40,18 +36,7 @@ public class DefaultForwardFlow : IForwardFlow
// 填充占位符
ReplacePlaceholder(context);
- // 使用Natasha构建动态表达式
- if (!context.PrefixScript.IsNullOrEmpty())
- {
- if (context.PrefixScript.Contains("await"))
- {
- await NDelegate.RandomDomain().AsyncFunc(context.PrefixScript)(context);
- }
- else
- {
- NDelegate.RandomDomain().Action(context.PrefixScript)(context);
- }
- }
+ await ExecuteScriptAsync(context, true);
// 将body序列化成对应的格式字符串
context.TargetInterfaceInput = SerializationBody(context);
@@ -64,17 +49,7 @@ public class DefaultForwardFlow : IForwardFlow
// 将响应的字符串反序列化成jtoken
context.TargetInterfaceOutput = DeserializationResponse(context, res);
- if (!context.PostfixScript.IsNullOrEmpty())
- {
- if (context.PostfixScript.Contains("await"))
- {
- await NDelegate.RandomDomain().AsyncFunc(context.PostfixScript)(context);
- }
- else
- {
- NDelegate.RandomDomain().Action(context.PostfixScript)(context);
- }
- }
+ await ExecuteScriptAsync(context, false);
// 校验状态码,失败发飞书
VerifyStatusCode(context);
@@ -84,12 +59,6 @@ public class DefaultForwardFlow : IForwardFlow
public virtual async Task OutParamRestructure(ForwardCoreContext context, JToken response)
{
- if (context.TargetInterfaceOutput is JArray)
- {
- // 虚构系统入参数组路径
- VirtualizePath(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();
if (tokens.Count == 0) tokens = response.SelectTokens($"{pdfLabelConfig.Path}").ToList();
if (tokens.Count == 0) return mappedRes;
- var token = tokens[0];
- UploadResultDto oss = null;
+ var token = tokens.First();
+ UploadResultDto oss = null!;
switch (Enum.Parse(pdfLabelConfig.Value))
{
case LabelType.Url:
- {
- string url = token.Value();
- using var r = await ClientFactory.CreateClient().GetAsync(url);
- await using var stream = await r.Content.ReadAsStreamAsync();
- oss = OSSHelper.Upload(stream, Guid.NewGuid().ToString("D") + ".pdf");
- break;
- }
+ {
+ string url = token.Value()!;
+ using var r = await ClientFactory.CreateClient().GetAsync(url);
+ await using var stream = await r.Content.ReadAsStreamAsync();
+ oss = OSSHelper.Upload(stream, Guid.NewGuid().ToString("D") + ".pdf");
+ break;
+ }
case LabelType.Base64:
byte[] bytes = Convert.FromBase64String(token.ToString());
if (bytes.Length > 0)
@@ -143,8 +112,8 @@ public class DefaultForwardFlow : IForwardFlow
throw new ArgumentOutOfRangeException();
}
- mappedRes["oss_key"] = oss!.Key;
- mappedRes["oss_url"] = oss!.TempUrl;
+ mappedRes["oss_key"] = oss.Key;
+ mappedRes["oss_url"] = oss.TempUrl;
}
#endregion
@@ -159,7 +128,7 @@ public class DefaultForwardFlow : IForwardFlow
///
public virtual void AfterRequest(ForwardCoreContext context)
{
- if (context.ServiceProviderRequestLog.Response == RequestLogDto.Init)
+ if (context.ServiceProviderRequestLog.Response == null)
{
ThrowBusinessException(context, "请注意维护服务商日志!");
}
@@ -205,9 +174,8 @@ public class DefaultForwardFlow : IForwardFlow
catch (Exception)
{
ThrowBusinessException(context,
- $"期望返回类型:{context.TargetInterface.ResponseContentType},解析过程中发生异常",
- RequestServiceProviderFailedCommon.DeSerializeFailedCode);
- return default;
+ $"期望返回类型:{context.TargetInterface.ResponseContentType},解析过程中发生异常");
+ return default!;
}
}
@@ -241,7 +209,7 @@ public class DefaultForwardFlow : IForwardFlow
}
// 失败
- string errorMsg = null;
+ string? errorMsg = null;
var errorConfig = context.ReturnConfigList
.FirstOrDefault(x => x.ConfigType == InterfaceReturnConfigType.ErrorMsg);
if (errorConfig != null)
@@ -250,8 +218,7 @@ public class DefaultForwardFlow : IForwardFlow
}
// 业务失败发飞书
- ThrowBusinessException(context, errorMsg ?? "未通过状态码验证。",
- RequestServiceProviderFailedCommon.VerifyStatusFailedCode);
+ ThrowBusinessException(context, errorMsg ?? "未通过状态码验证。");
}
///
@@ -259,10 +226,8 @@ public class DefaultForwardFlow : IForwardFlow
///
/// 上下文
/// 异常消息
- /// 异常状态码
///
- protected virtual void ThrowBusinessException(ForwardCoreContext context, string msg,
- string code = "400")
+ protected virtual void ThrowBusinessException(ForwardCoreContext context, string msg)
{
if (context.ServiceProviderRequestLog.Cost == default)
{
@@ -334,8 +299,7 @@ public class DefaultForwardFlow : IForwardFlow
msg.Headers.Remove(item.FieldName);
if (!msg.Headers.TryAddWithoutValidation(item.FieldName, item.FieldValue))
{
- ThrowBusinessException(context, $"请求头无法插入 {item.FieldName}:{item.FieldValue}",
- RequestServiceProviderFailedCommon.HeaderInsertFailed);
+ ThrowBusinessException(context, $"请求头无法插入 {item.FieldName}:{item.FieldValue}");
}
}
@@ -348,16 +312,14 @@ public class DefaultForwardFlow : IForwardFlow
catch (TaskCanceledException ex)
{
// 捕获超时异常
- ThrowBusinessException(context, "请求超时," + ex.Message,
- RequestServiceProviderFailedCommon.RequestFailedCode);
+ ThrowBusinessException(context, "请求超时," + ex.Message);
}
catch (HttpRequestException rex)
{
- ThrowBusinessException(context, "请求失败," + rex.Message,
- RequestServiceProviderFailedCommon.RequestFailedCode);
+ ThrowBusinessException(context, "请求失败," + rex.Message);
}
- return default;
+ return default!;
}
protected virtual async Task RequestCoreAsync(ForwardCoreContext context, HttpRequestMessage msg)
@@ -368,21 +330,18 @@ public class DefaultForwardFlow : IForwardFlow
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)
{
- while (!await CanVisitInterfaceAsync(context.TargetInterface.Code, qps))
+ while (!await CanVisitInterfaceAsync(context))
{
await Task.Delay(context.Feature.SpinPeriod);
}
}
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
{
- nameValueCollection = JsonConvert.DeserializeObject>(body);
+ nameValueCollection = JsonConvert.DeserializeObject>(body) ??
+ new Dictionary();
}
msg.Content = new FormUrlEncodedContent(nameValueCollection);
@@ -485,7 +445,7 @@ public class DefaultForwardFlow : IForwardFlow
case ContentType.Xml: // json转xml
var jsonStr = JsonConvert.SerializeObject(body);
var xmlDoc = JsonConvert.DeserializeXmlNode(jsonStr);
- data = xmlDoc?.OuterXml;
+ data = xmlDoc?.OuterXml ?? string.Empty;
break;
case ContentType.FormUrlEncoded:
data = JsonConvert.SerializeObject(body); //到请求时解析成Dictionary
@@ -535,7 +495,7 @@ public class DefaultForwardFlow : IForwardFlow
}
}
- item.FieldValue = context.GetAccountFieldValueOrNull(fieldName);
+ item.FieldValue = context.GetAccountFieldValueOrNull(fieldName) ?? string.Empty;
break;
case GlobalConst.Url:
item.FieldValue = context.TargetInterface.RequestAddress;
@@ -553,7 +513,7 @@ public class DefaultForwardFlow : IForwardFlow
: (timeStamp / 1000).ToString(), // s
GlobalConst.DateTime => TurnDate(now, item.Description),
GlobalConst.Url => context.TargetInterface.RequestAddress,
- GlobalConst.FromAccount => context.GetAccountFieldValueOrNull(item.Name),
+ GlobalConst.FromAccount => context.GetAccountFieldValueOrNull(item.Name) ?? string.Empty,
_ => 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(dateStyle);
- }
- catch (Exception)
- {
- return dateTime.ToString(GlobalConst.DefaultDateStyle);
- }
+ 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 list)
+ async Task CanVisitInterfaceAsync(ForwardCoreContext context)
{
- if (list == null || list.Count == 0) return;
- foreach (var item in list)
- {
- if (!string.IsNullOrEmpty(item.MapAlias))
- {
- item.MapAlias = "[*]." + item.MapAlias;
- }
+ string interfaceCode = context.TargetInterface.Code;
+ int qps = context.Feature.TargetInterfaceQps;
+ var hash = context.Feature.AccountHashCode;
- VirtualizePath(item.Children);
- }
- }
-
- async Task CanVisitInterfaceAsync(string interfaceCode, int qps)
- {
// REDIS限流
- string redisKey = $"{GlobalConst.ServiceProviderInterfaceQPSKeyPrefix}:{interfaceCode}";
+ string redisKey = $"{GlobalConst.ServiceProviderInterfaceQpsKeyPrefix}:{interfaceCode}:{hash}";
var num = await RedisHelper.Client.EvalAsync("""
local redisKey=ARGV[1];
local qps=ARGV[2];
@@ -685,4 +635,25 @@ public class DefaultForwardFlow : IForwardFlow
""", null, redisKey, qps);
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(arg), script);
+ await (Task)@delegate.DynamicInvoke(context)!;
+ }
+ else
+ {
+ var @delegate = DelegateDic.GetOrAdd(scriptHash,
+ static (_, arg) => NDelegate.RandomDomain().Action(arg), script);
+ @delegate.DynamicInvoke(context);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Helpers/CommonExtensions.cs b/src/InterfaceForward.Application/Helpers/CommonExtensions.cs
index 58f7a90..802ceea 100644
--- a/src/InterfaceForward.Application/Helpers/CommonExtensions.cs
+++ b/src/InterfaceForward.Application/Helpers/CommonExtensions.cs
@@ -4,8 +4,6 @@ namespace InterfaceForward.Application.Helpers;
public static class CommonExtensions
{
-
-
public static async Task GetAllBytesAsync(this IFormFile file)
{
using (var stream = file.OpenReadStream())
diff --git a/src/InterfaceForward.Application/Helpers/StringHelper.cs b/src/InterfaceForward.Application/Helpers/StringHelper.cs
new file mode 100644
index 0000000..b99243d
--- /dev/null
+++ b/src/InterfaceForward.Application/Helpers/StringHelper.cs
@@ -0,0 +1,51 @@
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace InterfaceForward.Application.Helpers;
+
+public class StringHelper
+{
+ ///
+ /// 只能输入英文字符和键盘上常用的符号
+ ///
+ ///
+ ///
+ 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);
+ }
+
+
+ /// 获取字符串的hash值
+ ///
+ ///
+ 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();
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/HostServices/AppSubscribeEvent.cs b/src/InterfaceForward.Application/HostServices/AppSubscribeEvent.cs
new file mode 100644
index 0000000..827e052
--- /dev/null
+++ b/src/InterfaceForward.Application/HostServices/AppSubscribeEvent.cs
@@ -0,0 +1,41 @@
+using InterfaceForward.Domain.Shared.Enum;
+
+namespace InterfaceForward.Application.HostServices;
+
+public class AppSubscribeEvent
+{
+ ///
+ /// 操作类型
+ ///
+ public OptionType OptionType { get; set; }
+
+ ///
+ /// 应用key
+ ///
+ public string AppKey { get; set; }
+
+ ///
+ /// 系统接口code
+ ///
+ public string SystemInterfaceCode { get; set; }
+
+ ///
+ /// 服务商code
+ ///
+ public string ServiceProviderCode { get; set; }
+
+ ///
+ /// 【慎重!】mq预取数量(服务并发度,给0就是默认4)
+ ///
+ public ushort FetchCount { get; set; }
+
+ ///
+ /// 【慎重!】失败重回队列(一般false,即接口通不做重试,否则业务错误可能死循环)
+ ///
+ public bool FailedRequeue { get; set; }
+
+ ///
+ /// 【慎重!】删除队列(删除专用,一般false,即接口通只解除绑定,不删除队列)
+ ///
+ public bool IsDeleteQueue { get; set; }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/HostServices/AppSubscribeEventHandler.cs b/src/InterfaceForward.Application/HostServices/AppSubscribeEventHandler.cs
new file mode 100644
index 0000000..f3934cb
--- /dev/null
+++ b/src/InterfaceForward.Application/HostServices/AppSubscribeEventHandler.cs
@@ -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查询所有订阅";
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/HostServices/AppSubscribeHostService.cs b/src/InterfaceForward.Application/HostServices/AppSubscribeHostService.cs
new file mode 100644
index 0000000..6f71358
--- /dev/null
+++ b/src/InterfaceForward.Application/HostServices/AppSubscribeHostService.cs
@@ -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 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(str);
+ AsyncHelper.RunSync(() => _appSubscribeEventHandler.Handle(@event));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/InterfaceForward.Application.csproj b/src/InterfaceForward.Application/InterfaceForward.Application.csproj
index 48745e6..e8ef809 100644
--- a/src/InterfaceForward.Application/InterfaceForward.Application.csproj
+++ b/src/InterfaceForward.Application/InterfaceForward.Application.csproj
@@ -18,6 +18,7 @@
+
diff --git a/src/InterfaceForward.Application/InterfaceForwardApplicationModule.cs b/src/InterfaceForward.Application/InterfaceForwardApplicationModule.cs
index 7c38515..d8595b7 100644
--- a/src/InterfaceForward.Application/InterfaceForwardApplicationModule.cs
+++ b/src/InterfaceForward.Application/InterfaceForwardApplicationModule.cs
@@ -1,5 +1,6 @@
using System.Security.Authentication;
using Fake.AspNetCore;
+using Fake.EventBus.RabbitMQ;
using Fake.Modularity;
using Fake.ObjectMapping.AutoMapper;
using InterfaceForward.Application.Contracts;
@@ -10,6 +11,7 @@ namespace InterfaceForward.Application;
[DependsOn(typeof(FakeAspNetCoreModule))]
[DependsOn(typeof(FakeObjectMappingAutoMapperModule))]
+[DependsOn(typeof(FakeEventBusRabbitMqModule))]
[DependsOn(typeof(InterfaceForwardRepositoriesModule))]
public class InterfaceForwardApplicationModule:FakeModule
{
diff --git a/src/InterfaceForward.Application/Rabbit/BatchForwardEvent.cs b/src/InterfaceForward.Application/Rabbit/BatchForwardEvent.cs
new file mode 100644
index 0000000..3516c0a
--- /dev/null
+++ b/src/InterfaceForward.Application/Rabbit/BatchForwardEvent.cs
@@ -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; }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Rabbit/BatchForwardEventHandler.cs b/src/InterfaceForward.Application/Rabbit/BatchForwardEventHandler.cs
new file mode 100644
index 0000000..fed2d28
--- /dev/null
+++ b/src/InterfaceForward.Application/Rabbit/BatchForwardEventHandler.cs
@@ -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 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(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 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Rabbit/RabbitClient.cs b/src/InterfaceForward.Application/Rabbit/RabbitClient.cs
new file mode 100644
index 0000000..6913ab7
--- /dev/null
+++ b/src/InterfaceForward.Application/Rabbit/RabbitClient.cs
@@ -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
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs b/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs
index 607395a..43de9bf 100644
--- a/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs
+++ b/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs
@@ -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 InterfaceForward.Application.Contracts;
using InterfaceForward.Application.Contracts.ForwardCore;
@@ -16,93 +19,85 @@ using SJZY.InterfaceRelay.Repository.Log.ValueObjects;
namespace InterfaceForward.Application.Services;
-public class InterfaceForwardCommon : ITransientDependency
+public class InterfaceForwardCommon(
+ InterfaceForwardQuery interfaceForwardQuery,
+ ILogRepository logRepository,
+ IServiceProvider serviceProvider,
+ IOptionsSnapshot options)
+ : ITransientDependency
{
///
- /// 上下文缓存数据:映射,账户
+ /// 缓存数据:映射,账户 默认过期时间
///
- public static readonly int ContextCacheTtl = int.MaxValue;
+ public static readonly int DefaultCacheTtl = int.MaxValue;
private readonly Dictionary _flows = new();
- private readonly InterfaceForwardQuery _interfaceForwardQuery;
- 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 options)
- {
- _interfaceForwardQuery = interfaceForwardQuery;
- _logRepository = logRepository;
- // _segContext = segContext;
- _serviceProvider = serviceProvider;
- _interfaceRelayOptions = options.Value;
- }
+ private readonly InterfaceRelayOptions _options = options.Value;
public async Task> VerifyBusinessAndBuildForwardContexts(string upStreamCode,
string serviceProviderCode)
{
+ // todo:字符串优化
// tips:上下文的缓存不维护,在发布时统一替代。
var contextCacheKey = GetContextCacheKey(upStreamCode, serviceProviderCode);
- // _segContext.Context.Span.AddLog(LogEvent.Message("【转发核心上下文】开始构建"));
var cacheContext = (await RedisHelper.Client.GetAsync(contextCacheKey))
.ToObject();
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: "系统接口不存在");
- _logRepository.AppRequestLog.InterfaceCode = systemInterface.Code;
- _logRepository.AppRequestLog.InterfaceName = systemInterface.Name;
+ logRepository.AppRequestLog.InterfaceCode = systemInterface.Code;
+ logRepository.AppRequestLog.InterfaceName = systemInterface.Name;
- var serviceProvider = await _interfaceForwardQuery.GetServiceProviderByCodeAsync(serviceProviderCode);
- if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");
- cacheContext = await _interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider);
- await RedisHelper.Client.SetAsync(contextCacheKey, cacheContext.ToJson(), ContextCacheTtl);
+ var serviceProviderDto = await interfaceForwardQuery.GetServiceProviderByCodeAsync(serviceProviderCode);
+ if (serviceProviderDto == null) throw new BusinessException(message: "服务商不存在");
+ cacheContext = await interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProviderDto);
+ await RedisHelper.Client.SetAsync(contextCacheKey, cacheContext.ToJson(), DefaultCacheTtl);
}
- // _segContext.Context.Span.AddLog(LogEvent.Message("【转发核心上下文】构建完成"));
// 记录系统接口日志
- _logRepository.AppRequestLog.InterfaceCode = cacheContext.OriginalInterface.Code;
- _logRepository.AppRequestLog.InterfaceName = cacheContext.OriginalInterface.Name;
+ logRepository.AppRequestLog.InterfaceCode = cacheContext.OriginalInterface.Code;
+ logRepository.AppRequestLog.InterfaceName = cacheContext.OriginalInterface.Name;
// 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(accountCacheKey))
.ToObject>();
if (accountFieldCache == null)
{
- // _segContext.Context.Span.AddLog(LogEvent.Message("【服务商账户】没能命中缓存,重新构建"));
- var serviceProvider = cacheContext.TargetServiceProvider;
- var accountId = await _interfaceForwardQuery.GetAccountIdAsync(serviceProvider.Id, _logRepository.AppId);
+ var serviceProviderDto = cacheContext.TargetServiceProvider;
+ var accountId = await interfaceForwardQuery.GetAccountIdAsync(serviceProviderDto.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);
await RedisHelper.Client.SetAsync(accountCacheKey, accountFieldCache.ToJson(), ttl);
}
- // _segContext.Context.Span.AddLog(LogEvent.Message("【服务商账户】构建完成"));
-
// 组装转发上下文
var res = new List();
foreach (var item in cacheContext.TargetInterfaces)
{
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
{
TargetInterface = item.TargetInterface,
@@ -117,42 +112,37 @@ public class InterfaceForwardCommon : ITransientDependency
var token = context.FixedFieldList.FirstOrDefault(x => x.FieldValue == GlobalConst.Token);
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.Address = context.TargetInterface.RequestAddress;
context.ServiceProviderRequestLog.InterfaceCode = context.TargetInterface.Code;
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)
throw new BusinessException(message: "找不到接口转发流程" + context.TargetInterface.FlowCode);
_flows.Add(context.TargetInterface.Code, flow);
-
res.Add(context);
}
// tips:把出参映射树放到最后一个上下文中,是为了在流程结束时执行出参映射
res.Last().OutParamTreeList = cacheContext.OutParamTreeList;
- // _segContext.Context.Span.AddLog(LogEvent.Message("【转发上下文】全部组装完成"));
-
return res;
}
// 优先用服务商接口指定流程,其次使用服务商的,再其次是默认的
- public IForwardFlow GetForwardFlow(string interfaceFlowCode, string serviceProviderFlowCode)
+ public IForwardFlow GetForwardFlow(string flowCode)
{
- var flowCode = interfaceFlowCode.IsNullOrWhiteSpace()
- ? serviceProviderFlowCode.IsNullOrWhiteSpace()
- ? nameof(DefaultForwardFlow)
- : serviceProviderFlowCode
- : interfaceFlowCode;
- return _serviceProvider.GetServices()
- .FirstOrDefault(x => x.GetType().Name == flowCode);
+ return serviceProvider.GetServices()
+ .FirstOrDefault(x => x.GetType().Name == flowCode)?? throw new BusinessException("");
}
///
@@ -170,7 +160,7 @@ public class InterfaceForwardCommon : ITransientDependency
if (interfaceDto == default)
{
- return ContextCacheTtl;
+ return DefaultCacheTtl;
}
var configs = cacheContext.ServiceProviderAuthConfigs;
@@ -179,10 +169,16 @@ public class InterfaceForwardCommon : ITransientDependency
var body = new JObject();
foreach (var item in configs.Where(x => x.Flag == AuthParameterFlag.InParameter))
{
+ Debug.Assert(item.ParameterAlias != null, "item.ParameterAlias != null");
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
{
TargetInterface = interfaceDto.TargetInterface,
@@ -191,12 +187,12 @@ public class InterfaceForwardCommon : ITransientDependency
ReturnConfigList = interfaceDto.ReturnConfigList,
InParamTreeList = null,
OutParamTreeList = null,
- PrefixScript = null,
- PostfixScript = null,
+ PrefixScript = interfaceDto.PrefixScript,
+ PostfixScript = interfaceDto.PostfixScript,
OriginalInterfaceInput = body,
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,
Address = authContext.TargetInterface.RequestAddress,
@@ -204,22 +200,16 @@ public class InterfaceForwardCommon : ITransientDependency
InterfaceName = authContext.TargetInterface.Name
};
- // 走动态脚本
- var scriptConfig = configs.FirstOrDefault(x => x.Flag == AuthParameterFlag.Script);
- if (scriptConfig != default && !scriptConfig.Value1.IsNullOrEmpty())
- {
- // 使用Natasha构建动态表达式
- return NDelegate.RandomDomain().Func(scriptConfig.Value1)(authContext);
- }
-
// 授权也走主流程
if (!cacheContext.TargetServiceProvider.AuthInterfaceCode.IsNullOrWhiteSpace())
{
- var flow = GetForwardFlow(interfaceDto.TargetInterface.FlowCode,
- cacheContext.TargetServiceProvider.FlowCode);
+ Debug.Assert(cacheContext.ServiceProviderAuthInterface != null, "cacheContext.ServiceProviderAuthInterface != null");
+ var flow = GetForwardFlow(cacheContext.ServiceProviderAuthInterface.TargetInterface.FlowCode!);
if (flow == default)
- throw new BusinessException(message: "找不到接口转发流程" + interfaceDto.TargetInterface.FlowCode);
- _flows.Add(interfaceDto.TargetInterface.Code, flow);
+ throw new BusinessException(message: "找不到接口转发流程" +
+ 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);
if (tokenConfig != default)
{
+ Debug.Assert(tokenConfig.ParameterAlias != null, "tokenConfig.ParameterAlias != null");
accountFieldCache.Add(new ServiceProviderAccountFieldWithValueDto
{
FieldName = GlobalConst.Token,
FieldValue = tokenConfig.Value1.IsNullOrWhiteSpace()
- ? response[tokenConfig.ParameterAlias]?.ToString()
- : tokenConfig.Value1.Trim() + " " + response[tokenConfig.ParameterAlias]
+ ? response.SelectToken(tokenConfig.ParameterAlias)?.ToString() ??
+ throw new BusinessException($"授权接口返回报文中未找到{tokenConfig.ParameterAlias}")
+ : tokenConfig.Value1!.Trim() + " " + response.SelectToken(tokenConfig.ParameterAlias)
});
}
@@ -247,8 +239,10 @@ public class InterfaceForwardCommon : ITransientDependency
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(effectTimeConfig.Value1) switch
{
EffectTimeUnit.Second => expiredValue,
@@ -357,20 +351,23 @@ public class InterfaceForwardCommon : ITransientDependency
{
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];
- // _segContext.Context.Span.AddLog(LogEvent.Message("开始入参重构"));
+
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);
- // _segContext.Context.Span.AddLog(LogEvent.Message("* 请求结束"));
if (isMulti) return response; // 一对多不在这映射
var res = await flow.OutParamRestructure(context, response);
-
- // _segContext.Context.Span.AddLog(LogEvent.Message("完成反参重构"));
+ if (context.Feature.IsWait)
+ {
+ context.ServiceProviderRequestLog.Response2 = res.ToJson();
+ }
return res;
}
@@ -384,10 +381,15 @@ public class InterfaceForwardCommon : ITransientDependency
}
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;
}
+
+ /// 获取字符串的hash值
+ ///
+ ///
+ 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();
+ }
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Services/InterfaceForwardService.cs b/src/InterfaceForward.Application/Services/InterfaceForwardService.cs
index 8bd6612..7635852 100644
--- a/src/InterfaceForward.Application/Services/InterfaceForwardService.cs
+++ b/src/InterfaceForward.Application/Services/InterfaceForwardService.cs
@@ -5,8 +5,11 @@ using Newtonsoft.Json.Linq;
using InterfaceForward.Application.Contracts;
using InterfaceForward.Application.Contracts.ForwardCore;
using InterfaceForward.Application.Filters;
+using InterfaceForward.Application.Rabbit;
+using InterfaceForward.Domain.Shared;
using InterfaceForward.Repositories.Log.Services;
using Microsoft.AspNetCore.Mvc;
+using RabbitMQ.Client;
namespace InterfaceForward.Application.Services;
@@ -21,29 +24,100 @@ public class InterfaceForwardService : BaseService, IInterfaceForwardService
{
private readonly InterfaceForwardCommon _forwardCommon;
private readonly ILogRepository _logRepository;
- private readonly InterfaceRelayOptions _options;
+ private readonly RabbitClient _rabbitClient;
///
- public InterfaceForwardService(
- ILogRepository logRepository
- , InterfaceForwardCommon forwardCommon
- , IOptionsSnapshot options
- )
+ public InterfaceForwardService(ILogRepository logRepository,
+ InterfaceForwardCommon forwardCommon, RabbitClient rabbitClient)
{
_logRepository = logRepository;
_forwardCommon = forwardCommon;
- _options = options.Value;
+ _rabbitClient = rabbitClient;
}
+ ///
+ [HttpPost("BatchForwardThenPushToQueue")]
+ public async Task BatchForwardThenPushToQueueAsync(
+ [FromQuery] [Required] string upStreamCode,
+ [FromQuery] [Required] string serviceProviderCode,
+ [FromBody] [Required] List
[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, // 设置格式化输出
Converters = { new StringEnumConverter() },
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;
- }
-
///
/// 获取接口映射 发布记录列表
///
@@ -53,7 +44,7 @@ public class InterfaceMapPublishService : BaseService
public async Task> GetPublishedListAsync([Required] int interfaceId,
[Required] int serviceProviderId, int pageIndex = 1, int pageSize = 50)
{
- return await _interfaceMapPublishRepository.GetPublishedListAsync(interfaceId, serviceProviderId, pageIndex,
+ return await interfaceMapPublishRepository.GetPublishedListAsync(interfaceId, serviceProviderId, pageIndex,
pageSize);
}
@@ -68,18 +59,18 @@ public class InterfaceMapPublishService : BaseService
public async Task GetPublishCompareAsync([Required] string interfaceCode,
[Required] int serviceProviderId, string version)
{
- var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(interfaceCode);
+ var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(interfaceCode);
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: "服务商不存在");
// 没有指定比对的版本,就是比对数据库最新数据
- string dbContent = version.IsNullOrWhiteSpace()
+ var dbContent = version.IsNullOrWhiteSpace()
? JsonConvert.SerializeObject(
- await _interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider),
+ await interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider),
Settings)
- : await _interfaceMapPublishRepository.GetFirstAsync(a =>
+ : await interfaceMapPublishRepository.GetFirstAsync(a =>
a.InterfaceId == systemInterface.Id && a.ServiceProviderId == serviceProviderId &&
a.PublishedVersion == version, a => a.PublishedContent);
@@ -88,7 +79,7 @@ public class InterfaceMapPublishService : BaseService
string redisContent = JsonConvert.SerializeObject(cache, Settings);
- var differenceContent = CompareTexts(redisContent, dbContent ?? "");
+ var differenceContent = CompareTexts(redisContent, dbContent);
return new InterfaceMapPublishedDifferenceVO
{
@@ -107,21 +98,21 @@ public class InterfaceMapPublishService : BaseService
[HttpPost("InterfaceMap/Publish")]
public async Task PublishAsync(InterfaceMapPublishInput input)
{
- var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
+ var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
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: "服务商不存在");
// 确认比对期间数据是否被篡改
var differ = new 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 diffResult = inlineBuilder.BuildDiffModel(content, input.DBContent);
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);
var publishEntity = new InterfaceMapPublishedEntity
{
@@ -137,15 +128,15 @@ public class InterfaceMapPublishService : BaseService
using var ts = TransacationHelper.GetReadCommitted();
- await _interfaceMapPublishRepository.InsertAsync(publishEntity);
+ await interfaceMapPublishRepository.InsertAsync(publishEntity);
// 更新版本号
mapsEntities.ForEach(x => x.PublishedVersion = publishEntity.PublishedVersion);
- await _interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
+ await interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
// update cache
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();
return true;
@@ -160,19 +151,19 @@ public class InterfaceMapPublishService : BaseService
[HttpPost("InterfaceMap/PublishRollback")]
public async Task PublishRollbackAsync(InterfaceMapRollbackInput input)
{
- var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
+ var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
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: "服务商不存在");
- var mapsEntities = await _interfaceMapRepository.GetListAsync(x =>
+ var mapsEntities = await interfaceMapRepository.GetListAsync(x =>
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == input.ServiceProviderId);
if (mapsEntities == null || mapsEntities.Count == 0)
{
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.PublishedVersion == input.Version, a => a.PublishedContent);
// tips:空是合法的,虽然在业务上不存在
@@ -198,14 +189,14 @@ public class InterfaceMapPublishService : BaseService
using var ts = TransacationHelper.GetReadCommitted();
- await _interfaceMapPublishRepository.InsertAsync(publishEntity);
+ await interfaceMapPublishRepository.InsertAsync(publishEntity);
// 更新版本号
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,
- InterfaceForwardCommon.ContextCacheTtl);
+ InterfaceForwardCommon.DefaultCacheTtl);
ts.Complete();
diff --git a/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapService.Details.cs b/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapService.Details.cs
new file mode 100644
index 0000000..3bd560e
--- /dev/null
+++ b/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapService.Details.cs
@@ -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
+{
+ ///
+ /// 获取映射详情(编辑页)
+ ///
+ /// 系统接口id
+ /// 服务商id
+ ///
+ [HttpGet("InterfaceMap/EditMapDetails")]
+ public async Task 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;
+ }
+
+ ///
+ /// 获取映射参数级联列表
+ ///
+ /// 系统接口id
+ /// 服务商id
+ ///
+ [HttpGet("InterfaceMap/GetMapParameterCascadedList")]
+ public async Task>
+ 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(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();
+
+ // 一个系统接口入参
+ 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>(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>(@params)
+ });
+ }
+
+ return res;
+ }
+
+ ///
+ /// 保存参数映射
+ ///
+ ///
+ ///
+ [HttpPost("InterfaceMap/SaveParameterMaps")]
+ public async Task 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();
+ var entitiesForInsert = new List();
+ var fixedValues = new List();
+ 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;
+ }
+
+ ///
+ /// 校验必填项,并填充实体
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static void VerifyAndFillEntities(SaveParameterMapsInput input,
+ List entitiesForInsert, List entitiesForUpdate,
+ List 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
+ }));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapService.cs b/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapService.cs
index 45e5553..c9a6c49 100644
--- a/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapService.cs
+++ b/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapService.cs
@@ -2,7 +2,6 @@
using System.Diagnostics;
using InterfaceForward.Application.Contracts.Dtos.Interface;
using InterfaceForward.Application.Helpers;
-using InterfaceForward.Domain.Shared.Enum;
using InterfaceForward.Repositories;
using InterfaceForward.Repositories.Interface.Entitys;
using InterfaceForward.Repositories.Interface.Services;
@@ -16,13 +15,14 @@ namespace InterfaceForward.Application.Services.InterfaceMap;
/// 接口映射服务
///
[ApiExplorerSettings(GroupName = "接口映射服务")]
-public class InterfaceMapService : BaseService
+public partial class InterfaceMapService : ApplicationService
{
+ private readonly IBasicRepository _fixedParameterRepository;
+ private readonly IBasicRepository _interfaceMapDetailRepository;
private readonly IInterfaceMapRepository _interfaceMapRepository;
private readonly IBasicRepository _interfaceRepository;
private readonly IBasicRepository _parameterQueries;
- private readonly IBasicRepository _interfaceMapDetailRepository;
- private readonly IBasicRepository _fixedParameterRepository;
+ private readonly IBasicRepository _serviceProviderQueries;
///
public InterfaceMapService(
@@ -30,17 +30,17 @@ public class InterfaceMapService : BaseService
, IBasicRepository interfaceRepository
, IBasicRepository parameterQueries
, IBasicRepository interfaceMapDetailRepository
- , IBasicRepository fixedParameterRepository)
+ , IBasicRepository fixedParameterRepository
+ , IBasicRepository serviceProviderQueries)
{
_interfaceMapRepository = interfaceMapRepository;
_interfaceRepository = interfaceRepository;
_parameterQueries = parameterQueries;
_interfaceMapDetailRepository = interfaceMapDetailRepository;
_fixedParameterRepository = fixedParameterRepository;
+ _serviceProviderQueries = serviceProviderQueries;
}
- #region 接口映射
-
///
/// 创建接口映射
///
@@ -84,7 +84,7 @@ public class InterfaceMapService : BaseService
PrefixScript = null,
PostfixScript = null
}).ToList();
-
+
var maps2 = input.DownStreamIds.Select(id => maps.Single(x => x.DownStreamId == id)).ToList();
return await _interfaceMapRepository.InsertRangeAsync(maps2);
@@ -93,12 +93,12 @@ public class InterfaceMapService : BaseService
///
/// 获取接口映射列表
///
- /// 系统接口id
+ ///
///
- [HttpGet("InterfaceMap/GetMapListById")]
- public async Task> GetMapListByIdAsync([Required] int interfaceId)
+ [HttpGet("InterfaceMap/GetMapList")]
+ public async Task> GetMapListByIdAsync(GetMapListInputVO inputVo)
{
- return await _interfaceMapRepository.GetMapListByIdAsync(interfaceId);
+ return await _interfaceMapRepository.GetMapListAsync(inputVo);
}
///
@@ -111,6 +111,19 @@ public class InterfaceMapService : BaseService
[HttpPost("InterfaceMap/Delete")]
public async Task 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 =>
x.UpStreamId == interfaceId && x.ServiceProviderId == serviceProviderId, x => x.Id);
if (mapIds == null || mapIds.Count == 0) throw new BusinessException(message: "映射不存在");
@@ -124,240 +137,9 @@ public class InterfaceMapService : BaseService
ts.Complete();
+ var contextCacheKey = InterfaceForwardCommon.GetContextCacheKey(systemInterfaceCode, serviceProviderCode);
+ await RedisHelper.Client.DelAsync(contextCacheKey);
+
return true;
}
-
- #endregion
-
- #region 映射详情
-
- ///
- /// 获取映射详情(编辑页)
- ///
- /// 系统接口id
- /// 服务商id
- ///
- [HttpGet("InterfaceMap/EditMapDetails")]
- public async Task 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;
- }
-
- ///
- /// 获取映射参数级联列表
- ///
- /// 系统接口id
- /// 服务商id
- ///
- [HttpGet("InterfaceMap/GetMapParameterCascadedList")]
- public async Task>
- 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(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();
-
- // 一个系统接口入参
- 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>(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>(@params)
- });
- }
-
- return res;
- }
-
- ///
- /// 保存参数映射
- ///
- ///
- ///
- [HttpPost("InterfaceMap/SaveParameterMaps")]
- public async Task 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();
- var entitiesForInsert = new List();
- var fixedValues = new List();
- 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;
- }
-
- ///
- /// 校验必填项,并填充实体
- ///
- ///
- ///
- ///
- ///
- ///
- private static void VerifyAndFillEntities(SaveParameterMapsInput input,
- List entitiesForInsert, List entitiesForUpdate,
- List 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
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/ContextFeature.cs b/src/InterfaceForward.Domain.Shared/Dtos/ContextFeature.cs
index e4a0df8..e81c0d8 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/ContextFeature.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/ContextFeature.cs
@@ -3,9 +3,9 @@
public class ContextFeature
{
///
- /// 服务商接口默认QPS
+ /// 目标接口QPS
///
- public int ServiceProviderDefaultQps { get; set; }
+ public int TargetInterfaceQps { get; set; }
///
/// 是否等待
@@ -16,4 +16,9 @@ public class ContextFeature
/// 自旋周期ms
///
public int SpinPeriod { get; set; }
+
+ ///
+ /// 账户hash
+ ///
+ public string AccountHashCode { get; set; } = null!;
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/FixedFieldWithValueDto.cs b/src/InterfaceForward.Domain.Shared/Dtos/FixedFieldWithValueDto.cs
index 5399080..abddb9d 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/FixedFieldWithValueDto.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/FixedFieldWithValueDto.cs
@@ -8,16 +8,16 @@ public class FixedFieldWithValueDto
/// 字段所属
///
public FieldOwner FieldOwner { get; set; }
-
+
///
/// 字段名称
///
- public string FieldName { get; set; }
+ public string FieldName { get; set; } = null!;
///
/// 字段值
///
- public string FieldValue { get; set; }
+ public string FieldValue { get; set; } = null!;
///
/// 字段位置
@@ -27,7 +27,7 @@ public class FixedFieldWithValueDto
///
/// 字段描述
///
- public string Description { get; set; }
+ public string? Description { get; set; }
}
public enum FieldOwner
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs b/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs
index 0d0897a..13bb303 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs
@@ -26,7 +26,7 @@ public class ForwardCoreContextCache
public List OutParamTreeList { get; set; }
///
- /// 目标服务商授权接口
+ /// 目标服务商授权接口(serviceProvider.AuthInterfaceCode存在时,一定不为null)
///
public TargetInterfaceSummary? ServiceProviderAuthInterface { get; set; }
@@ -46,13 +46,13 @@ public class ServiceProviderAuthConfigDto
///
/// 参数路径
///
- 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
@@ -60,35 +60,35 @@ public class TargetInterfaceSummary
///
/// 目标服务商接口
///
- public InterfaceDto TargetInterface { get; set; }
+ public InterfaceDto TargetInterface { get; set; } = null!;
///
/// 请求前置脚本
///
- public string PrefixScript { get; set; }
+ public string? PrefixScript { get; set; }
///
/// 请求后置脚本
///
- public string PostfixScript { get; set; }
-
+ public string? PostfixScript { get; set; }
+
///
/// 入参映射列表(树形结构)
///
- public List InParamTreeList { get; set; }
+ public List InParamTreeList { get; set; } = [];
///
/// 固定值列表
///
- public List FixedFieldList { get; set; }
+ public List FixedFieldList { get; set; } = [];
///
/// 返回配置列表
///
- public List ReturnConfigList { get; set; }
+ public List ReturnConfigList { get; set; } = [];
///
/// 表单字段列表
///
- public List FormFieldList { get; set; }
+ public List FormFieldList { get; set; } = [];
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceDto.cs b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceDto.cs
index a45ab5b..53ca5e7 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceDto.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceDto.cs
@@ -12,7 +12,7 @@ public class InterfaceDto
///
/// 接口名
///
- public string Name { get; set; }
+ public string Name { get; set; } = null!;
///
/// 服务商id
@@ -22,12 +22,12 @@ public class InterfaceDto
///
/// 接口代码
///
- public string Code { get; set; }
+ public string Code { get; set; } = null!;
///
/// 请求地址
///
- public string RequestAddress { get; set; }
+ public string RequestAddress { get; set; } = null!;
///
/// 请求协议
@@ -37,7 +37,7 @@ public class InterfaceDto
///
/// 请求方式
///
- public string RequestMethod { get; set; }
+ public string RequestMethod { get; set; } = null!;
///
/// 内容类型
@@ -67,7 +67,7 @@ public class InterfaceDto
///
/// 流程代码
///
- public string FlowCode { get; set; }
+ public string? FlowCode { get; set; }
///
/// 是否禁用授权
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceFormFieldDto.cs b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceFormFieldDto.cs
index 15fe01b..897fd79 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceFormFieldDto.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceFormFieldDto.cs
@@ -5,15 +5,15 @@ public class InterfaceFormFieldDto
///
/// 参数名
///
- public string Name { get; set; }
+ public string Name { get; set; } = null!;
///
/// 参数值
///
- public string Value { get; set; }
+ public string Value { get; set; } = null!;
///
/// 参数描述
///
- public string Description { get; set; }
+ public string? Description { get; set; }
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceReturnConfigDto.cs b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceReturnConfigDto.cs
index 4bbce2a..0afec98 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceReturnConfigDto.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceReturnConfigDto.cs
@@ -20,11 +20,11 @@ namespace InterfaceForward.Domain.Shared.Dtos
///
/// 路径
///
- public string Path { get; set; }
+ public string Path { get; set; } = null!;
///
/// 值
///
- public string Value { get; set; }
+ public string Value { get; set; } = null!;
}
}
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/RequestLogDto.cs b/src/InterfaceForward.Domain.Shared/Dtos/RequestLogDto.cs
index 0018d40..78916fd 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/RequestLogDto.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/RequestLogDto.cs
@@ -2,8 +2,6 @@
public class RequestLogDto(Guid requestId)
{
- public const string Init = null!;
-
///
/// 请求Id
///
@@ -22,7 +20,7 @@ public class RequestLogDto(Guid requestId)
///
/// 应用/服务商名称
///
- public string Name { get; set; }
+ public string? Name { get; set; }
///
/// 发起请求IP
@@ -32,27 +30,27 @@ public class RequestLogDto(Guid requestId)
///
/// 接口代码
///
- public string InterfaceCode { get; set; }
+ public string? InterfaceCode { get; set; }
///
/// 请求接口名称
///
- public string InterfaceName { get; set; }
+ public string? InterfaceName { get; set; }
///
/// 请求地址
///
- public string Address { get; set; }
+ public string? Address { get; set; }
///
/// 请求头
///
- public string Headers { get; set; }
+ public string? Headers { get; set; }
///
/// 请求体
///
- public string Content { get; set; }
+ public string? Content { get; set; }
///
/// 响应耗时ms
@@ -62,7 +60,12 @@ public class RequestLogDto(Guid requestId)
///
/// 响应结果
///
- public string Response { get; set; } = Init;
+ public string? Response { get; set; }
+
+ ///
+ /// 响应结果映射后
+ ///
+ public string? Response2 { get; set; }
///
/// 是否成功调用
@@ -72,5 +75,5 @@ public class RequestLogDto(Guid requestId)
///
/// 异常
///
- public string Exception { get; set; }
+ public string? Exception { get; set; }
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/RequestServiceProviderFailedCommon.cs b/src/InterfaceForward.Domain.Shared/Dtos/RequestServiceProviderFailedCommon.cs
index ce61376..19edcca 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/RequestServiceProviderFailedCommon.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/RequestServiceProviderFailedCommon.cs
@@ -11,6 +11,8 @@ public static class RequestServiceProviderFailedCommon
public static string VerifyStatusFailedCode = "4003";
public static string HeaderInsertFailed = "4004";
+
+ public static string QpsOutLimit = "4005";
public static bool IsRequestServiceProviderFailed(string code)
{
diff --git a/src/InterfaceForward.Domain.Shared/Dtos/ServiceProviderDto.cs b/src/InterfaceForward.Domain.Shared/Dtos/ServiceProviderDto.cs
index 2b3f6c2..9628334 100644
--- a/src/InterfaceForward.Domain.Shared/Dtos/ServiceProviderDto.cs
+++ b/src/InterfaceForward.Domain.Shared/Dtos/ServiceProviderDto.cs
@@ -10,12 +10,12 @@ public class ServiceProviderDto
///
/// 服务商名称
///
- public string Name { get; set; }
-
+ public string Name { get; set; } = null!;
+
///
/// 服务商代码
///
- public string Code { get; set; }
+ public string Code { get; set; } = null!;
///
/// 是否禁用授权
@@ -25,12 +25,12 @@ public class ServiceProviderDto
///
/// 流程代码
///
- public string FlowCode { get; set; }
+ public string? FlowCode { get; set; }
///
/// 授权接口Code
///
- public string AuthInterfaceCode { get; set; }
+ public string? AuthInterfaceCode { get; set; }
///
/// 请求超时(s)
diff --git a/src/InterfaceForward.Domain.Shared/GlobalConst.cs b/src/InterfaceForward.Domain.Shared/GlobalConst.cs
index 7aca7c1..fd0236c 100644
--- a/src/InterfaceForward.Domain.Shared/GlobalConst.cs
+++ b/src/InterfaceForward.Domain.Shared/GlobalConst.cs
@@ -8,6 +8,7 @@ public static class GlobalConst
public const string DateTime = "$DateTime";
public const string Url = "$Url";
public const string Token = "$token";
+ public const string AccountHashCode = "$hash";
///
/// 不执行动态代码
@@ -27,5 +28,7 @@ public static class GlobalConst
///
/// 服务商接口qps redis key前缀
///
- public const string ServiceProviderInterfaceQPSKeyPrefix = "InterfaceQPS";
+ public const string ServiceProviderInterfaceQpsKeyPrefix = "InterfaceQPS";
+
+ public const string InterfaceRelayExchange = "sjzy.interface.relay.direct";
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Repositories/Interface/Entitys/InterfaceMapEntity.cs b/src/InterfaceForward.Repositories/Interface/Entitys/InterfaceMapEntity.cs
index 5dfacb0..8fd20fe 100644
--- a/src/InterfaceForward.Repositories/Interface/Entitys/InterfaceMapEntity.cs
+++ b/src/InterfaceForward.Repositories/Interface/Entitys/InterfaceMapEntity.cs
@@ -23,7 +23,7 @@
/// 系统接口Code
///
[SugarColumn(ColumnName = "UpStreamCode")]
- public string UpStreamCode { get; set; }
+ public string UpStreamCode { get; set; } = null!;
///
/// 服务商ID
@@ -41,24 +41,30 @@
/// 服务商接口Code
///
[SugarColumn(ColumnName = "DownStreamCode")]
- public string DownStreamCode { get; set; }
+ public string DownStreamCode { get; set; } = null!;
///
/// 当前线上版本
///
[SugarColumn(ColumnName = "PublishedVersion")]
- public string PublishedVersion { get; set; }
+ public string? PublishedVersion { get; set; }
///
/// 前置脚本
///
[SugarColumn(ColumnName = "PrefixScript")]
- public string PrefixScript { get; set; }
+ public string? PrefixScript { get; set; }
///
/// 后置脚本
///
[SugarColumn(ColumnName = "PostfixScript")]
- public string PostfixScript { get; set; }
+ public string? PostfixScript { get; set; }
+
+ ///
+ /// 支持不对等映射(自由映射)
+ ///
+ [SugarColumn(ColumnName = "FreeMap")]
+ public bool FreeMap { get; set; }
}
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Repositories/Interface/Services/IInterfaceMapRepository.cs b/src/InterfaceForward.Repositories/Interface/Services/IInterfaceMapRepository.cs
index 8c24409..57d8b50 100644
--- a/src/InterfaceForward.Repositories/Interface/Services/IInterfaceMapRepository.cs
+++ b/src/InterfaceForward.Repositories/Interface/Services/IInterfaceMapRepository.cs
@@ -11,9 +11,9 @@ namespace InterfaceForward.Repositories.Interface.Services
///
/// 获取接口映射列表
///
- /// 系统接口id
+ ///
///
- Task> GetMapListByIdAsync(int upstreamId);
+ Task> GetMapListAsync(GetMapListInputVO inputVo);
///
/// 获取参数映射列表
diff --git a/src/InterfaceForward.Repositories/Interface/Services/InterfaceMapRepository.cs b/src/InterfaceForward.Repositories/Interface/Services/InterfaceMapRepository.cs
index a29bc6a..e10d580 100644
--- a/src/InterfaceForward.Repositories/Interface/Services/InterfaceMapRepository.cs
+++ b/src/InterfaceForward.Repositories/Interface/Services/InterfaceMapRepository.cs
@@ -13,9 +13,9 @@ public class InterfaceMapRepository : BasicRepository, IScop
///
/// 获取映射列表
///
- /// 上游接口id
+ ///
///
- public async Task> GetMapListByIdAsync(int upstreamId)
+ public async Task> GetMapListAsync(GetMapListInputVO inputVo)
{
// 上下游一对多关系
var query = Context.Queryable()
@@ -23,8 +23,10 @@ public class InterfaceMapRepository : BasicRepository, IScop
.LeftJoin((a, b, c) => a.DownStreamId == c.Id) // 服务商接口 N
.LeftJoin((a, b, c, d) => c.ServiceProviderId == d.Id)
.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)
+ .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 })
.OrderBy(a => a.UpdateTime, OrderByType.Desc);
@@ -40,24 +42,25 @@ public class InterfaceMapRepository : BasicRepository, IScop
InterfaceCode = SqlFunc.MappingColumn("GROUP_CONCAT(c.Code ORDER BY a.id SEPARATOR ',')"),
UpdateTime = a.UpdateTime,
PublishedVersion = a.PublishedVersion
- }).ToListAsync();
+ }).ToPagedListAsync(inputVo.PageIndex, inputVo.PageSize);
}
///
- public async Task> GetParameterMapListAsync(List mapIds, bool isInParaMap)
+ public async Task> GetParameterMapListAsync(List 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`
-from t_interface_map t
+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
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_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_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
ORDER BY `b`.`Sort` ASC
*/
-
+
var ids = mapIds.JoinAsString(",");
// 因为入参映射有多个 出参映射只有一个(挂在第一个映射)
return await Context.SqlQueryable(@$"
diff --git a/src/InterfaceForward.Repositories/Interface/ValueObjects/EditMapDetailsOutput.cs b/src/InterfaceForward.Repositories/Interface/ValueObjects/EditMapDetailsOutput.cs
index d0ef245..977b0fa 100644
--- a/src/InterfaceForward.Repositories/Interface/ValueObjects/EditMapDetailsOutput.cs
+++ b/src/InterfaceForward.Repositories/Interface/ValueObjects/EditMapDetailsOutput.cs
@@ -5,12 +5,12 @@ public class EditMapDetailsOutput
///
/// 入参映射
///
- public List InParaMapList { get; set; } = new();
+ public List InParaMapList { get; set; } = [];
///
/// 出参映射明细
///
- public List MapDetails { get; set; } = new();
+ public List MapDetails { get; set; } = [];
}
public class MapSummary
@@ -24,21 +24,26 @@ public class MapSummary
/// 接口id
///
public int InterfaceId { get; set; }
-
+
///
/// 接口Name
///
- public string InterfaceName { get; set; }
+ public string InterfaceName { get; set; } = null!;
///
/// 前置脚本
///
- public string PrefixScript { get; set; }
+ public string? PrefixScript { get; set; }
///
/// 后置脚本
///
- public string PostfixScript { get; set; }
+ public string? PostfixScript { get; set; }
+
+ ///
+ /// 自由映射(不对等映射)
+ ///
+ public bool FreeMap { get; set; }
///
/// 映射明细
diff --git a/src/InterfaceForward.Repositories/Interface/ValueObjects/GetMapListInputVO.cs b/src/InterfaceForward.Repositories/Interface/ValueObjects/GetMapListInputVO.cs
new file mode 100644
index 0000000..203a38f
--- /dev/null
+++ b/src/InterfaceForward.Repositories/Interface/ValueObjects/GetMapListInputVO.cs
@@ -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; }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Repositories/Interface/ValueObjects/InterfaceMapPublishedDifferenceVO.cs b/src/InterfaceForward.Repositories/Interface/ValueObjects/InterfaceMapPublishedDifferenceVO.cs
index e19956a..3a93876 100644
--- a/src/InterfaceForward.Repositories/Interface/ValueObjects/InterfaceMapPublishedDifferenceVO.cs
+++ b/src/InterfaceForward.Repositories/Interface/ValueObjects/InterfaceMapPublishedDifferenceVO.cs
@@ -5,15 +5,15 @@ public class InterfaceMapPublishedDifferenceVO
///
/// 线上的缓存
///
- public string PublishedContent { get; set; }
+ public string? PublishedContent { get; set; }
///
/// 数据库的内容
///
- public string DbContent { get; set; }
+ public string? DbContent { get; set; }
///
/// 与线上版本的差异内容
///
- public string DifferenceContent { get; set; }
+ public string? DifferenceContent { get; set; }
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs b/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs
index f5a6f0d..c9069c5 100644
--- a/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs
+++ b/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs
@@ -144,7 +144,7 @@ ORDER BY `b`.`Sort` ASC").ToListAsync();
///
///
///
- public async Task GetInterfaceByCodeAsync(string interfaceCode)
+ public async Task GetInterfaceByCodeAsync(string interfaceCode)
{
var res = await GetInterfaceByCodesAsync(interfaceCode);
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);
- if (authInterface != null)
+ if (authInterface == null)
{
- context.ServiceProviderAuthInterface = new TargetInterfaceSummary
- {
- 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),
- };
+ throw new BusinessException($"授权接口{serviceProvider.AuthInterfaceCode}不存在");
}
+
+ 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);