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 }; } }