chore: 提交现有修改

This commit is contained in:
2025-12-02 12:11:25 +08:00
parent 541b75ecd8
commit 5332c87d9d
37 changed files with 429 additions and 677 deletions

View File

@@ -15,31 +15,19 @@ namespace TakeoutSaaS.Application.Dictionary.Services;
/// <summary>
/// 参数字典应用服务实现。
/// </summary>
public sealed class DictionaryAppService : IDictionaryAppService
public sealed class DictionaryAppService(
IDictionaryRepository repository,
IDictionaryCache cache,
ITenantProvider tenantProvider,
ILogger<DictionaryAppService> logger) : IDictionaryAppService
{
private readonly IDictionaryRepository _repository;
private readonly IDictionaryCache _cache;
private readonly ITenantProvider _tenantProvider;
private readonly ILogger<DictionaryAppService> _logger;
public DictionaryAppService(
IDictionaryRepository repository,
IDictionaryCache cache,
ITenantProvider tenantProvider,
ILogger<DictionaryAppService> logger)
{
_repository = repository;
_cache = cache;
_tenantProvider = tenantProvider;
_logger = logger;
}
public async Task<DictionaryGroupDto> CreateGroupAsync(CreateDictionaryGroupRequest request, CancellationToken cancellationToken = default)
{
var normalizedCode = NormalizeCode(request.Code);
var targetTenant = ResolveTargetTenant(request.Scope);
var existing = await _repository.FindGroupByCodeAsync(normalizedCode, cancellationToken);
var existing = await repository.FindGroupByCodeAsync(normalizedCode, cancellationToken);
if (existing != null)
{
throw new BusinessException(ErrorCodes.Conflict, $"字典分组编码 {normalizedCode} 已存在");
@@ -56,9 +44,9 @@ public sealed class DictionaryAppService : IDictionaryAppService
IsEnabled = true
};
await _repository.AddGroupAsync(group, cancellationToken);
await _repository.SaveChangesAsync(cancellationToken);
_logger.LogInformation("创建字典分组:{Code}({Scope})", group.Code, group.Scope);
await repository.AddGroupAsync(group, cancellationToken);
await repository.SaveChangesAsync(cancellationToken);
logger.LogInformation("创建字典分组:{Code}({Scope})", group.Code, group.Scope);
return MapGroup(group, includeItems: false);
}
@@ -71,9 +59,9 @@ public sealed class DictionaryAppService : IDictionaryAppService
group.Description = request.Description?.Trim();
group.IsEnabled = request.IsEnabled;
await _repository.SaveChangesAsync(cancellationToken);
await repository.SaveChangesAsync(cancellationToken);
await InvalidateCacheAsync(group, cancellationToken);
_logger.LogInformation("更新字典分组:{GroupId}", group.Id);
logger.LogInformation("更新字典分组:{GroupId}", group.Id);
return MapGroup(group, includeItems: false);
}
@@ -82,19 +70,19 @@ public sealed class DictionaryAppService : IDictionaryAppService
var group = await RequireGroupAsync(groupId, cancellationToken);
EnsureScopePermission(group.Scope);
await _repository.RemoveGroupAsync(group, cancellationToken);
await _repository.SaveChangesAsync(cancellationToken);
await repository.RemoveGroupAsync(group, cancellationToken);
await repository.SaveChangesAsync(cancellationToken);
await InvalidateCacheAsync(group, cancellationToken);
_logger.LogInformation("删除字典分组:{GroupId}", group.Id);
logger.LogInformation("删除字典分组:{GroupId}", group.Id);
}
public async Task<IReadOnlyList<DictionaryGroupDto>> SearchGroupsAsync(DictionaryGroupQuery request, CancellationToken cancellationToken = default)
{
var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = tenantProvider.GetCurrentTenantId();
var scope = ResolveScopeForQuery(request.Scope, tenantId);
EnsureScopePermission(scope);
var groups = await _repository.SearchGroupsAsync(scope, cancellationToken);
var groups = await repository.SearchGroupsAsync(scope, cancellationToken);
var includeItems = request.IncludeItems;
var result = new List<DictionaryGroupDto>(groups.Count);
@@ -103,7 +91,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
IReadOnlyList<DictionaryItemDto> items = Array.Empty<DictionaryItemDto>();
if (includeItems)
{
var itemEntities = await _repository.GetItemsByGroupIdAsync(group.Id, cancellationToken);
var itemEntities = await repository.GetItemsByGroupIdAsync(group.Id, cancellationToken);
items = itemEntities.Select(MapItem).ToList();
}
@@ -131,10 +119,10 @@ public sealed class DictionaryAppService : IDictionaryAppService
IsEnabled = request.IsEnabled
};
await _repository.AddItemAsync(item, cancellationToken);
await _repository.SaveChangesAsync(cancellationToken);
await repository.AddItemAsync(item, cancellationToken);
await repository.SaveChangesAsync(cancellationToken);
await InvalidateCacheAsync(group, cancellationToken);
_logger.LogInformation("新增字典项:{ItemId}", item.Id);
logger.LogInformation("新增字典项:{ItemId}", item.Id);
return MapItem(item);
}
@@ -150,9 +138,9 @@ public sealed class DictionaryAppService : IDictionaryAppService
item.IsDefault = request.IsDefault;
item.IsEnabled = request.IsEnabled;
await _repository.SaveChangesAsync(cancellationToken);
await repository.SaveChangesAsync(cancellationToken);
await InvalidateCacheAsync(group, cancellationToken);
_logger.LogInformation("更新字典项:{ItemId}", item.Id);
logger.LogInformation("更新字典项:{ItemId}", item.Id);
return MapItem(item);
}
@@ -162,10 +150,10 @@ public sealed class DictionaryAppService : IDictionaryAppService
var group = await RequireGroupAsync(item.GroupId, cancellationToken);
EnsureScopePermission(group.Scope);
await _repository.RemoveItemAsync(item, cancellationToken);
await _repository.SaveChangesAsync(cancellationToken);
await repository.RemoveItemAsync(item, cancellationToken);
await repository.SaveChangesAsync(cancellationToken);
await InvalidateCacheAsync(group, cancellationToken);
_logger.LogInformation("删除字典项:{ItemId}", item.Id);
logger.LogInformation("删除字典项:{ItemId}", item.Id);
}
public async Task<IReadOnlyDictionary<string, IReadOnlyList<DictionaryItemDto>>> GetCachedItemsAsync(DictionaryBatchQueryRequest request, CancellationToken cancellationToken = default)
@@ -181,7 +169,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
return new Dictionary<string, IReadOnlyList<DictionaryItemDto>>(StringComparer.OrdinalIgnoreCase);
}
var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = tenantProvider.GetCurrentTenantId();
var result = new Dictionary<string, IReadOnlyList<DictionaryItemDto>>(StringComparer.OrdinalIgnoreCase);
foreach (var code in normalizedCodes)
@@ -202,7 +190,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
private async Task<DictionaryGroup> RequireGroupAsync(long groupId, CancellationToken cancellationToken)
{
var group = await _repository.FindGroupByIdAsync(groupId, cancellationToken);
var group = await repository.FindGroupByIdAsync(groupId, cancellationToken);
if (group == null)
{
throw new BusinessException(ErrorCodes.NotFound, "字典分组不存在");
@@ -213,7 +201,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
private async Task<DictionaryItem> RequireItemAsync(long itemId, CancellationToken cancellationToken)
{
var item = await _repository.FindItemByIdAsync(itemId, cancellationToken);
var item = await repository.FindItemByIdAsync(itemId, cancellationToken);
if (item == null)
{
throw new BusinessException(ErrorCodes.NotFound, "字典项不存在");
@@ -224,7 +212,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
private long ResolveTargetTenant(DictionaryScope scope)
{
var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = tenantProvider.GetCurrentTenantId();
if (scope == DictionaryScope.System)
{
EnsurePlatformTenant(tenantId);
@@ -253,7 +241,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
private void EnsureScopePermission(DictionaryScope scope)
{
var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = tenantProvider.GetCurrentTenantId();
if (scope == DictionaryScope.System && tenantId != 0)
{
throw new BusinessException(ErrorCodes.Forbidden, "仅平台管理员可操作系统字典");
@@ -270,7 +258,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
private async Task InvalidateCacheAsync(DictionaryGroup group, CancellationToken cancellationToken)
{
await _cache.RemoveAsync(group.TenantId, group.Code, cancellationToken);
await cache.RemoveAsync(group.TenantId, group.Code, cancellationToken);
if (group.Scope == DictionaryScope.Business)
{
return;
@@ -281,20 +269,20 @@ public sealed class DictionaryAppService : IDictionaryAppService
private async Task<IReadOnlyList<DictionaryItemDto>> GetOrLoadCacheAsync(long tenantId, string code, CancellationToken cancellationToken)
{
var cached = await _cache.GetAsync(tenantId, code, cancellationToken);
var cached = await cache.GetAsync(tenantId, code, cancellationToken);
if (cached != null)
{
return cached;
}
var entities = await _repository.GetItemsByCodesAsync(new[] { code }, tenantId, includeSystem: false, cancellationToken);
var entities = await repository.GetItemsByCodesAsync(new[] { code }, tenantId, includeSystem: false, cancellationToken);
var items = entities
.Where(item => item.IsEnabled && (item.Group?.IsEnabled ?? true))
.Select(MapItem)
.OrderBy(item => item.SortOrder)
.ToList();
await _cache.SetAsync(tenantId, code, items, cancellationToken);
await cache.SetAsync(tenantId, code, items, cancellationToken);
return items;
}

View File

@@ -5,30 +5,25 @@ namespace TakeoutSaaS.Application.Sms.Contracts;
/// <summary>
/// 发送验证码请求。
/// </summary>
public sealed class SendVerificationCodeRequest
/// <remarks>
/// 创建发送请求。
/// </remarks>
public sealed class SendVerificationCodeRequest(string phoneNumber, string scene, SmsProviderKind? provider = null)
{
/// <summary>
/// 创建发送请求。
/// </summary>
public SendVerificationCodeRequest(string phoneNumber, string scene, SmsProviderKind? provider = null)
{
PhoneNumber = phoneNumber;
Scene = scene;
Provider = provider;
}
/// <summary>
/// 手机号(支持 +86 前缀或纯 11 位)。
/// </summary>
public string PhoneNumber { get; }
public string PhoneNumber { get; } = phoneNumber;
/// <summary>
/// 业务场景(如 login/register/reset
/// </summary>
public string Scene { get; }
public string Scene { get; } = scene;
/// <summary>
/// 指定服务商,未指定则使用默认配置。
/// </summary>
public SmsProviderKind? Provider { get; }
public SmsProviderKind? Provider { get; } = provider;
}

View File

@@ -3,30 +3,25 @@ namespace TakeoutSaaS.Application.Sms.Contracts;
/// <summary>
/// 校验验证码请求。
/// </summary>
public sealed class VerifyVerificationCodeRequest
/// <remarks>
/// 创建校验请求。
/// </remarks>
public sealed class VerifyVerificationCodeRequest(string phoneNumber, string scene, string code)
{
/// <summary>
/// 创建校验请求。
/// </summary>
public VerifyVerificationCodeRequest(string phoneNumber, string scene, string code)
{
PhoneNumber = phoneNumber;
Scene = scene;
Code = code;
}
/// <summary>
/// 手机号。
/// </summary>
public string PhoneNumber { get; }
public string PhoneNumber { get; } = phoneNumber;
/// <summary>
/// 业务场景。
/// </summary>
public string Scene { get; }
public string Scene { get; } = scene;
/// <summary>
/// 填写的验证码。
/// </summary>
public string Code { get; }
public string Code { get; } = code;
}

View File

@@ -5,42 +5,35 @@ namespace TakeoutSaaS.Application.Storage.Contracts;
/// <summary>
/// 直传凭证请求模型。
/// </summary>
public sealed class DirectUploadRequest
/// <remarks>
/// 创建直传请求。
/// </remarks>
public sealed class DirectUploadRequest(UploadFileType fileType, string fileName, string contentType, long contentLength, string? requestOrigin)
{
/// <summary>
/// 创建直传请求。
/// </summary>
public DirectUploadRequest(UploadFileType fileType, string fileName, string contentType, long contentLength, string? requestOrigin)
{
FileType = fileType;
FileName = fileName;
ContentType = contentType;
ContentLength = contentLength;
RequestOrigin = requestOrigin;
}
/// <summary>
/// 文件类型。
/// </summary>
public UploadFileType FileType { get; }
public UploadFileType FileType { get; } = fileType;
/// <summary>
/// 文件名。
/// </summary>
public string FileName { get; }
public string FileName { get; } = fileName;
/// <summary>
/// 内容类型。
/// </summary>
public string ContentType { get; }
public string ContentType { get; } = contentType;
/// <summary>
/// 文件长度。
/// </summary>
public long ContentLength { get; }
public long ContentLength { get; } = contentLength;
/// <summary>
/// 请求来源Origin/Referer
/// </summary>
public string? RequestOrigin { get; }
public string? RequestOrigin { get; } = requestOrigin;
}

View File

@@ -6,54 +6,46 @@ namespace TakeoutSaaS.Application.Storage.Contracts;
/// <summary>
/// 上传文件请求模型。
/// </summary>
public sealed class UploadFileRequest
/// <remarks>
/// 创建上传文件请求。
/// </remarks>
public sealed class UploadFileRequest(
UploadFileType fileType,
Stream content,
string fileName,
string contentType,
long contentLength,
string? requestOrigin)
{
/// <summary>
/// 创建上传文件请求。
/// </summary>
public UploadFileRequest(
UploadFileType fileType,
Stream content,
string fileName,
string contentType,
long contentLength,
string? requestOrigin)
{
FileType = fileType;
Content = content;
FileName = fileName;
ContentType = contentType;
ContentLength = contentLength;
RequestOrigin = requestOrigin;
}
/// <summary>
/// 文件分类。
/// </summary>
public UploadFileType FileType { get; }
public UploadFileType FileType { get; } = fileType;
/// <summary>
/// 文件流。
/// </summary>
public Stream Content { get; }
public Stream Content { get; } = content;
/// <summary>
/// 原始文件名。
/// </summary>
public string FileName { get; }
public string FileName { get; } = fileName;
/// <summary>
/// 内容类型。
/// </summary>
public string ContentType { get; }
public string ContentType { get; } = contentType;
/// <summary>
/// 文件大小。
/// </summary>
public long ContentLength { get; }
public long ContentLength { get; } = contentLength;
/// <summary>
/// 请求来源Origin/Referer
/// </summary>
public string? RequestOrigin { get; }
public string? RequestOrigin { get; } = requestOrigin;
}