using Fake.DependencyInjection;
using Fake.Application;
using Oms.Application.Dtos.Channels;
using Oms.Application.Integrations.Itfx;
using Oms.Domain.ChannelsAggregate.Repositories;
using Oms.Domain.OrdersAggregate.Entities;
namespace Oms.Application.Shared;
public sealed class OrderDownstreamCredentials
{
public string SupplierCode { get; set; } = string.Empty;
public string AppKey { get; set; } = string.Empty;
public string AppSecret { get; set; } = string.Empty;
}
///
/// 解析订单下游 itfx 凭证(优先订单绑定字段,回退渠道分组)。
///
public class OrderDownstreamCredentialResolver(
IServiceChannelRepository serviceChannelRepository,
IServiceChannelGroupRepository serviceChannelGroupRepository,
IItfxMetadataClient itfxMetadataClient) : IScopedDependency
{
public async Task ResolveAsync(Order order, CancellationToken cancellationToken = default)
{
var fromSnapshot = await TryResolveFromChannelRulesSnapshotAsync(order, cancellationToken);
if (fromSnapshot != null)
{
return fromSnapshot;
}
if (!string.IsNullOrWhiteSpace(order.DownstreamSupplierCode)
&& !string.IsNullOrWhiteSpace(order.DownstreamAppKey)
&& order.DownstreamAppId is > 0)
{
var app = await itfxMetadataClient.GetAppAsync(order.DownstreamAppId.Value, cancellationToken);
var appSecret = app?.AppSecret?.Trim();
if (string.IsNullOrWhiteSpace(appSecret))
{
throw new BusinessException("应用 AppSecret 未配置");
}
return new OrderDownstreamCredentials
{
SupplierCode = order.DownstreamSupplierCode.Trim(),
AppKey = order.DownstreamAppKey.Trim(),
AppSecret = appSecret
};
}
if (string.IsNullOrWhiteSpace(order.ChannelCode))
{
throw new BusinessException("订单未绑定服务渠道,无法预报下游");
}
var channel = await serviceChannelRepository.GetByChannelCodeAsync(order.ChannelCode.Trim(), cancellationToken);
if (channel == null)
{
throw new BusinessException($"未找到渠道「{order.ChannelCode}」");
}
if (!channel.GroupId.HasValue || channel.GroupId.Value <= 0)
{
throw new BusinessException($"渠道「{order.ChannelCode}」未关联服务商分组");
}
var group = await serviceChannelGroupRepository.FirstOrDefaultAsync(x => x.Id == channel.GroupId.Value, cancellationToken);
if (group == null)
{
throw new BusinessException("服务商分组不存在或已删除");
}
if (group.AppId <= 0)
{
throw new BusinessException($"服务商「{group.Name}」未配置应用");
}
if (group.ApiServiceProviderId <= 0)
{
throw new BusinessException($"服务商「{group.Name}」未配置 API 服务商");
}
var appInfo = await itfxMetadataClient.GetAppAsync(group.AppId, cancellationToken);
if (appInfo == null || string.IsNullOrWhiteSpace(appInfo.AppKey) || string.IsNullOrWhiteSpace(appInfo.AppSecret))
{
throw new BusinessException("应用 AppKey / AppSecret 未配置");
}
var sp = await itfxMetadataClient.GetServiceProviderAsync(group.ApiServiceProviderId, cancellationToken);
if (sp == null || string.IsNullOrWhiteSpace(sp.Code))
{
throw new BusinessException("API 服务商编码(supplierCode)未配置");
}
return new OrderDownstreamCredentials
{
SupplierCode = sp.Code.Trim(),
AppKey = appInfo.AppKey.Trim(),
AppSecret = appInfo.AppSecret.Trim()
};
}
private async Task TryResolveFromChannelRulesSnapshotAsync(
Order order,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(order.ChannelCode))
{
return null;
}
var channel = await serviceChannelRepository.GetByChannelCodeAsync(order.ChannelCode.Trim(), cancellationToken);
if (channel == null)
{
return null;
}
// 规则快照里固化了 itfx 应用信息(含 AppSecret)。
const string rulesOperatorType = "rules";
var bindTime = order.CreateTime == default ? DateTime.Now : order.CreateTime;
var record = await serviceChannelRepository.GetEffectivePublishRecordAsync(
channel.Id,
rulesOperatorType,
bindTime,
cancellationToken);
if (record == null || string.IsNullOrWhiteSpace(record.Content))
{
return null;
}
var snapshot = OmsDocHelper.DeserializeJson(record.Content);
var downstream = snapshot?.Downstream;
if (downstream == null)
{
return null;
}
// 订单绑定的 AppId 与快照不一致时,直接回退旧逻辑。
if (order.DownstreamAppId is > 0
&& downstream.AppId is > 0
&& order.DownstreamAppId.Value != downstream.AppId.Value)
{
return null;
}
var appSecret = downstream.AppSecret?.Trim();
if (string.IsNullOrWhiteSpace(appSecret))
{
return null;
}
var appKey = !string.IsNullOrWhiteSpace(order.DownstreamAppKey)
? order.DownstreamAppKey.Trim()
: downstream.AppKey?.Trim();
if (string.IsNullOrWhiteSpace(appKey))
{
return null;
}
var supplierCode = !string.IsNullOrWhiteSpace(order.DownstreamSupplierCode)
? order.DownstreamSupplierCode.Trim()
: downstream.SupplierCode?.Trim();
if (string.IsNullOrWhiteSpace(supplierCode))
{
return null;
}
return new OrderDownstreamCredentials
{
SupplierCode = supplierCode,
AppKey = appKey,
AppSecret = appSecret
};
}
}