feat: 客户 OpenAPI 机机凭证与 whoami 探针
All checks were successful
Build and Deploy / deploy (push) Successful in 58s
All checks were successful
Build and Deploy / deploy (push) Successful in 58s
一主客户一对 AppKey/AppSecret,支持门户自助与后台代管启停重置,供应商回调路径不动。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
955a3c8c6d
commit
2688058cb3
41
sql/migrate_customer_openapi_credential.sql
Normal file
41
sql/migrate_customer_openapi_credential.sql
Normal file
@ -0,0 +1,41 @@
|
||||
-- 客户 OpenAPI 机机凭证(一客户一对 AppKey/AppSecret,独立于 itfx)
|
||||
-- 可重复执行。
|
||||
-- 目标表:t_customer_openapi_credential
|
||||
|
||||
SET @db := DATABASE();
|
||||
|
||||
SET @exists := (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.TABLES
|
||||
WHERE TABLE_SCHEMA = @db
|
||||
AND TABLE_NAME = 't_customer_openapi_credential'
|
||||
);
|
||||
|
||||
SET @sql := IF(
|
||||
@exists = 0,
|
||||
'CREATE TABLE `t_customer_openapi_credential` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT COMMENT ''主键'',
|
||||
`CustomerId` INT NOT NULL COMMENT ''归属主客户 t_customer.Id'',
|
||||
`AppKey` VARCHAR(64) NOT NULL COMMENT ''开放接口 AppKey'',
|
||||
`AppSecret` VARCHAR(128) NOT NULL COMMENT ''开放接口 AppSecret(明文,创建/重置时返回)'',
|
||||
`Enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''是否启用:0-否,1-是'',
|
||||
`LastUsedAt` DATETIME NULL COMMENT ''最近鉴权成功时间'',
|
||||
`LastUsedIp` VARCHAR(64) NULL COMMENT ''最近鉴权成功 IP'',
|
||||
`Remark` VARCHAR(512) NULL COMMENT ''备注'',
|
||||
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除:0-否,1-是'',
|
||||
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT ''并发戳'',
|
||||
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT ''创建人'',
|
||||
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ''创建时间'',
|
||||
`UpdateUserId` INT NULL COMMENT ''更新人'',
|
||||
`UpdateTime` DATETIME NULL COMMENT ''更新时间'',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `uk_coc_customer` (`CustomerId`),
|
||||
UNIQUE KEY `uk_coc_app_key` (`AppKey`),
|
||||
KEY `idx_coc_enabled` (`Enabled`, `IsDeleted`)
|
||||
) COMMENT=''客户 OpenAPI 机机凭证''',
|
||||
'SELECT ''t_customer_openapi_credential already exists'' AS info'
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
@ -0,0 +1,66 @@
|
||||
namespace Oms.Application.Dtos.Customers;
|
||||
|
||||
public class CustomerOpenApiCredentialOutput
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
public string AppKey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>默认掩码;仅 ensure(新建)/ reset-secret 返回完整明文。</summary>
|
||||
public string AppSecret { get; set; } = string.Empty;
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public bool HasCredential { get; set; }
|
||||
|
||||
/// <summary>本次响应是否包含完整 AppSecret(前端可提示用户复制保存)。</summary>
|
||||
public bool SecretRevealed { get; set; }
|
||||
|
||||
public DateTime? LastUsedAt { get; set; }
|
||||
|
||||
public string? LastUsedIp { get; set; }
|
||||
|
||||
public DateTime? CreateTime { get; set; }
|
||||
|
||||
public DateTime? UpdateTime { get; set; }
|
||||
}
|
||||
|
||||
public class CustomerOpenApiCredentialAdminInput
|
||||
{
|
||||
public int CustomerId { get; set; }
|
||||
}
|
||||
|
||||
public class CustomerOpenApiCredentialSetEnabledInput
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
|
||||
public class CustomerOpenApiCredentialAdminSetEnabledInput : CustomerOpenApiCredentialAdminInput
|
||||
{
|
||||
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; }
|
||||
}
|
||||
@ -0,0 +1,255 @@
|
||||
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)
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
82
src/Oms.Application/Shared/CustomerOpenApiAuthHelper.cs
Normal file
82
src/Oms.Application/Shared/CustomerOpenApiAuthHelper.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using Fake.Application;
|
||||
using Fake.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Oms.Domain.CustomersAggregate.Entities;
|
||||
using Oms.Domain.CustomersAggregate.Enums;
|
||||
using Oms.Domain.CustomersAggregate.Repositories;
|
||||
|
||||
namespace Oms.Application.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// 客户 OpenAPI 机机鉴权:按 AppKey+AppSecret 解析主客户,供 whoami 及后续业务接口复用。
|
||||
/// </summary>
|
||||
public class CustomerOpenApiAuthHelper : IScopedDependency
|
||||
{
|
||||
private readonly ICustomerOpenApiCredentialRepository _credentialRepository;
|
||||
private readonly ICustomerRepository _customerRepository;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public CustomerOpenApiAuthHelper(
|
||||
ICustomerOpenApiCredentialRepository credentialRepository,
|
||||
ICustomerRepository customerRepository,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_credentialRepository = credentialRepository;
|
||||
_customerRepository = customerRepository;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
public async Task<(Customer Customer, CustomerOpenApiCredential Credential)> ResolveAsync(
|
||||
string? appKey,
|
||||
string? appSecret,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var key = appKey?.Trim();
|
||||
var secret = appSecret?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(secret))
|
||||
{
|
||||
throw new BusinessException("凭证无效");
|
||||
}
|
||||
|
||||
var credential = await _credentialRepository.GetByAppKeyAsync(key, cancellationToken);
|
||||
if (credential == null
|
||||
|| !credential.Enabled
|
||||
|| !string.Equals(credential.AppSecret, secret, StringComparison.Ordinal))
|
||||
{
|
||||
throw new BusinessException("凭证无效");
|
||||
}
|
||||
|
||||
var customer = await _customerRepository.FirstOrDefaultAsync(
|
||||
x => x.Id == credential.CustomerId,
|
||||
cancellationToken);
|
||||
if (customer == null
|
||||
|| customer.ParentCustomerId.HasValue
|
||||
|| customer.Status != CustomerStatus.Normal)
|
||||
{
|
||||
throw new BusinessException("凭证无效");
|
||||
}
|
||||
|
||||
credential.LastUsedAt = DateTime.Now;
|
||||
credential.LastUsedIp = ResolveClientIp();
|
||||
await _credentialRepository.UpdateAsync(credential);
|
||||
|
||||
return (customer, credential);
|
||||
}
|
||||
|
||||
private string? ResolveClientIp()
|
||||
{
|
||||
var http = _httpContextAccessor.HttpContext;
|
||||
if (http == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var forwarded = http.Request.Headers["X-Forwarded-For"].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(forwarded))
|
||||
{
|
||||
return forwarded.Split(',')[0].Trim();
|
||||
}
|
||||
|
||||
return http.Connection.RemoteIpAddress?.ToString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
using Oms.Domain.Shared;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Oms.Domain.CustomersAggregate.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 客户 OpenAPI 机机凭证(一主客户一对密钥,独立于 itfx)。
|
||||
/// </summary>
|
||||
[SugarTable("t_customer_openapi_credential")]
|
||||
public class CustomerOpenApiCredential : OmsAuditedAggregateRoot
|
||||
{
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
[SugarColumn(Length = 64)]
|
||||
public string AppKey { get; set; } = string.Empty;
|
||||
|
||||
[SugarColumn(Length = 128)]
|
||||
public string AppSecret { get; set; } = string.Empty;
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public DateTime? LastUsedAt { get; set; }
|
||||
|
||||
[SugarColumn(Length = 64, IsNullable = true)]
|
||||
public string? LastUsedIp { get; set; }
|
||||
|
||||
[SugarColumn(Length = 512, IsNullable = true)]
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
using Fake.Domain.Repositories;
|
||||
using Oms.Domain.CustomersAggregate.Entities;
|
||||
|
||||
namespace Oms.Domain.CustomersAggregate.Repositories;
|
||||
|
||||
public interface ICustomerOpenApiCredentialRepository : IRepository<CustomerOpenApiCredential>
|
||||
{
|
||||
Task<CustomerOpenApiCredential?> GetByCustomerIdAsync(
|
||||
int customerId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<CustomerOpenApiCredential?> GetByAppKeyAsync(
|
||||
string appKey,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
using Fake.DependencyInjection;
|
||||
using Oms.Domain.CustomersAggregate.Entities;
|
||||
using Oms.Domain.CustomersAggregate.Repositories;
|
||||
using Oms.SqlSugarCore.Repositories;
|
||||
|
||||
namespace Oms.SqlSugarCore.Customers;
|
||||
|
||||
public class CustomerOpenApiCredentialRepository
|
||||
: OmsSqlSugarRepository<CustomerOpenApiCredential>,
|
||||
ICustomerOpenApiCredentialRepository,
|
||||
IScopedDependency
|
||||
{
|
||||
public Task<CustomerOpenApiCredential?> GetByCustomerIdAsync(
|
||||
int customerId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return FirstOrDefaultAsync(x => x.CustomerId == customerId, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<CustomerOpenApiCredential?> GetByAppKeyAsync(
|
||||
string appKey,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return FirstOrDefaultAsync(x => x.AppKey == appKey, cancellationToken);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user