257 lines
9.0 KiB
C#
257 lines
9.0 KiB
C#
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;
|
||
|
||
/// <summary>
|
||
/// 客户 OpenAPI 凭证管理(门户当前用户 + 后台代管)。
|
||
/// </summary>
|
||
[ApiExplorerSettings(GroupName = "OMS-Customer")]
|
||
public class CustomerOpenApiCredentialService : ApplicationService
|
||
{
|
||
private const string SecretMask = "********";
|
||
|
||
private ICustomerRepository CustomerRepository =>
|
||
LazyServiceProvider.GetRequiredService<ICustomerRepository>();
|
||
|
||
private ICustomerOpenApiCredentialRepository CredentialRepository =>
|
||
LazyServiceProvider.GetRequiredService<ICustomerOpenApiCredentialRepository>();
|
||
|
||
/// <summary>当前登录主客户查看凭证(Secret 掩码)。</summary>
|
||
[HttpGet("/customer/openapi-credential")]
|
||
public async Task<CustomerOpenApiCredentialOutput> GetMineAsync()
|
||
{
|
||
var customer = await GetCurrentMainCustomerOrThrowAsync();
|
||
return await MapGetAsync(customer.Id);
|
||
}
|
||
|
||
/// <summary>当前登录主客户:无则生成并返回完整 Secret;已有则掩码返回。</summary>
|
||
[HttpPost("/customer/openapi-credential/ensure")]
|
||
public async Task<CustomerOpenApiCredentialOutput> EnsureMineAsync()
|
||
{
|
||
var customer = await GetCurrentMainCustomerOrThrowAsync();
|
||
return await EnsureAsync(customer.Id);
|
||
}
|
||
|
||
/// <summary>当前登录主客户重置 Secret。</summary>
|
||
[HttpPost("/customer/openapi-credential/reset-secret")]
|
||
public async Task<CustomerOpenApiCredentialOutput> ResetMineSecretAsync()
|
||
{
|
||
var customer = await GetCurrentMainCustomerOrThrowAsync();
|
||
return await ResetSecretAsync(customer.Id);
|
||
}
|
||
|
||
/// <summary>当前登录主客户启停。</summary>
|
||
[HttpPost("/customer/openapi-credential/set-enabled")]
|
||
public async Task<CustomerOpenApiCredentialOutput> SetMineEnabledAsync(
|
||
[FromBody] CustomerOpenApiCredentialSetEnabledInput input)
|
||
{
|
||
var customer = await GetCurrentMainCustomerOrThrowAsync();
|
||
return await SetEnabledAsync(customer.Id, input.Enabled);
|
||
}
|
||
|
||
/// <summary>后台按客户查看凭证。</summary>
|
||
[HttpGet("/customer/openapi-credential/admin/get")]
|
||
public async Task<CustomerOpenApiCredentialOutput> AdminGetAsync([FromQuery] int customerId)
|
||
{
|
||
EnsureAdminSession();
|
||
await EnsureMainCustomerAsync(customerId);
|
||
return await MapGetAsync(customerId);
|
||
}
|
||
|
||
/// <summary>后台确保生成凭证。</summary>
|
||
[HttpPost("/customer/openapi-credential/admin/ensure")]
|
||
public async Task<CustomerOpenApiCredentialOutput> AdminEnsureAsync(
|
||
[FromBody] CustomerOpenApiCredentialAdminInput input)
|
||
{
|
||
EnsureAdminSession();
|
||
await EnsureMainCustomerAsync(input.CustomerId);
|
||
return await EnsureAsync(input.CustomerId);
|
||
}
|
||
|
||
/// <summary>后台重置 Secret。</summary>
|
||
[HttpPost("/customer/openapi-credential/admin/reset-secret")]
|
||
public async Task<CustomerOpenApiCredentialOutput> AdminResetSecretAsync(
|
||
[FromBody] CustomerOpenApiCredentialAdminInput input)
|
||
{
|
||
EnsureAdminSession();
|
||
await EnsureMainCustomerAsync(input.CustomerId);
|
||
return await ResetSecretAsync(input.CustomerId);
|
||
}
|
||
|
||
/// <summary>后台启停。</summary>
|
||
[HttpPost("/customer/openapi-credential/admin/set-enabled")]
|
||
public async Task<CustomerOpenApiCredentialOutput> AdminSetEnabledAsync(
|
||
[FromBody] CustomerOpenApiCredentialAdminSetEnabledInput input)
|
||
{
|
||
EnsureAdminSession();
|
||
await EnsureMainCustomerAsync(input.CustomerId);
|
||
return await SetEnabledAsync(input.CustomerId, input.Enabled);
|
||
}
|
||
|
||
private async Task<CustomerOpenApiCredentialOutput> 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<CustomerOpenApiCredentialOutput> 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<CustomerOpenApiCredentialOutput> 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<CustomerOpenApiCredentialOutput> 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<Customer> 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<string> 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)
|
||
};
|
||
}
|
||
}
|