using System.Runtime.ExceptionServices;
using Fake.DependencyInjection;
using InterfaceForward.Application.Contracts;
using InterfaceForward.Application.Contracts.ForwardCore;
using InterfaceForward.Application.ForwardCore;
using InterfaceForward.Application.Helpers;
using InterfaceForward.Domain.Shared;
using InterfaceForward.Domain.Shared.Dtos;
using InterfaceForward.Domain.Shared.Enum;
using InterfaceForward.Repositories;
using InterfaceForward.Repositories.Log.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using SJZY.InterfaceRelay.Repository.Log.ValueObjects;
namespace InterfaceForward.Application.Services;
public class InterfaceForwardCommon : ITransientDependency
{
///
/// 上下文缓存数据:映射,账户
///
public static readonly int ContextCacheTtl = 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;
}
public async Task> VerifyBusinessAndBuildForwardContexts(string upStreamCode,
string serviceProviderCode)
{
// 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);
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
_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);
}
// _segContext.Context.Span.AddLog(LogEvent.Message("【转发核心上下文】构建完成"));
// 记录系统接口日志
_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 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);
// 服务商授权
if (!serviceProvider.IsDisableAuth && accountId == default)
{
throw new BusinessException(message: $"服务商:{serviceProvider.Name} 下不存在此账户,请联系管理员或前往接口通创建");
}
accountFieldCache = await _interfaceForwardQuery.GetAccountFieldListAsync(accountId);
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;
var context = new ForwardCoreContext
{
TargetInterface = item.TargetInterface,
FixedFieldList = item.FixedFieldList,
AccountFieldList = accountFieldCache,
ReturnConfigList = item.ReturnConfigList,
FormFieldList = item.FormFieldList,
InParamTreeList = item.InParamTreeList,
PrefixScript = item.PrefixScript,
PostfixScript = item.PostfixScript
};
var token = context.FixedFieldList.FirstOrDefault(x => x.FieldValue == GlobalConst.Token);
if (token != default)
{
token.FieldValue = context.GetAccountFieldValueOrNull(GlobalConst.Token);
}
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);
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)
{
var flowCode = interfaceFlowCode.IsNullOrWhiteSpace()
? serviceProviderFlowCode.IsNullOrWhiteSpace()
? nameof(DefaultForwardFlow)
: serviceProviderFlowCode
: interfaceFlowCode;
return _serviceProvider.GetServices()
.FirstOrDefault(x => x.GetType().Name == flowCode);
}
///
/// 处理服务商授权接口,返回一个过期时间表示服务商账户缓存的有效期
/// tips:因为我们将授权接口的返回值作为服务商账户字段的一部分一起缓存,所以这里需要返回过期时间
///
///
///
///
///
public virtual async Task HandleAuthInterfaceAsync(ForwardCoreContextCache cacheContext,
List accountFieldCache)
{
var interfaceDto = cacheContext.ServiceProviderAuthInterface;
if (interfaceDto == default)
{
return ContextCacheTtl;
}
var configs = cacheContext.ServiceProviderAuthConfigs;
// 构建入参
var body = new JObject();
foreach (var item in configs.Where(x => x.Flag == AuthParameterFlag.InParameter))
{
body[item.ParameterAlias] = item.Value1;
}
// 构建授权上下文
var authContext = new ForwardCoreContext
{
TargetInterface = interfaceDto.TargetInterface,
FixedFieldList = interfaceDto.FixedFieldList, // can filter service provider fix fields
AccountFieldList = accountFieldCache, // can add from cache
ReturnConfigList = interfaceDto.ReturnConfigList,
InParamTreeList = null,
OutParamTreeList = null,
PrefixScript = null,
PostfixScript = null,
OriginalInterfaceInput = body,
FormFieldList = interfaceDto.FormFieldList // normal case is null
};
authContext.ServiceProviderRequestLog = new RequestLogDto(_logRepository.AppRequestLog.RequestId)
{
Name = cacheContext.TargetServiceProvider.Name,
Address = authContext.TargetInterface.RequestAddress,
InterfaceCode = authContext.TargetInterface.Code,
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);
if (flow == default)
throw new BusinessException(message: "找不到接口转发流程" + interfaceDto.TargetInterface.FlowCode);
_flows.Add(interfaceDto.TargetInterface.Code, flow);
}
// 请求授权接口
var response = await InternalForwardAsync(authContext);
// 添加到固定字段以便标准转发流程使用
var tokenConfig = configs.FirstOrDefault(x => x.Flag == AuthParameterFlag.Token);
if (tokenConfig != default)
{
accountFieldCache.Add(new ServiceProviderAccountFieldWithValueDto
{
FieldName = GlobalConst.Token,
FieldValue = tokenConfig.Value1.IsNullOrWhiteSpace()
? response[tokenConfig.ParameterAlias]?.ToString()
: tokenConfig.Value1.Trim() + " " + response[tokenConfig.ParameterAlias]
});
}
// 计算过期时间
var fixedEffectTimeConfig = configs.FirstOrDefault(x => x.Flag == AuthParameterFlag.FixedEffectTime);
int.TryParse(fixedEffectTimeConfig?.Value1, out var ttl);
var effectTimeConfig = configs.FirstOrDefault(x => x.Flag == AuthParameterFlag.EffectTime);
if (effectTimeConfig == default)
{
return ttl;
}
if (int.TryParse(response[effectTimeConfig.ParameterAlias]?.ToString(), out var expiredValue))
{
ttl = Enum.Parse(effectTimeConfig.Value1) switch
{
EffectTimeUnit.Second => expiredValue,
EffectTimeUnit.Minute => expiredValue * 60,
EffectTimeUnit.Hour => expiredValue * 60 * 60,
_ => throw new ArgumentOutOfRangeException()
};
}
return ttl;
}
///
/// 获取上下文redis key
///
/// 系统接口code
/// 服务商code
///
public static string GetContextCacheKey(string upStreamCode, string serviceProviderCode)
{
return $"ForwardContext:up_{upStreamCode}:sp_{serviceProviderCode}";
}
///
/// 获取账户redis key
/// ex:
/// 如果appId==null,则返回:ForwardAccount:sp_{serviceProviderCode}:app_*
/// 如果appId!=null,则返回:ForwardAccount:sp_{serviceProviderCode}:app_{appId}
///
/// 服务商code
/// 应用id
///
public static string GetAccountCacheKey(string serviceProviderCode, int? appId = null)
{
return $"ForwardAccount:sp_{serviceProviderCode}:app_{(appId == null ? "*" : appId)}";
}
public async Task