feat: openApi 分组暴露推费与下单,去掉 OMS 侧 appKey 校验
All checks were successful
Build and Deploy / deploy (push) Successful in 58s
All checks were successful
Build and Deploy / deploy (push) Successful in 58s
Swagger openApi 提供 /openapi/pickup-order/push-fee 与 /openapi/parcel/create;删除 method 路由与 whoami。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
2688058cb3
commit
a3b54b6b07
@ -41,26 +41,3 @@ public class CustomerOpenApiCredentialAdminSetEnabledInput : CustomerOpenApiCred
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
|
||||
public class CustomerOpenApiWhoAmIInput
|
||||
{
|
||||
public string? AppSecret { get; set; }
|
||||
}
|
||||
|
||||
public class CustomerOpenApiWhoAmIResult
|
||||
{
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
public string CustomerCode { get; set; } = string.Empty;
|
||||
|
||||
public string CustomerName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class CustomerOpenApiEnvelope
|
||||
{
|
||||
public string Code { get; set; } = "200";
|
||||
|
||||
public string Msg { get; set; } = "成功";
|
||||
|
||||
public CustomerOpenApiWhoAmIResult? Data { get; set; }
|
||||
}
|
||||
|
||||
17
src/Oms.Application/Dtos/Orders/OpenApiParcelCreateInput.cs
Normal file
17
src/Oms.Application/Dtos/Orders/OpenApiParcelCreateInput.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace Oms.Application.Dtos.Orders;
|
||||
|
||||
/// <summary>
|
||||
/// 开放接口创建订单包裹:在标准下单入参上增加 AppKey/AppSecret(Body,非自定义 Header)。
|
||||
/// </summary>
|
||||
public class OpenApiParcelCreateInput : OrderParcelCreateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户开放接口 AppKey。
|
||||
/// </summary>
|
||||
public string? AppKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户开放接口 AppSecret。
|
||||
/// </summary>
|
||||
public string? AppSecret { get; set; }
|
||||
}
|
||||
@ -360,15 +360,6 @@ public class PickupCarrierValueFeeOutput
|
||||
public decimal? FeeAmount { get; set; }
|
||||
}
|
||||
|
||||
public class PickupOpenApiEnvelope
|
||||
{
|
||||
public string Code { get; set; } = "200";
|
||||
|
||||
public string Msg { get; set; } = "成功";
|
||||
|
||||
public PickupOpenApiPushFeeResult? Data { get; set; }
|
||||
}
|
||||
|
||||
public class PickupOpenApiPushFeeResult
|
||||
{
|
||||
public string PickupNo { get; set; } = string.Empty;
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Customers;
|
||||
using Oms.Application.Shared;
|
||||
|
||||
namespace Oms.Application.Services.Customers;
|
||||
|
||||
/// <summary>
|
||||
/// 客户 OpenAPI 机机入口(AllowAnonymous,AppKey + AppSecret)。
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "OMS-Customer")]
|
||||
public class CustomerOpenApiService : ApplicationService
|
||||
{
|
||||
private CustomerOpenApiAuthHelper AuthHelper =>
|
||||
LazyServiceProvider.GetRequiredService<CustomerOpenApiAuthHelper>();
|
||||
|
||||
/// <summary>
|
||||
/// 探针:校验凭证并返回客户身份。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/openapi/customer/whoami")]
|
||||
public async Task<CustomerOpenApiEnvelope> WhoAmIAsync(
|
||||
[FromQuery] string? appKey,
|
||||
[FromHeader(Name = "appSecret")] string? appSecretHeader,
|
||||
[FromBody] CustomerOpenApiWhoAmIInput? body)
|
||||
{
|
||||
try
|
||||
{
|
||||
var appSecret = !string.IsNullOrWhiteSpace(appSecretHeader)
|
||||
? appSecretHeader
|
||||
: body?.AppSecret;
|
||||
var (customer, _) = await AuthHelper.ResolveAsync(appKey, appSecret);
|
||||
return new CustomerOpenApiEnvelope
|
||||
{
|
||||
Code = "200",
|
||||
Msg = "成功",
|
||||
Data = new CustomerOpenApiWhoAmIResult
|
||||
{
|
||||
CustomerId = customer.Id,
|
||||
CustomerCode = customer.CustomerCode,
|
||||
CustomerName = customer.CustomerName
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (BusinessException ex)
|
||||
{
|
||||
return Fail(ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Fail(string.IsNullOrWhiteSpace(ex.Message) ? "系统异常" : ex.Message, "500");
|
||||
}
|
||||
}
|
||||
|
||||
private static CustomerOpenApiEnvelope Fail(string msg, string code = "401") =>
|
||||
new()
|
||||
{
|
||||
Code = code,
|
||||
Msg = msg,
|
||||
Data = null
|
||||
};
|
||||
}
|
||||
52
src/Oms.Application/Services/OpenApi/OpenApiService.cs
Normal file
52
src/Oms.Application/Services/OpenApi/OpenApiService.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Orders;
|
||||
using Oms.Application.Services.Orders;
|
||||
using Oms.Application.Shared;
|
||||
|
||||
namespace Oms.Application.Services.OpenApi;
|
||||
|
||||
/// <summary>
|
||||
/// 对外开放 API(Swagger 文档分组:openApi)。
|
||||
/// 客户机机:AppKey/AppSecret 放在 JSON Body;供应商推费由上游 itfx 鉴权,OMS 不再验 key。
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "openApi")]
|
||||
public class OpenApiService : ApplicationService
|
||||
{
|
||||
private CustomerOpenApiAuthHelper AuthHelper =>
|
||||
LazyServiceProvider.GetRequiredService<CustomerOpenApiAuthHelper>();
|
||||
|
||||
private PickupOrderFeePushService FeePush =>
|
||||
LazyServiceProvider.GetRequiredService<PickupOrderFeePushService>();
|
||||
|
||||
private OrderService OrderService =>
|
||||
LazyServiceProvider.GetRequiredService<OrderService>();
|
||||
|
||||
/// <summary>
|
||||
/// 推送揽收件重尺并按当前产品计算应收应付(上游 itfx 鉴权后转发,无需 AppKey)。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/openapi/pickup-order/push-fee")]
|
||||
public Task<PickupOpenApiPushFeeResult> PushFeeAsync([FromBody] PickupOpenApiPushFeeInput input) =>
|
||||
FeePush.PushFeeAsync(input);
|
||||
|
||||
/// <summary>
|
||||
/// 创建订单包裹(客户机机:Body 传 AppKey、AppSecret)。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/openapi/parcel/create")]
|
||||
public async Task<OrderParcelCreateOutput> CreateParcelAsync([FromBody] OpenApiParcelCreateInput input)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(input);
|
||||
|
||||
var (customer, _) = await AuthHelper.ResolveAsync(input.AppKey, input.AppSecret);
|
||||
if (input.CustomerId is > 0 && input.CustomerId.Value != customer.Id)
|
||||
{
|
||||
throw new BusinessException("凭证无效");
|
||||
}
|
||||
|
||||
input.CustomerId = customer.Id;
|
||||
return await OrderService.CreateParcelAsync(input);
|
||||
}
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Orders;
|
||||
|
||||
namespace Oms.Application.Services.Orders;
|
||||
|
||||
/// <summary>
|
||||
/// 国内揽收开放接口(承运商推送件重尺)。
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "OMS-Order")]
|
||||
public class PickupOpenApiService : ApplicationService
|
||||
{
|
||||
private PickupOrderFeePushService FeePush =>
|
||||
LazyServiceProvider.GetRequiredService<PickupOrderFeePushService>();
|
||||
|
||||
/// <summary>
|
||||
/// 开放平台路由:method=open.api.openCommon.pushFee,仅校验 appKey。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/openapi")]
|
||||
public Task<PickupOpenApiEnvelope> InvokeAsync(
|
||||
[FromQuery] string? appKey,
|
||||
[FromQuery] string? method,
|
||||
[FromBody] PickupOpenApiPushFeeInput? body) =>
|
||||
HandlePushFeeAsync(appKey, method, body);
|
||||
|
||||
/// <summary>
|
||||
/// 推送揽收重量/尺寸并按当前产品计算应收应付。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/openapi/openCommon/pushFee")]
|
||||
public Task<PickupOpenApiEnvelope> PushFeeAsync(
|
||||
[FromQuery] string? appKey,
|
||||
[FromBody] PickupOpenApiPushFeeInput? body) =>
|
||||
HandlePushFeeAsync(appKey, PickupOrderFeePushService.PushFeeMethod, body);
|
||||
|
||||
private async Task<PickupOpenApiEnvelope> HandlePushFeeAsync(
|
||||
string? appKey,
|
||||
string? method,
|
||||
PickupOpenApiPushFeeInput? body)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(method)
|
||||
&& !string.Equals(method.Trim(), PickupOrderFeePushService.PushFeeMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Fail("400", $"不支持的 method:{method}");
|
||||
}
|
||||
|
||||
var data = await FeePush.PushFeeAsync(appKey, body);
|
||||
return new PickupOpenApiEnvelope
|
||||
{
|
||||
Code = "200",
|
||||
Msg = "成功",
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
catch (BusinessException ex)
|
||||
{
|
||||
return Fail("400", ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Fail("500", string.IsNullOrWhiteSpace(ex.Message) ? "系统异常" : ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static PickupOpenApiEnvelope Fail(string code, string msg) =>
|
||||
new()
|
||||
{
|
||||
Code = code,
|
||||
Msg = msg,
|
||||
Data = null
|
||||
};
|
||||
}
|
||||
@ -2,7 +2,6 @@ using Fake.Application;
|
||||
using Fake.DependencyInjection;
|
||||
using Oms.Application.Dtos.Channels;
|
||||
using Oms.Application.Dtos.Orders;
|
||||
using Oms.Application.Integrations.Itfx;
|
||||
using Oms.Application.Services.Orders.Issues;
|
||||
using Oms.Application.Shared;
|
||||
using Oms.Domain.ChannelsAggregate.Entities;
|
||||
@ -10,7 +9,6 @@ using Oms.Domain.ChannelsAggregate.Repositories;
|
||||
using Oms.Domain.OrdersAggregate.Consts;
|
||||
using Oms.Domain.OrdersAggregate.Entities;
|
||||
using Oms.Domain.OrdersAggregate.Repositories;
|
||||
using Oms.Domain.SystemAggregate.Consts;
|
||||
using Oms.Domain.SystemAggregate.Repositories;
|
||||
|
||||
namespace Oms.Application.Services.Orders;
|
||||
@ -20,51 +18,34 @@ namespace Oms.Application.Services.Orders;
|
||||
/// </summary>
|
||||
public class PickupOrderFeePushService : IScopedDependency
|
||||
{
|
||||
public const string PushFeeMethod = "open.api.openCommon.pushFee";
|
||||
|
||||
private readonly IPickupOrderRepository _pickupOrderRepository;
|
||||
private readonly ISystemOtherConfigRepository _otherConfigRepository;
|
||||
private readonly ISalesProductRepository _salesProductRepository;
|
||||
private readonly IServiceChannelRepository _serviceChannelRepository;
|
||||
private readonly IServiceChannelGroupRepository _serviceChannelGroupRepository;
|
||||
private readonly IWeightRuleRepository _weightRuleRepository;
|
||||
private readonly IWeightRoundingRepository _weightRoundingRepository;
|
||||
private readonly IKpiAreaRepository _kpiAreaRepository;
|
||||
private readonly IWarehouseAddressRepository _warehouseAddressRepository;
|
||||
private readonly IItfxMetadataClient _itfxMetadataClient;
|
||||
|
||||
public PickupOrderFeePushService(
|
||||
IPickupOrderRepository pickupOrderRepository,
|
||||
ISystemOtherConfigRepository otherConfigRepository,
|
||||
ISalesProductRepository salesProductRepository,
|
||||
IServiceChannelRepository serviceChannelRepository,
|
||||
IServiceChannelGroupRepository serviceChannelGroupRepository,
|
||||
IWeightRuleRepository weightRuleRepository,
|
||||
IWeightRoundingRepository weightRoundingRepository,
|
||||
IKpiAreaRepository kpiAreaRepository,
|
||||
IWarehouseAddressRepository warehouseAddressRepository,
|
||||
IItfxMetadataClient itfxMetadataClient)
|
||||
IWarehouseAddressRepository warehouseAddressRepository)
|
||||
{
|
||||
_pickupOrderRepository = pickupOrderRepository;
|
||||
_otherConfigRepository = otherConfigRepository;
|
||||
_salesProductRepository = salesProductRepository;
|
||||
_serviceChannelRepository = serviceChannelRepository;
|
||||
_serviceChannelGroupRepository = serviceChannelGroupRepository;
|
||||
_weightRuleRepository = weightRuleRepository;
|
||||
_weightRoundingRepository = weightRoundingRepository;
|
||||
_kpiAreaRepository = kpiAreaRepository;
|
||||
_warehouseAddressRepository = warehouseAddressRepository;
|
||||
_itfxMetadataClient = itfxMetadataClient;
|
||||
}
|
||||
|
||||
public async Task<PickupOpenApiPushFeeResult> PushFeeAsync(string? appKey, PickupOpenApiPushFeeInput? input)
|
||||
public async Task<PickupOpenApiPushFeeResult> PushFeeAsync(PickupOpenApiPushFeeInput? input)
|
||||
{
|
||||
var key = (appKey ?? string.Empty).Trim();
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new BusinessException("appKey 不能为空");
|
||||
}
|
||||
|
||||
if (input == null)
|
||||
{
|
||||
throw new BusinessException("请求体不能为空");
|
||||
@ -90,8 +71,6 @@ public class PickupOrderFeePushService : IScopedDependency
|
||||
var pickup = await _pickupOrderRepository.GetByCarrierWaybillNoAsync(carrierWaybillNo)
|
||||
?? throw new BusinessException($"揽收单不存在:承运运单号 {carrierWaybillNo}");
|
||||
|
||||
await EnsureAppKeyAsync(key, pickup);
|
||||
|
||||
if (pickup.PayStatus == PickupPayStatusCodes.Paid)
|
||||
{
|
||||
throw new BusinessException("揽收单已支付,拒绝修改件重尺与费用");
|
||||
@ -240,67 +219,6 @@ public class PickupOrderFeePushService : IScopedDependency
|
||||
};
|
||||
}
|
||||
|
||||
private async Task EnsureAppKeyAsync(string appKey, PickupOrder pickup)
|
||||
{
|
||||
var config = await _otherConfigRepository.GetByConfigCodeAsync(SystemOtherConfigCodes.PickupOpenApi);
|
||||
if (config != null && config.Enabled && !string.IsNullOrWhiteSpace(config.AppKey))
|
||||
{
|
||||
if (!string.Equals(config.AppKey.Trim(), appKey, StringComparison.Ordinal))
|
||||
{
|
||||
throw new BusinessException("appKey 不正确");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var expected = await ResolvePickupAppKeyAsync(pickup);
|
||||
if (string.IsNullOrWhiteSpace(expected)
|
||||
|| !string.Equals(expected, appKey, StringComparison.Ordinal))
|
||||
{
|
||||
throw new BusinessException("appKey 不正确");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> ResolvePickupAppKeyAsync(PickupOrder pickup)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pickup.CarrierService))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var product = await _salesProductRepository.GetByProductCodeAsync(pickup.CarrierService.Trim());
|
||||
if (product == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var providers = await _salesProductRepository.GetAllocationProvidersAsync(product.Id);
|
||||
var channelId = providers
|
||||
.Where(x => x.ServiceChannelId is > 0)
|
||||
.OrderBy(x => x.SerialNumber)
|
||||
.Select(x => x.ServiceChannelId!.Value)
|
||||
.FirstOrDefault();
|
||||
if (channelId <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var channel = await _serviceChannelRepository.FirstOrDefaultAsync(x => x.Id == channelId);
|
||||
if (channel?.GroupId is not > 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var group = await _serviceChannelGroupRepository.FirstOrDefaultAsync(x => x.Id == channel.GroupId.Value);
|
||||
if (group == null || group.AppId <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var app = await _itfxMetadataClient.GetAppAsync(group.AppId);
|
||||
return app?.AppKey?.Trim();
|
||||
}
|
||||
|
||||
private async Task<(decimal VolumetricWeight, decimal ChargeableWeight)> CalculateChargeableAsync(
|
||||
SalesProduct product,
|
||||
OrderWeight weight)
|
||||
|
||||
@ -190,10 +190,7 @@ public class SystemOtherConfigService : ApplicationService
|
||||
}
|
||||
else if (string.Equals(configCode, SystemOtherConfigCodes.PickupOpenApi, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input.AppKey))
|
||||
{
|
||||
throw new BusinessException("揽收开放接口 AppKey 不能为空");
|
||||
}
|
||||
// 鉴权已改由 itfx 上游完成;本配置不再强制 AppKey
|
||||
}
|
||||
else if (string.Equals(configCode, SystemOtherConfigCodes.WeChatMp, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
||||
@ -56,7 +56,7 @@ public static class SystemOtherConfigCodes
|
||||
public const string ApiCenter = "api_center";
|
||||
|
||||
/// <summary>
|
||||
/// 国内揽收服务商推送件重尺/费用:仅校验 AppKey(暂不验签)。
|
||||
/// 国内揽收服务商推送件重尺/费用(历史配置;鉴权已改由 itfx 上游应用完成,OMS 不再校验 AppKey)。
|
||||
/// </summary>
|
||||
public const string PickupOpenApi = "pickup_open_api";
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user