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