using System.Security.Cryptography;
using Fake.Application;
using Microsoft.AspNetCore.Mvc;
using Oms.Application.Dtos.Customers;
using Oms.Application.Shared;
using Oms.Domain.CustomersAggregate.Entities;
using Oms.Domain.CustomersAggregate.Repositories;
namespace Oms.Application.Services.Customers;
///
/// 客户 OpenAPI 凭证管理(门户当前用户 + 后台代管)。
///
[ApiExplorerSettings(GroupName = "OMS-Customer")]
public class CustomerOpenApiCredentialService : ApplicationService
{
private const string SecretMask = "********";
private ICustomerRepository CustomerRepository =>
LazyServiceProvider.GetRequiredService();
private ICustomerOpenApiCredentialRepository CredentialRepository =>
LazyServiceProvider.GetRequiredService();
/// 当前登录主客户查看凭证(Secret 掩码)。
[HttpGet("/customer/openapi-credential")]
public async Task GetMineAsync()
{
var customer = await GetCurrentMainCustomerOrThrowAsync();
return await MapGetAsync(customer.Id);
}
/// 当前登录主客户:无则生成并返回完整 Secret;已有则掩码返回。
[HttpPost("/customer/openapi-credential/ensure")]
public async Task EnsureMineAsync()
{
var customer = await GetCurrentMainCustomerOrThrowAsync();
return await EnsureAsync(customer.Id);
}
/// 当前登录主客户重置 Secret。
[HttpPost("/customer/openapi-credential/reset-secret")]
public async Task ResetMineSecretAsync()
{
var customer = await GetCurrentMainCustomerOrThrowAsync();
return await ResetSecretAsync(customer.Id);
}
/// 当前登录主客户启停。
[HttpPost("/customer/openapi-credential/set-enabled")]
public async Task SetMineEnabledAsync(
[FromBody] CustomerOpenApiCredentialSetEnabledInput input)
{
var customer = await GetCurrentMainCustomerOrThrowAsync();
return await SetEnabledAsync(customer.Id, input.Enabled);
}
/// 后台按客户查看凭证。
[HttpGet("/customer/openapi-credential/admin/get")]
public async Task AdminGetAsync([FromQuery] int customerId)
{
EnsureAdminSession();
await EnsureMainCustomerAsync(customerId);
return await MapGetAsync(customerId);
}
/// 后台确保生成凭证。
[HttpPost("/customer/openapi-credential/admin/ensure")]
public async Task AdminEnsureAsync(
[FromBody] CustomerOpenApiCredentialAdminInput input)
{
EnsureAdminSession();
await EnsureMainCustomerAsync(input.CustomerId);
return await EnsureAsync(input.CustomerId);
}
/// 后台重置 Secret。
[HttpPost("/customer/openapi-credential/admin/reset-secret")]
public async Task AdminResetSecretAsync(
[FromBody] CustomerOpenApiCredentialAdminInput input)
{
EnsureAdminSession();
await EnsureMainCustomerAsync(input.CustomerId);
return await ResetSecretAsync(input.CustomerId);
}
/// 后台启停。
[HttpPost("/customer/openapi-credential/admin/set-enabled")]
public async Task AdminSetEnabledAsync(
[FromBody] CustomerOpenApiCredentialAdminSetEnabledInput input)
{
EnsureAdminSession();
await EnsureMainCustomerAsync(input.CustomerId);
return await SetEnabledAsync(input.CustomerId, input.Enabled);
}
private async Task MapGetAsync(int customerId)
{
var entity = await CredentialRepository.GetByCustomerIdAsync(customerId);
if (entity == null)
{
return new CustomerOpenApiCredentialOutput
{
CustomerId = customerId,
HasCredential = false,
Enabled = false,
AppKey = string.Empty,
AppSecret = string.Empty,
SecretRevealed = false
};
}
return MapOutput(entity, revealSecret: false);
}
private async Task EnsureAsync(int customerId)
{
var existing = await CredentialRepository.GetByCustomerIdAsync(customerId);
if (existing != null)
{
return MapOutput(existing, revealSecret: false);
}
var entity = new CustomerOpenApiCredential
{
CustomerId = customerId,
AppKey = await GenerateUniqueAppKeyAsync(),
AppSecret = GenerateAppSecret(),
Enabled = true
};
entity = await CredentialRepository.InsertAsync(entity);
return MapOutput(entity, revealSecret: true);
}
private async Task ResetSecretAsync(int customerId)
{
var entity = await CredentialRepository.GetByCustomerIdAsync(customerId);
if (entity == null)
{
throw new BusinessException("尚未生成开放接口凭证,请先生成");
}
entity.AppSecret = GenerateAppSecret();
await CredentialRepository.UpdateAsync(entity);
return MapOutput(entity, revealSecret: true);
}
private async Task SetEnabledAsync(int customerId, bool enabled)
{
var entity = await CredentialRepository.GetByCustomerIdAsync(customerId);
if (entity == null)
{
throw new BusinessException("尚未生成开放接口凭证,请先生成");
}
entity.Enabled = enabled;
await CredentialRepository.UpdateAsync(entity);
return MapOutput(entity, revealSecret: false);
}
private async Task EnsureMainCustomerAsync(int customerId)
{
if (customerId <= 0)
{
throw new BusinessException("客户ID不能为空");
}
var customer = await CustomerRepository.FirstOrDefaultAsync(x => x.Id == customerId);
if (customer == null)
{
throw new BusinessException("客户不存在");
}
if (customer.ParentCustomerId.HasValue)
{
throw new BusinessException("仅主客户可配置开放接口凭证");
}
}
private async Task GetCurrentMainCustomerOrThrowAsync()
{
if (!OmsSessionHelper.IsCustomerLogin(CurrentUser))
{
throw new BusinessException(
"当前是管理后台登录,不能调用客户门户接口。请到「客户」列表打开对应客户的开放接口凭证;客户请从客户门户登录后操作");
}
var mapId = OmsSessionHelper.TryGetCustomerMapId(CurrentUser);
if (!mapId.HasValue || mapId.Value <= 0)
{
throw new BusinessException("当前登录不是客户账号,无法读取开放接口凭证");
}
var customer = await CustomerRepository.FirstOrDefaultAsync(x => x.UserId == mapId.Value);
if (customer == null)
{
throw new BusinessException("客户不存在");
}
if (customer.ParentCustomerId.HasValue)
{
throw new BusinessException("仅主客户可管理开放接口凭证");
}
return customer;
}
private void EnsureAdminSession()
{
if (OmsSessionHelper.IsCustomerLogin(CurrentUser))
{
throw new BusinessException("无权限");
}
}
private async Task GenerateUniqueAppKeyAsync()
{
for (var i = 0; i < 8; i++)
{
var key = $"oms_{Guid.NewGuid():N}"[..20];
var existed = await CredentialRepository.GetByAppKeyAsync(key);
if (existed == null)
{
return key;
}
}
throw new BusinessException("生成 AppKey 失败,请重试");
}
private static string GenerateAppSecret()
{
var bytes = RandomNumberGenerator.GetBytes(24);
return Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', 'x').Replace('/', 'y');
}
private static CustomerOpenApiCredentialOutput MapOutput(
CustomerOpenApiCredential entity,
bool revealSecret)
{
return new CustomerOpenApiCredentialOutput
{
Id = entity.Id,
CustomerId = entity.CustomerId,
AppKey = entity.AppKey,
AppSecret = revealSecret ? entity.AppSecret : SecretMask,
Enabled = entity.Enabled,
HasCredential = true,
SecretRevealed = revealSecret,
LastUsedAt = entity.LastUsedAt,
LastUsedIp = entity.LastUsedIp,
CreateTime = entity.CreateTime,
UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime)
};
}
}