From 5332c87d9da94aac6101837ffb82b2324979f049 Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Tue, 2 Dec 2025 12:11:25 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=8F=90=E4=BA=A4=E7=8E=B0=E6=9C=89?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/AuthController.cs | 21 ++-- .../Controllers/DictionaryController.cs | 31 +++--- .../Controllers/AuthController.cs | 19 ++-- .../Controllers/MeController.cs | 17 ++- .../Services/DictionaryAppService.cs | 80 ++++++-------- .../Contracts/SendVerificationCodeRequest.cs | 21 ++-- .../VerifyVerificationCodeRequest.cs | 21 ++-- .../Storage/Contracts/DirectUploadRequest.cs | 27 ++--- .../Storage/Contracts/UploadFileRequest.cs | 42 +++----- .../Exceptions/BusinessException.cs | 9 +- .../Exceptions/ValidationException.cs | 10 +- .../Results/PagedResult.cs | 27 ++--- .../Tenancy/TenantContext.cs | 27 ++--- .../Ids/SnowflakeIdGenerator.cs | 24 ++--- .../Middleware/CorrelationIdMiddleware.cs | 19 +--- .../Middleware/ExceptionHandlingMiddleware.cs | 19 +--- .../Middleware/RequestLoggingMiddleware.cs | 14 +-- .../HttpContextCurrentUserAccessor.cs | 15 +-- .../Swagger/ConfigureSwaggerOptions.cs | 17 +-- .../App/Persistence/AppDataSeeder.cs | 50 ++++----- .../App/Repositories/EfDeliveryRepository.cs | 37 +++---- .../App/Repositories/EfMerchantRepository.cs | 39 +++---- .../App/Repositories/EfOrderRepository.cs | 53 +++++---- .../App/Repositories/EfPaymentRepository.cs | 37 +++---- .../App/Repositories/EfProductRepository.cs | 101 +++++++++--------- .../App/Repositories/EfStoreRepository.cs | 51 ++++----- .../Repositories/EfDictionaryRepository.cs | 30 +++--- .../Services/DistributedDictionaryCache.cs | 17 +-- .../Persistence/EfIdentityUserRepository.cs | 12 +-- .../Persistence/EfMiniUserRepository.cs | 18 ++-- .../Services/RedisLoginRateLimiter.cs | 17 +-- .../Services/RedisRefreshTokenStore.cs | 17 +-- .../Identity/Services/WeChatAuthService.cs | 13 +-- .../Models/StorageDirectUploadRequest.cs | 32 +++--- .../Models/StorageUploadRequest.cs | 64 +++++------ .../TenantProvider.cs | 17 ++- .../TenantResolutionMiddleware.cs | 41 +++---- 37 files changed, 429 insertions(+), 677 deletions(-) diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs index 962ee57..b406c49 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs @@ -17,21 +17,16 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 管理后台认证接口 /// +/// +/// +/// +/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/auth")] -public sealed class AuthController : BaseApiController +public sealed class AuthController(IAdminAuthService authService) : BaseApiController { - private readonly IAdminAuthService _authService; - /// - /// - /// - /// - public AuthController(IAdminAuthService authService) - { - _authService = authService; - } /// /// 登录获取 Token @@ -41,7 +36,7 @@ public sealed class AuthController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Login([FromBody] AdminLoginRequest request, CancellationToken cancellationToken) { - var response = await _authService.LoginAsync(request, cancellationToken); + var response = await authService.LoginAsync(request, cancellationToken); return ApiResponse.Ok(response); } @@ -53,7 +48,7 @@ public sealed class AuthController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> RefreshToken([FromBody] RefreshTokenRequest request, CancellationToken cancellationToken) { - var response = await _authService.RefreshTokenAsync(request, cancellationToken); + var response = await authService.RefreshTokenAsync(request, cancellationToken); return ApiResponse.Ok(response); } @@ -72,7 +67,7 @@ public sealed class AuthController : BaseApiController return ApiResponse.Error(ErrorCodes.Unauthorized, "Token 缺少有效的用户标识"); } - var profile = await _authService.GetProfileAsync(userId, cancellationToken); + var profile = await authService.GetProfileAsync(userId, cancellationToken); return ApiResponse.Ok(profile); } } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs index f63c273..d98e420 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs @@ -12,21 +12,16 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 参数字典管理。 /// +/// +/// 初始化字典控制器。 +/// +/// 字典服务 [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/dictionaries")] -public sealed class DictionaryController : BaseApiController +public sealed class DictionaryController(IDictionaryAppService dictionaryAppService) : BaseApiController { - private readonly IDictionaryAppService _dictionaryAppService; - /// - /// 初始化字典控制器。 - /// - /// 字典服务 - public DictionaryController(IDictionaryAppService dictionaryAppService) - { - _dictionaryAppService = dictionaryAppService; - } /// /// 查询字典分组。 @@ -36,7 +31,7 @@ public sealed class DictionaryController : BaseApiController [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> GetGroups([FromQuery] DictionaryGroupQuery query, CancellationToken cancellationToken) { - var groups = await _dictionaryAppService.SearchGroupsAsync(query, cancellationToken); + var groups = await dictionaryAppService.SearchGroupsAsync(query, cancellationToken); return ApiResponse>.Ok(groups); } @@ -48,7 +43,7 @@ public sealed class DictionaryController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> CreateGroup([FromBody] CreateDictionaryGroupRequest request, CancellationToken cancellationToken) { - var group = await _dictionaryAppService.CreateGroupAsync(request, cancellationToken); + var group = await dictionaryAppService.CreateGroupAsync(request, cancellationToken); return ApiResponse.Ok(group); } @@ -60,7 +55,7 @@ public sealed class DictionaryController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> UpdateGroup(long groupId, [FromBody] UpdateDictionaryGroupRequest request, CancellationToken cancellationToken) { - var group = await _dictionaryAppService.UpdateGroupAsync(groupId, request, cancellationToken); + var group = await dictionaryAppService.UpdateGroupAsync(groupId, request, cancellationToken); return ApiResponse.Ok(group); } @@ -72,7 +67,7 @@ public sealed class DictionaryController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> DeleteGroup(long groupId, CancellationToken cancellationToken) { - await _dictionaryAppService.DeleteGroupAsync(groupId, cancellationToken); + await dictionaryAppService.DeleteGroupAsync(groupId, cancellationToken); return ApiResponse.Success(); } @@ -85,7 +80,7 @@ public sealed class DictionaryController : BaseApiController public async Task> CreateItem(long groupId, [FromBody] CreateDictionaryItemRequest request, CancellationToken cancellationToken) { request.GroupId = groupId; - var item = await _dictionaryAppService.CreateItemAsync(request, cancellationToken); + var item = await dictionaryAppService.CreateItemAsync(request, cancellationToken); return ApiResponse.Ok(item); } @@ -97,7 +92,7 @@ public sealed class DictionaryController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> UpdateItem(long itemId, [FromBody] UpdateDictionaryItemRequest request, CancellationToken cancellationToken) { - var item = await _dictionaryAppService.UpdateItemAsync(itemId, request, cancellationToken); + var item = await dictionaryAppService.UpdateItemAsync(itemId, request, cancellationToken); return ApiResponse.Ok(item); } @@ -109,7 +104,7 @@ public sealed class DictionaryController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> DeleteItem(long itemId, CancellationToken cancellationToken) { - await _dictionaryAppService.DeleteItemAsync(itemId, cancellationToken); + await dictionaryAppService.DeleteItemAsync(itemId, cancellationToken); return ApiResponse.Success(); } @@ -120,7 +115,7 @@ public sealed class DictionaryController : BaseApiController [ProducesResponseType(typeof(ApiResponse>>), StatusCodes.Status200OK)] public async Task>>> BatchGet([FromBody] DictionaryBatchQueryRequest request, CancellationToken cancellationToken) { - var dictionaries = await _dictionaryAppService.GetCachedItemsAsync(request, cancellationToken); + var dictionaries = await dictionaryAppService.GetCachedItemsAsync(request, cancellationToken); return ApiResponse>>.Ok(dictionaries); } } diff --git a/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs b/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs index 07961a3..332dec2 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs @@ -10,21 +10,16 @@ namespace TakeoutSaaS.MiniApi.Controllers; /// /// 小程序登录认证 /// +/// +/// 小程序登录认证 +/// +/// [ApiVersion("1.0")] [Authorize] [Route("api/mini/v{version:apiVersion}/auth")] -public sealed class AuthController : BaseApiController +public sealed class AuthController(IMiniAuthService authService) : BaseApiController { - private readonly IMiniAuthService _authService; - /// - /// 小程序登录认证 - /// - /// - public AuthController(IMiniAuthService authService) - { - _authService = authService; - } /// /// 微信登录 @@ -34,7 +29,7 @@ public sealed class AuthController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> LoginWithWeChat([FromBody] WeChatLoginRequest request, CancellationToken cancellationToken) { - var response = await _authService.LoginWithWeChatAsync(request, cancellationToken); + var response = await authService.LoginWithWeChatAsync(request, cancellationToken); return ApiResponse.Ok(response); } @@ -46,7 +41,7 @@ public sealed class AuthController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> RefreshToken([FromBody] RefreshTokenRequest request, CancellationToken cancellationToken) { - var response = await _authService.RefreshTokenAsync(request, cancellationToken); + var response = await authService.RefreshTokenAsync(request, cancellationToken); return ApiResponse.Ok(response); } } diff --git a/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs b/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs index 4dd1fa8..4bd29e4 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs @@ -16,21 +16,16 @@ namespace TakeoutSaaS.MiniApi.Controllers; /// /// 当前用户信息 /// +/// +/// +/// +/// [ApiVersion("1.0")] [Authorize] [Route("api/mini/v{version:apiVersion}/me")] -public sealed class MeController : BaseApiController +public sealed class MeController(IMiniAuthService authService) : BaseApiController { - private readonly IMiniAuthService _authService; - /// - /// - /// - /// - public MeController(IMiniAuthService authService) - { - _authService = authService; - } /// /// 获取用户档案 @@ -46,7 +41,7 @@ public sealed class MeController : BaseApiController return ApiResponse.Error(ErrorCodes.Unauthorized, "Token 缺少有效的用户标识"); } - var profile = await _authService.GetProfileAsync(userId, cancellationToken); + var profile = await authService.GetProfileAsync(userId, cancellationToken); return ApiResponse.Ok(profile); } } diff --git a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs index 8ecfc79..5ff8e9a 100644 --- a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs +++ b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs @@ -15,31 +15,19 @@ namespace TakeoutSaaS.Application.Dictionary.Services; /// /// 参数字典应用服务实现。 /// -public sealed class DictionaryAppService : IDictionaryAppService +public sealed class DictionaryAppService( + IDictionaryRepository repository, + IDictionaryCache cache, + ITenantProvider tenantProvider, + ILogger logger) : IDictionaryAppService { - private readonly IDictionaryRepository _repository; - private readonly IDictionaryCache _cache; - private readonly ITenantProvider _tenantProvider; - private readonly ILogger _logger; - - public DictionaryAppService( - IDictionaryRepository repository, - IDictionaryCache cache, - ITenantProvider tenantProvider, - ILogger logger) - { - _repository = repository; - _cache = cache; - _tenantProvider = tenantProvider; - _logger = logger; - } public async Task 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> 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(groups.Count); @@ -103,7 +91,7 @@ public sealed class DictionaryAppService : IDictionaryAppService IReadOnlyList items = Array.Empty(); 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>> GetCachedItemsAsync(DictionaryBatchQueryRequest request, CancellationToken cancellationToken = default) @@ -181,7 +169,7 @@ public sealed class DictionaryAppService : IDictionaryAppService return new Dictionary>(StringComparer.OrdinalIgnoreCase); } - var tenantId = _tenantProvider.GetCurrentTenantId(); + var tenantId = tenantProvider.GetCurrentTenantId(); var result = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var code in normalizedCodes) @@ -202,7 +190,7 @@ public sealed class DictionaryAppService : IDictionaryAppService private async Task 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 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> 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; } diff --git a/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs b/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs index 16e659d..402385f 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs @@ -5,30 +5,25 @@ namespace TakeoutSaaS.Application.Sms.Contracts; /// /// 发送验证码请求。 /// -public sealed class SendVerificationCodeRequest +/// +/// 创建发送请求。 +/// +public sealed class SendVerificationCodeRequest(string phoneNumber, string scene, SmsProviderKind? provider = null) { - /// - /// 创建发送请求。 - /// - public SendVerificationCodeRequest(string phoneNumber, string scene, SmsProviderKind? provider = null) - { - PhoneNumber = phoneNumber; - Scene = scene; - Provider = provider; - } + /// /// 手机号(支持 +86 前缀或纯 11 位)。 /// - public string PhoneNumber { get; } + public string PhoneNumber { get; } = phoneNumber; /// /// 业务场景(如 login/register/reset)。 /// - public string Scene { get; } + public string Scene { get; } = scene; /// /// 指定服务商,未指定则使用默认配置。 /// - public SmsProviderKind? Provider { get; } + public SmsProviderKind? Provider { get; } = provider; } diff --git a/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs b/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs index 57df45e..034230c 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs @@ -3,30 +3,25 @@ namespace TakeoutSaaS.Application.Sms.Contracts; /// /// 校验验证码请求。 /// -public sealed class VerifyVerificationCodeRequest +/// +/// 创建校验请求。 +/// +public sealed class VerifyVerificationCodeRequest(string phoneNumber, string scene, string code) { - /// - /// 创建校验请求。 - /// - public VerifyVerificationCodeRequest(string phoneNumber, string scene, string code) - { - PhoneNumber = phoneNumber; - Scene = scene; - Code = code; - } + /// /// 手机号。 /// - public string PhoneNumber { get; } + public string PhoneNumber { get; } = phoneNumber; /// /// 业务场景。 /// - public string Scene { get; } + public string Scene { get; } = scene; /// /// 填写的验证码。 /// - public string Code { get; } + public string Code { get; } = code; } diff --git a/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs b/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs index de9a69f..b757e36 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs @@ -5,42 +5,35 @@ namespace TakeoutSaaS.Application.Storage.Contracts; /// /// 直传凭证请求模型。 /// -public sealed class DirectUploadRequest +/// +/// 创建直传请求。 +/// +public sealed class DirectUploadRequest(UploadFileType fileType, string fileName, string contentType, long contentLength, string? requestOrigin) { - /// - /// 创建直传请求。 - /// - public DirectUploadRequest(UploadFileType fileType, string fileName, string contentType, long contentLength, string? requestOrigin) - { - FileType = fileType; - FileName = fileName; - ContentType = contentType; - ContentLength = contentLength; - RequestOrigin = requestOrigin; - } + /// /// 文件类型。 /// - public UploadFileType FileType { get; } + public UploadFileType FileType { get; } = fileType; /// /// 文件名。 /// - public string FileName { get; } + public string FileName { get; } = fileName; /// /// 内容类型。 /// - public string ContentType { get; } + public string ContentType { get; } = contentType; /// /// 文件长度。 /// - public long ContentLength { get; } + public long ContentLength { get; } = contentLength; /// /// 请求来源(Origin/Referer)。 /// - public string? RequestOrigin { get; } + public string? RequestOrigin { get; } = requestOrigin; } diff --git a/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs b/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs index d60bb7b..ea19f91 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs @@ -6,54 +6,46 @@ namespace TakeoutSaaS.Application.Storage.Contracts; /// /// 上传文件请求模型。 /// -public sealed class UploadFileRequest +/// +/// 创建上传文件请求。 +/// +public sealed class UploadFileRequest( + UploadFileType fileType, + Stream content, + string fileName, + string contentType, + long contentLength, + string? requestOrigin) { - /// - /// 创建上传文件请求。 - /// - 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; - } + /// /// 文件分类。 /// - public UploadFileType FileType { get; } + public UploadFileType FileType { get; } = fileType; /// /// 文件流。 /// - public Stream Content { get; } + public Stream Content { get; } = content; /// /// 原始文件名。 /// - public string FileName { get; } + public string FileName { get; } = fileName; /// /// 内容类型。 /// - public string ContentType { get; } + public string ContentType { get; } = contentType; /// /// 文件大小。 /// - public long ContentLength { get; } + public long ContentLength { get; } = contentLength; /// /// 请求来源(Origin/Referer)。 /// - public string? RequestOrigin { get; } + public string? RequestOrigin { get; } = requestOrigin; } diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/BusinessException.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/BusinessException.cs index c270130..b14dc4a 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/BusinessException.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/BusinessException.cs @@ -3,16 +3,11 @@ namespace TakeoutSaaS.Shared.Abstractions.Exceptions; /// /// 业务异常(用于可预期的业务校验错误)。 /// -public class BusinessException : Exception +public class BusinessException(int errorCode, string message) : Exception(message) { /// /// 业务错误码。 /// - public int ErrorCode { get; } - - public BusinessException(int errorCode, string message) : base(message) - { - ErrorCode = errorCode; - } + public int ErrorCode { get; } = errorCode; } diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs index a4c48d0..a87def6 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs @@ -6,17 +6,11 @@ namespace TakeoutSaaS.Shared.Abstractions.Exceptions; /// /// 验证异常(用于聚合验证错误信息)。 /// -public class ValidationException : Exception +public class ValidationException(IDictionary errors) : Exception("一个或多个验证错误") { /// /// 字段/属性的错误集合。 /// - public IDictionary Errors { get; } - - public ValidationException(IDictionary errors) - : base("一个或多个验证错误") - { - Errors = errors; - } + public IDictionary Errors { get; } = errors; } diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs index aee0aee..69c5ba4 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs @@ -6,42 +6,33 @@ namespace TakeoutSaaS.Shared.Abstractions.Results; /// 分页结果包装,携带列表与总条数等元数据。 /// /// 数据类型。 -public sealed class PagedResult +/// +/// 初始化分页结果。 +/// +public sealed class PagedResult(IReadOnlyList items, int page, int pageSize, int totalCount) { /// /// 数据列表。 /// - public IReadOnlyList Items { get; } + public IReadOnlyList Items { get; } = items; /// /// 当前页码,从 1 开始。 /// - public int Page { get; } + public int Page { get; } = page; /// /// 每页条数。 /// - public int PageSize { get; } + public int PageSize { get; } = pageSize; /// /// 总条数。 /// - public int TotalCount { get; } + public int TotalCount { get; } = totalCount; /// /// 总页数。 /// - public int TotalPages { get; } - - /// - /// 初始化分页结果。 - /// - public PagedResult(IReadOnlyList items, int page, int pageSize, int totalCount) - { - Items = items; - Page = page; - PageSize = pageSize; - TotalCount = totalCount; - TotalPages = pageSize == 0 ? 0 : (int)Math.Ceiling(totalCount / (double)pageSize); - } + public int TotalPages { get; } = pageSize == 0 ? 0 : (int)Math.Ceiling(totalCount / (double)pageSize); } diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/TenantContext.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/TenantContext.cs index a53b38f..8ba5da6 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/TenantContext.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/TenantContext.cs @@ -3,40 +3,33 @@ namespace TakeoutSaaS.Shared.Abstractions.Tenancy; /// /// 租户上下文:封装当前请求解析得到的租户标识、编号及解析来源。 /// -public sealed class TenantContext +/// +/// 初始化租户上下文。 +/// +/// 租户 ID +/// 租户编码(可选) +/// 解析来源 +public sealed class TenantContext(long tenantId, string? tenantCode, string source) { /// /// 未解析到租户时的默认上下文。 /// public static TenantContext Empty { get; } = new(0, null, "unresolved"); - /// - /// 初始化租户上下文。 - /// - /// 租户 ID - /// 租户编码(可选) - /// 解析来源 - public TenantContext(long tenantId, string? tenantCode, string source) - { - TenantId = tenantId; - TenantCode = tenantCode; - Source = source; - } - /// /// 当前租户 ID,未解析时为 Guid.Empty。 /// - public long TenantId { get; } + public long TenantId { get; } = tenantId; /// /// 当前租户编码(例如子域名或业务编码),可为空。 /// - public string? TenantCode { get; } + public string? TenantCode { get; } = tenantCode; /// /// 租户解析来源(Header、Host、Token 等)。 /// - public string Source { get; } + public string Source { get; } = source; /// /// 是否已成功解析到租户。 diff --git a/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs b/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs index 533789d..06d22fb 100644 --- a/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs +++ b/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs @@ -8,7 +8,12 @@ namespace TakeoutSaaS.Shared.Kernel.Ids; /// /// 基于雪花算法的长整型 ID 生成器。 /// -public sealed class SnowflakeIdGenerator : IIdGenerator +/// +/// 初始化生成器。 +/// +/// 工作节点 ID。 +/// 机房 ID。 +public sealed class SnowflakeIdGenerator(long workerId = 0, long datacenterId = 0) : IIdGenerator { private const long Twepoch = 1577836800000L; // 2020-01-01 UTC private const int WorkerIdBits = 5; @@ -23,23 +28,12 @@ public sealed class SnowflakeIdGenerator : IIdGenerator private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; private const long SequenceMask = -1L ^ (-1L << SequenceBits); - private readonly long _workerId; - private readonly long _datacenterId; + private readonly long _workerId = Normalize(workerId, MaxWorkerId, nameof(workerId)); + private readonly long _datacenterId = Normalize(datacenterId, MaxDatacenterId, nameof(datacenterId)); private long _lastTimestamp = -1L; - private long _sequence; + private long _sequence = RandomNumberGenerator.GetInt32(0, (int)SequenceMask); private readonly object _syncRoot = new(); - /// - /// 初始化生成器。 - /// - /// 工作节点 ID。 - /// 机房 ID。 - public SnowflakeIdGenerator(long workerId = 0, long datacenterId = 0) - { - _workerId = Normalize(workerId, MaxWorkerId, nameof(workerId)); - _datacenterId = Normalize(datacenterId, MaxDatacenterId, nameof(datacenterId)); - _sequence = RandomNumberGenerator.GetInt32(0, (int)SequenceMask); - } /// public long NextId() diff --git a/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs b/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs index ddbed3c..07740f7 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs @@ -11,22 +11,11 @@ namespace TakeoutSaaS.Shared.Web.Middleware; /// /// 统一 TraceId/CorrelationId,贯穿日志与响应。 /// -public sealed class CorrelationIdMiddleware +public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger logger, IIdGenerator idGenerator) { private const string TraceHeader = "X-Trace-Id"; private const string RequestHeader = "X-Request-Id"; - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly IIdGenerator _idGenerator; - - public CorrelationIdMiddleware(RequestDelegate next, ILogger logger, IIdGenerator idGenerator) - { - _next = next; - _logger = logger; - _idGenerator = idGenerator; - } - public async Task InvokeAsync(HttpContext context) { var traceId = ResolveTraceId(context); @@ -39,14 +28,14 @@ public sealed class CorrelationIdMiddleware return Task.CompletedTask; }); - using (_logger.BeginScope(new Dictionary + using (logger.BeginScope(new Dictionary { ["TraceId"] = traceId })) { try { - await _next(context); + await next(context); } finally { @@ -67,7 +56,7 @@ public sealed class CorrelationIdMiddleware return requestId; } - return _idGenerator.NextId().ToString(); + return idGenerator.NextId().ToString(); } private static bool TryGetHeader(HttpContext context, string headerName, out string value) diff --git a/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs b/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs index da85777..2babea2 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs @@ -14,34 +14,23 @@ namespace TakeoutSaaS.Shared.Web.Middleware; /// /// 全局异常处理中间件,将异常统一映射为 ApiResponse。 /// -public sealed class ExceptionHandlingMiddleware +public sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger, IHostEnvironment environment) { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly IHostEnvironment _environment; - private static readonly JsonSerializerOptions SerializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; - public ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger, IHostEnvironment environment) - { - _next = next; - _logger = logger; - _environment = environment; - } - public async Task InvokeAsync(HttpContext context) { try { - await _next(context); + await next(context); } catch (Exception ex) { - _logger.LogError(ex, "未处理异常:{Message}", ex.Message); + logger.LogError(ex, "未处理异常:{Message}", ex.Message); await HandleExceptionAsync(context, ex); } } @@ -50,7 +39,7 @@ public sealed class ExceptionHandlingMiddleware { var (statusCode, response) = BuildErrorResponse(exception); - if (_environment.IsDevelopment()) + if (environment.IsDevelopment()) { response = response with { diff --git a/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs b/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs index 8121499..d2c12db 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs @@ -9,29 +9,21 @@ namespace TakeoutSaaS.Shared.Web.Middleware; /// /// 基础请求日志(方法、路径、耗时、状态码、TraceId)。 /// -public sealed class RequestLoggingMiddleware +public sealed class RequestLoggingMiddleware(RequestDelegate next, ILogger logger) { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - - public RequestLoggingMiddleware(RequestDelegate next, ILogger logger) - { - _next = next; - _logger = logger; - } public async Task InvokeAsync(HttpContext context) { var stopwatch = Stopwatch.StartNew(); try { - await _next(context); + await next(context); } finally { stopwatch.Stop(); var traceId = TraceContext.TraceId ?? context.TraceIdentifier; - _logger.LogInformation( + logger.LogInformation( "HTTP {Method} {Path} => {StatusCode} ({Elapsed} ms) TraceId:{TraceId}", context.Request.Method, context.Request.Path, diff --git a/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs b/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs index 6256f05..e7f5209 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs @@ -7,24 +7,19 @@ namespace TakeoutSaaS.Shared.Web.Security; /// /// 基于 HttpContext 的当前用户访问器。 /// -public sealed class HttpContextCurrentUserAccessor : ICurrentUserAccessor +/// +/// 初始化访问器。 +/// +public sealed class HttpContextCurrentUserAccessor(IHttpContextAccessor httpContextAccessor) : ICurrentUserAccessor { - private readonly IHttpContextAccessor _httpContextAccessor; - /// - /// 初始化访问器。 - /// - public HttpContextCurrentUserAccessor(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } /// public long UserId { get { - var principal = _httpContextAccessor.HttpContext?.User; + var principal = httpContextAccessor.HttpContext?.User; if (principal == null || !principal.Identity?.IsAuthenticated == true) { return 0; diff --git a/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs b/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs index e620d67..17f0c2a 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs @@ -9,22 +9,15 @@ namespace TakeoutSaaS.Shared.Web.Swagger; /// /// 根据 API 版本动态注册 Swagger 文档。 /// -internal sealed class ConfigureSwaggerOptions : IConfigureOptions +internal sealed class ConfigureSwaggerOptions( + IApiVersionDescriptionProvider provider, + IOptions settings) : IConfigureOptions { - private readonly IApiVersionDescriptionProvider _provider; - private readonly SwaggerDocumentSettings _settings; - - public ConfigureSwaggerOptions( - IApiVersionDescriptionProvider provider, - IOptions settings) - { - _provider = provider; - _settings = settings.Value; - } + private readonly SwaggerDocumentSettings _settings = settings.Value; public void Configure(SwaggerGenOptions options) { - foreach (var description in _provider.ApiVersionDescriptions) + foreach (var description in provider.ApiVersionDescriptions) { var info = new OpenApiInfo { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs index e9a70d7..c6949ce 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs @@ -16,42 +16,34 @@ namespace TakeoutSaaS.Infrastructure.App.Persistence; /// /// 业务数据种子,确保默认租户与基础字典可重复执行。 /// -public sealed class AppDataSeeder : IHostedService +/// +/// 初始化种子服务。 +/// +public sealed class AppDataSeeder( + IServiceProvider serviceProvider, + ILogger logger, + IOptions options) : IHostedService { - private readonly IServiceProvider _serviceProvider; - private readonly ILogger _logger; - private readonly AppSeedOptions _options; + private readonly AppSeedOptions _options = options.Value; - /// - /// 初始化种子服务。 - /// - public AppDataSeeder( - IServiceProvider serviceProvider, - ILogger logger, - IOptions options) - { - _serviceProvider = serviceProvider; - _logger = logger; - _options = options.Value; - } /// public async Task StartAsync(CancellationToken cancellationToken) { if (!_options.Enabled) { - _logger.LogInformation("AppSeed 未启用,跳过业务数据初始化"); + logger.LogInformation("AppSeed 未启用,跳过业务数据初始化"); return; } - using var scope = _serviceProvider.CreateScope(); + using var scope = serviceProvider.CreateScope(); var appDbContext = scope.ServiceProvider.GetRequiredService(); var dictionaryDbContext = scope.ServiceProvider.GetRequiredService(); var defaultTenantId = await EnsureDefaultTenantAsync(appDbContext, cancellationToken); await EnsureDictionarySeedsAsync(dictionaryDbContext, defaultTenantId, cancellationToken); - _logger.LogInformation("AppSeed 完成业务数据初始化"); + logger.LogInformation("AppSeed 完成业务数据初始化"); } /// @@ -65,7 +57,7 @@ public sealed class AppDataSeeder : IHostedService var tenantOptions = _options.DefaultTenant; if (tenantOptions == null || string.IsNullOrWhiteSpace(tenantOptions.Code) || string.IsNullOrWhiteSpace(tenantOptions.Name)) { - _logger.LogInformation("AppSeed 未配置默认租户,跳过租户种子"); + logger.LogInformation("AppSeed 未配置默认租户,跳过租户种子"); return null; } @@ -89,7 +81,7 @@ public sealed class AppDataSeeder : IHostedService await dbContext.Tenants.AddAsync(tenant, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); - _logger.LogInformation("AppSeed 已创建默认租户 {TenantCode}", code); + logger.LogInformation("AppSeed 已创建默认租户 {TenantCode}", code); return tenant.Id; } @@ -129,11 +121,11 @@ public sealed class AppDataSeeder : IHostedService { dbContext.Tenants.Update(existingTenant); await dbContext.SaveChangesAsync(cancellationToken); - _logger.LogInformation("AppSeed 已更新默认租户 {TenantCode}", code); + logger.LogInformation("AppSeed 已更新默认租户 {TenantCode}", code); } else { - _logger.LogInformation("AppSeed 默认租户 {TenantCode} 已存在且无需更新", code); + logger.LogInformation("AppSeed 默认租户 {TenantCode} 已存在且无需更新", code); } return existingTenant.Id; @@ -149,7 +141,7 @@ public sealed class AppDataSeeder : IHostedService if (!hasDictionaryGroups) { - _logger.LogInformation("AppSeed 未配置基础字典,跳过字典种子"); + logger.LogInformation("AppSeed 未配置基础字典,跳过字典种子"); } if (hasDictionaryGroups) @@ -158,7 +150,7 @@ public sealed class AppDataSeeder : IHostedService { if (string.IsNullOrWhiteSpace(groupOptions.Code) || string.IsNullOrWhiteSpace(groupOptions.Name)) { - _logger.LogWarning("AppSeed 跳过字典分组,Code 或 Name 为空"); + logger.LogWarning("AppSeed 跳过字典分组,Code 或 Name 为空"); continue; } @@ -183,7 +175,7 @@ public sealed class AppDataSeeder : IHostedService }; await dbContext.DictionaryGroups.AddAsync(group, cancellationToken); - _logger.LogInformation("AppSeed 创建字典分组 {GroupCode} (Tenant: {TenantId})", code, tenantId); + logger.LogInformation("AppSeed 创建字典分组 {GroupCode} (Tenant: {TenantId})", code, tenantId); } else { @@ -236,7 +228,7 @@ public sealed class AppDataSeeder : IHostedService if (systemParameters.Count == 0) { - _logger.LogInformation("AppSeed 未配置系统参数,跳过系统参数种子"); + logger.LogInformation("AppSeed 未配置系统参数,跳过系统参数种子"); return; } @@ -246,7 +238,7 @@ public sealed class AppDataSeeder : IHostedService if (!grouped.Any()) { - _logger.LogInformation("AppSeed 系统参数配置为空,跳过系统参数种子"); + logger.LogInformation("AppSeed 系统参数配置为空,跳过系统参数种子"); return; } @@ -271,7 +263,7 @@ public sealed class AppDataSeeder : IHostedService }; await dbContext.DictionaryGroups.AddAsync(dictionaryGroup, cancellationToken); - _logger.LogInformation("AppSeed 创建系统参数分组 (Tenant: {TenantId})", tenantId); + logger.LogInformation("AppSeed 创建系统参数分组 (Tenant: {TenantId})", tenantId); } var seedItems = group.Select(x => new DictionarySeedItemOptions diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs index 32b482f..34917c7 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs @@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// /// 配送聚合的 EF Core 仓储实现。 /// -public sealed class EfDeliveryRepository : IDeliveryRepository +/// +/// 初始化仓储。 +/// +public sealed class EfDeliveryRepository(TakeoutAppDbContext context) : IDeliveryRepository { - private readonly TakeoutAppDbContext _context; - /// - /// 初始化仓储。 - /// - public EfDeliveryRepository(TakeoutAppDbContext context) - { - _context = context; - } /// public Task FindByIdAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default) { - return _context.DeliveryOrders + return context.DeliveryOrders .AsNoTracking() .Where(x => x.TenantId == tenantId && x.Id == deliveryOrderId) .FirstOrDefaultAsync(cancellationToken); @@ -34,7 +29,7 @@ public sealed class EfDeliveryRepository : IDeliveryRepository /// public Task FindByOrderIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { - return _context.DeliveryOrders + return context.DeliveryOrders .AsNoTracking() .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .FirstOrDefaultAsync(cancellationToken); @@ -43,7 +38,7 @@ public sealed class EfDeliveryRepository : IDeliveryRepository /// public async Task> GetEventsAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default) { - var events = await _context.DeliveryEvents + var events = await context.DeliveryEvents .AsNoTracking() .Where(x => x.TenantId == tenantId && x.DeliveryOrderId == deliveryOrderId) .OrderBy(x => x.CreatedAt) @@ -55,25 +50,25 @@ public sealed class EfDeliveryRepository : IDeliveryRepository /// public Task AddDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default) { - return _context.DeliveryOrders.AddAsync(deliveryOrder, cancellationToken).AsTask(); + return context.DeliveryOrders.AddAsync(deliveryOrder, cancellationToken).AsTask(); } /// public Task AddEventAsync(DeliveryEvent deliveryEvent, CancellationToken cancellationToken = default) { - return _context.DeliveryEvents.AddAsync(deliveryEvent, cancellationToken).AsTask(); + return context.DeliveryEvents.AddAsync(deliveryEvent, cancellationToken).AsTask(); } /// public Task SaveChangesAsync(CancellationToken cancellationToken = default) { - return _context.SaveChangesAsync(cancellationToken); + return context.SaveChangesAsync(cancellationToken); } /// public async Task> SearchAsync(long tenantId, DeliveryStatus? status, long? orderId, CancellationToken cancellationToken = default) { - var query = _context.DeliveryOrders + var query = context.DeliveryOrders .AsNoTracking() .Where(x => x.TenantId == tenantId); @@ -95,23 +90,23 @@ public sealed class EfDeliveryRepository : IDeliveryRepository /// public Task UpdateDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default) { - _context.DeliveryOrders.Update(deliveryOrder); + context.DeliveryOrders.Update(deliveryOrder); return Task.CompletedTask; } /// public async Task DeleteDeliveryOrderAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default) { - var events = await _context.DeliveryEvents + var events = await context.DeliveryEvents .Where(x => x.TenantId == tenantId && x.DeliveryOrderId == deliveryOrderId) .ToListAsync(cancellationToken); if (events.Count > 0) { - _context.DeliveryEvents.RemoveRange(events); + context.DeliveryEvents.RemoveRange(events); } - var existing = await _context.DeliveryOrders + var existing = await context.DeliveryOrders .Where(x => x.TenantId == tenantId && x.Id == deliveryOrderId) .FirstOrDefaultAsync(cancellationToken); @@ -120,6 +115,6 @@ public sealed class EfDeliveryRepository : IDeliveryRepository return; } - _context.DeliveryOrders.Remove(existing); + context.DeliveryOrders.Remove(existing); } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs index 3aaa2c5..6158f92 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs @@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// /// 商户聚合的 EF Core 仓储实现。 /// -public sealed class EfMerchantRepository : IMerchantRepository +/// +/// 初始化仓储。 +/// +public sealed class EfMerchantRepository(TakeoutAppDbContext context) : IMerchantRepository { - private readonly TakeoutAppDbContext _context; - /// - /// 初始化仓储。 - /// - public EfMerchantRepository(TakeoutAppDbContext context) - { - _context = context; - } /// public Task FindByIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default) { - return _context.Merchants + return context.Merchants .AsNoTracking() .Where(x => x.TenantId == tenantId && x.Id == merchantId) .FirstOrDefaultAsync(cancellationToken); @@ -34,7 +29,7 @@ public sealed class EfMerchantRepository : IMerchantRepository /// public async Task> SearchAsync(long tenantId, MerchantStatus? status, CancellationToken cancellationToken = default) { - var query = _context.Merchants + var query = context.Merchants .AsNoTracking() .Where(x => x.TenantId == tenantId); @@ -51,7 +46,7 @@ public sealed class EfMerchantRepository : IMerchantRepository /// public async Task> GetStaffAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default) { - var staffs = await _context.MerchantStaff + var staffs = await context.MerchantStaff .AsNoTracking() .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId) .OrderBy(x => x.Name) @@ -63,7 +58,7 @@ public sealed class EfMerchantRepository : IMerchantRepository /// public async Task> GetContractsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default) { - var contracts = await _context.MerchantContracts + var contracts = await context.MerchantContracts .AsNoTracking() .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId) .OrderByDescending(x => x.CreatedAt) @@ -75,7 +70,7 @@ public sealed class EfMerchantRepository : IMerchantRepository /// public async Task> GetDocumentsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default) { - var documents = await _context.MerchantDocuments + var documents = await context.MerchantDocuments .AsNoTracking() .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId) .OrderBy(x => x.CreatedAt) @@ -87,44 +82,44 @@ public sealed class EfMerchantRepository : IMerchantRepository /// public Task AddMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default) { - return _context.Merchants.AddAsync(merchant, cancellationToken).AsTask(); + return context.Merchants.AddAsync(merchant, cancellationToken).AsTask(); } /// public Task AddStaffAsync(MerchantStaff staff, CancellationToken cancellationToken = default) { - return _context.MerchantStaff.AddAsync(staff, cancellationToken).AsTask(); + return context.MerchantStaff.AddAsync(staff, cancellationToken).AsTask(); } /// public Task AddContractAsync(MerchantContract contract, CancellationToken cancellationToken = default) { - return _context.MerchantContracts.AddAsync(contract, cancellationToken).AsTask(); + return context.MerchantContracts.AddAsync(contract, cancellationToken).AsTask(); } /// public Task AddDocumentAsync(MerchantDocument document, CancellationToken cancellationToken = default) { - return _context.MerchantDocuments.AddAsync(document, cancellationToken).AsTask(); + return context.MerchantDocuments.AddAsync(document, cancellationToken).AsTask(); } /// public Task SaveChangesAsync(CancellationToken cancellationToken = default) { - return _context.SaveChangesAsync(cancellationToken); + return context.SaveChangesAsync(cancellationToken); } /// public Task UpdateMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default) { - _context.Merchants.Update(merchant); + context.Merchants.Update(merchant); return Task.CompletedTask; } /// public async Task DeleteMerchantAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default) { - var existing = await _context.Merchants + var existing = await context.Merchants .Where(x => x.TenantId == tenantId && x.Id == merchantId) .FirstOrDefaultAsync(cancellationToken); @@ -133,6 +128,6 @@ public sealed class EfMerchantRepository : IMerchantRepository return; } - _context.Merchants.Remove(existing); + context.Merchants.Remove(existing); } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs index 91aa04f..c73a185 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs @@ -11,22 +11,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// /// 订单聚合的 EF Core 仓储实现。 /// -public sealed class EfOrderRepository : IOrderRepository +/// +/// 初始化仓储。 +/// +public sealed class EfOrderRepository(TakeoutAppDbContext context) : IOrderRepository { - private readonly TakeoutAppDbContext _context; - /// - /// 初始化仓储。 - /// - public EfOrderRepository(TakeoutAppDbContext context) - { - _context = context; - } /// public Task FindByIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { - return _context.Orders + return context.Orders .AsNoTracking() .Where(x => x.TenantId == tenantId && x.Id == orderId) .FirstOrDefaultAsync(cancellationToken); @@ -35,7 +30,7 @@ public sealed class EfOrderRepository : IOrderRepository /// public Task FindByOrderNoAsync(string orderNo, long tenantId, CancellationToken cancellationToken = default) { - return _context.Orders + return context.Orders .AsNoTracking() .Where(x => x.TenantId == tenantId && x.OrderNo == orderNo) .FirstOrDefaultAsync(cancellationToken); @@ -44,7 +39,7 @@ public sealed class EfOrderRepository : IOrderRepository /// public async Task> SearchAsync(long tenantId, OrderStatus? status, PaymentStatus? paymentStatus, CancellationToken cancellationToken = default) { - var query = _context.Orders + var query = context.Orders .AsNoTracking() .Where(x => x.TenantId == tenantId); @@ -68,7 +63,7 @@ public sealed class EfOrderRepository : IOrderRepository /// public async Task> GetItemsAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { - var items = await _context.OrderItems + var items = await context.OrderItems .AsNoTracking() .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .OrderBy(x => x.Id) @@ -80,7 +75,7 @@ public sealed class EfOrderRepository : IOrderRepository /// public async Task> GetStatusHistoryAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { - var histories = await _context.OrderStatusHistories + var histories = await context.OrderStatusHistories .AsNoTracking() .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .OrderBy(x => x.CreatedAt) @@ -92,7 +87,7 @@ public sealed class EfOrderRepository : IOrderRepository /// public async Task> GetRefundsAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { - var refunds = await _context.RefundRequests + var refunds = await context.RefundRequests .AsNoTracking() .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .OrderByDescending(x => x.CreatedAt) @@ -104,68 +99,68 @@ public sealed class EfOrderRepository : IOrderRepository /// public Task AddOrderAsync(Order order, CancellationToken cancellationToken = default) { - return _context.Orders.AddAsync(order, cancellationToken).AsTask(); + return context.Orders.AddAsync(order, cancellationToken).AsTask(); } /// public Task AddItemsAsync(IEnumerable items, CancellationToken cancellationToken = default) { - return _context.OrderItems.AddRangeAsync(items, cancellationToken); + return context.OrderItems.AddRangeAsync(items, cancellationToken); } /// public Task AddStatusHistoryAsync(OrderStatusHistory history, CancellationToken cancellationToken = default) { - return _context.OrderStatusHistories.AddAsync(history, cancellationToken).AsTask(); + return context.OrderStatusHistories.AddAsync(history, cancellationToken).AsTask(); } /// public Task AddRefundAsync(RefundRequest refund, CancellationToken cancellationToken = default) { - return _context.RefundRequests.AddAsync(refund, cancellationToken).AsTask(); + return context.RefundRequests.AddAsync(refund, cancellationToken).AsTask(); } /// public Task SaveChangesAsync(CancellationToken cancellationToken = default) { - return _context.SaveChangesAsync(cancellationToken); + return context.SaveChangesAsync(cancellationToken); } /// public Task UpdateOrderAsync(Order order, CancellationToken cancellationToken = default) { - _context.Orders.Update(order); + context.Orders.Update(order); return Task.CompletedTask; } /// public async Task DeleteOrderAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { - var items = await _context.OrderItems + var items = await context.OrderItems .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .ToListAsync(cancellationToken); if (items.Count > 0) { - _context.OrderItems.RemoveRange(items); + context.OrderItems.RemoveRange(items); } - var histories = await _context.OrderStatusHistories + var histories = await context.OrderStatusHistories .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .ToListAsync(cancellationToken); if (histories.Count > 0) { - _context.OrderStatusHistories.RemoveRange(histories); + context.OrderStatusHistories.RemoveRange(histories); } - var refunds = await _context.RefundRequests + var refunds = await context.RefundRequests .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .ToListAsync(cancellationToken); if (refunds.Count > 0) { - _context.RefundRequests.RemoveRange(refunds); + context.RefundRequests.RemoveRange(refunds); } - var existing = await _context.Orders + var existing = await context.Orders .Where(x => x.TenantId == tenantId && x.Id == orderId) .FirstOrDefaultAsync(cancellationToken); if (existing == null) @@ -173,6 +168,6 @@ public sealed class EfOrderRepository : IOrderRepository return; } - _context.Orders.Remove(existing); + context.Orders.Remove(existing); } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs index f4dfb6c..90be2a6 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs @@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// /// 支付记录的 EF Core 仓储实现。 /// -public sealed class EfPaymentRepository : IPaymentRepository +/// +/// 初始化仓储。 +/// +public sealed class EfPaymentRepository(TakeoutAppDbContext context) : IPaymentRepository { - private readonly TakeoutAppDbContext _context; - /// - /// 初始化仓储。 - /// - public EfPaymentRepository(TakeoutAppDbContext context) - { - _context = context; - } /// public Task FindByIdAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default) { - return _context.PaymentRecords + return context.PaymentRecords .AsNoTracking() .Where(x => x.TenantId == tenantId && x.Id == paymentId) .FirstOrDefaultAsync(cancellationToken); @@ -34,7 +29,7 @@ public sealed class EfPaymentRepository : IPaymentRepository /// public Task FindByOrderIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { - return _context.PaymentRecords + return context.PaymentRecords .AsNoTracking() .Where(x => x.TenantId == tenantId && x.OrderId == orderId) .FirstOrDefaultAsync(cancellationToken); @@ -43,7 +38,7 @@ public sealed class EfPaymentRepository : IPaymentRepository /// public async Task> GetRefundsAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default) { - var refunds = await _context.PaymentRefundRecords + var refunds = await context.PaymentRefundRecords .AsNoTracking() .Where(x => x.TenantId == tenantId && x.PaymentRecordId == paymentId) .OrderByDescending(x => x.CreatedAt) @@ -55,19 +50,19 @@ public sealed class EfPaymentRepository : IPaymentRepository /// public Task AddPaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default) { - return _context.PaymentRecords.AddAsync(payment, cancellationToken).AsTask(); + return context.PaymentRecords.AddAsync(payment, cancellationToken).AsTask(); } /// public Task AddRefundAsync(PaymentRefundRecord refund, CancellationToken cancellationToken = default) { - return _context.PaymentRefundRecords.AddAsync(refund, cancellationToken).AsTask(); + return context.PaymentRefundRecords.AddAsync(refund, cancellationToken).AsTask(); } /// public async Task> SearchAsync(long tenantId, PaymentStatus? status, CancellationToken cancellationToken = default) { - var query = _context.PaymentRecords + var query = context.PaymentRecords .AsNoTracking() .Where(x => x.TenantId == tenantId); @@ -84,28 +79,28 @@ public sealed class EfPaymentRepository : IPaymentRepository /// public Task SaveChangesAsync(CancellationToken cancellationToken = default) { - return _context.SaveChangesAsync(cancellationToken); + return context.SaveChangesAsync(cancellationToken); } /// public Task UpdatePaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default) { - _context.PaymentRecords.Update(payment); + context.PaymentRecords.Update(payment); return Task.CompletedTask; } /// public async Task DeletePaymentAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default) { - var refunds = await _context.PaymentRefundRecords + var refunds = await context.PaymentRefundRecords .Where(x => x.TenantId == tenantId && x.PaymentRecordId == paymentId) .ToListAsync(cancellationToken); if (refunds.Count > 0) { - _context.PaymentRefundRecords.RemoveRange(refunds); + context.PaymentRefundRecords.RemoveRange(refunds); } - var existing = await _context.PaymentRecords + var existing = await context.PaymentRecords .Where(x => x.TenantId == tenantId && x.Id == paymentId) .FirstOrDefaultAsync(cancellationToken); if (existing == null) @@ -113,6 +108,6 @@ public sealed class EfPaymentRepository : IPaymentRepository return; } - _context.PaymentRecords.Remove(existing); + context.PaymentRecords.Remove(existing); } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs index 234a367..65666bb 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs @@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// /// 商品聚合的 EF Core 仓储实现。 /// -public sealed class EfProductRepository : IProductRepository +/// +/// 初始化仓储。 +/// +public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductRepository { - private readonly TakeoutAppDbContext _context; - /// - /// 初始化仓储。 - /// - public EfProductRepository(TakeoutAppDbContext context) - { - _context = context; - } /// public Task FindByIdAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - return _context.Products + return context.Products .AsNoTracking() .Where(x => x.TenantId == tenantId && x.Id == productId) .FirstOrDefaultAsync(cancellationToken); @@ -34,7 +29,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> SearchAsync(long tenantId, long? categoryId, ProductStatus? status, CancellationToken cancellationToken = default) { - var query = _context.Products + var query = context.Products .AsNoTracking() .Where(x => x.TenantId == tenantId); @@ -58,7 +53,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetCategoriesAsync(long tenantId, CancellationToken cancellationToken = default) { - var categories = await _context.ProductCategories + var categories = await context.ProductCategories .AsNoTracking() .Where(x => x.TenantId == tenantId) .OrderBy(x => x.SortOrder) @@ -70,7 +65,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var skus = await _context.ProductSkus + var skus = await context.ProductSkus .AsNoTracking() .Where(x => x.TenantId == tenantId && x.ProductId == productId) .OrderBy(x => x.SortOrder) @@ -82,7 +77,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var groups = await _context.ProductAddonGroups + var groups = await context.ProductAddonGroups .AsNoTracking() .Where(x => x.TenantId == tenantId && x.ProductId == productId) .OrderBy(x => x.SortOrder) @@ -94,7 +89,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetAddonOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var groupIds = await _context.ProductAddonGroups + var groupIds = await context.ProductAddonGroups .AsNoTracking() .Where(x => x.TenantId == tenantId && x.ProductId == productId) .Select(x => x.Id) @@ -105,7 +100,7 @@ public sealed class EfProductRepository : IProductRepository return Array.Empty(); } - var options = await _context.ProductAddonOptions + var options = await context.ProductAddonOptions .AsNoTracking() .Where(x => x.TenantId == tenantId && groupIds.Contains(x.AddonGroupId)) .OrderBy(x => x.SortOrder) @@ -117,7 +112,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var groups = await _context.ProductAttributeGroups + var groups = await context.ProductAttributeGroups .AsNoTracking() .Where(x => x.TenantId == tenantId && x.ProductId == productId) .OrderBy(x => x.SortOrder) @@ -129,7 +124,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetAttributeOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var groupIds = await _context.ProductAttributeGroups + var groupIds = await context.ProductAttributeGroups .AsNoTracking() .Where(x => x.TenantId == tenantId && x.ProductId == productId) .Select(x => x.Id) @@ -140,7 +135,7 @@ public sealed class EfProductRepository : IProductRepository return Array.Empty(); } - var options = await _context.ProductAttributeOptions + var options = await context.ProductAttributeOptions .AsNoTracking() .Where(x => x.TenantId == tenantId && groupIds.Contains(x.AttributeGroupId)) .OrderBy(x => x.SortOrder) @@ -152,7 +147,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var assets = await _context.ProductMediaAssets + var assets = await context.ProductMediaAssets .AsNoTracking() .Where(x => x.TenantId == tenantId && x.ProductId == productId) .OrderBy(x => x.SortOrder) @@ -164,7 +159,7 @@ public sealed class EfProductRepository : IProductRepository /// public async Task> GetPricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var rules = await _context.ProductPricingRules + var rules = await context.ProductPricingRules .AsNoTracking() .Where(x => x.TenantId == tenantId && x.ProductId == productId) .OrderBy(x => x.SortOrder) @@ -176,59 +171,59 @@ public sealed class EfProductRepository : IProductRepository /// public Task AddCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default) { - return _context.ProductCategories.AddAsync(category, cancellationToken).AsTask(); + return context.ProductCategories.AddAsync(category, cancellationToken).AsTask(); } /// public Task AddProductAsync(Product product, CancellationToken cancellationToken = default) { - return _context.Products.AddAsync(product, cancellationToken).AsTask(); + return context.Products.AddAsync(product, cancellationToken).AsTask(); } /// public Task AddSkusAsync(IEnumerable skus, CancellationToken cancellationToken = default) { - return _context.ProductSkus.AddRangeAsync(skus, cancellationToken); + return context.ProductSkus.AddRangeAsync(skus, cancellationToken); } /// public Task AddAddonGroupsAsync(IEnumerable groups, IEnumerable options, CancellationToken cancellationToken = default) { - var addGroupsTask = _context.ProductAddonGroups.AddRangeAsync(groups, cancellationToken); - var addOptionsTask = _context.ProductAddonOptions.AddRangeAsync(options, cancellationToken); + var addGroupsTask = context.ProductAddonGroups.AddRangeAsync(groups, cancellationToken); + var addOptionsTask = context.ProductAddonOptions.AddRangeAsync(options, cancellationToken); return Task.WhenAll(addGroupsTask, addOptionsTask); } /// public Task AddAttributeGroupsAsync(IEnumerable groups, IEnumerable options, CancellationToken cancellationToken = default) { - var addGroupsTask = _context.ProductAttributeGroups.AddRangeAsync(groups, cancellationToken); - var addOptionsTask = _context.ProductAttributeOptions.AddRangeAsync(options, cancellationToken); + var addGroupsTask = context.ProductAttributeGroups.AddRangeAsync(groups, cancellationToken); + var addOptionsTask = context.ProductAttributeOptions.AddRangeAsync(options, cancellationToken); return Task.WhenAll(addGroupsTask, addOptionsTask); } /// public Task AddMediaAssetsAsync(IEnumerable assets, CancellationToken cancellationToken = default) { - return _context.ProductMediaAssets.AddRangeAsync(assets, cancellationToken); + return context.ProductMediaAssets.AddRangeAsync(assets, cancellationToken); } /// public Task AddPricingRulesAsync(IEnumerable rules, CancellationToken cancellationToken = default) { - return _context.ProductPricingRules.AddRangeAsync(rules, cancellationToken); + return context.ProductPricingRules.AddRangeAsync(rules, cancellationToken); } /// public Task SaveChangesAsync(CancellationToken cancellationToken = default) { - return _context.SaveChangesAsync(cancellationToken); + return context.SaveChangesAsync(cancellationToken); } /// public Task UpdateProductAsync(Product product, CancellationToken cancellationToken = default) { - _context.Products.Update(product); + context.Products.Update(product); return Task.CompletedTask; } @@ -241,7 +236,7 @@ public sealed class EfProductRepository : IProductRepository await RemoveAddonGroupsAsync(productId, tenantId, cancellationToken); await RemoveSkusAsync(productId, tenantId, cancellationToken); - var existing = await _context.Products + var existing = await context.Products .Where(x => x.TenantId == tenantId && x.Id == productId) .FirstOrDefaultAsync(cancellationToken); @@ -250,20 +245,20 @@ public sealed class EfProductRepository : IProductRepository return; } - _context.Products.Remove(existing); + context.Products.Remove(existing); } /// public Task UpdateCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default) { - _context.ProductCategories.Update(category); + context.ProductCategories.Update(category); return Task.CompletedTask; } /// public async Task DeleteCategoryAsync(long categoryId, long tenantId, CancellationToken cancellationToken = default) { - var existing = await _context.ProductCategories + var existing = await context.ProductCategories .Where(x => x.TenantId == tenantId && x.Id == categoryId) .FirstOrDefaultAsync(cancellationToken); @@ -272,13 +267,13 @@ public sealed class EfProductRepository : IProductRepository return; } - _context.ProductCategories.Remove(existing); + context.ProductCategories.Remove(existing); } /// public async Task RemoveSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var skus = await _context.ProductSkus + var skus = await context.ProductSkus .Where(x => x.TenantId == tenantId && x.ProductId == productId) .ToListAsync(cancellationToken); @@ -287,13 +282,13 @@ public sealed class EfProductRepository : IProductRepository return; } - _context.ProductSkus.RemoveRange(skus); + context.ProductSkus.RemoveRange(skus); } /// public async Task RemoveAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var groupIds = await _context.ProductAddonGroups + var groupIds = await context.ProductAddonGroups .Where(x => x.TenantId == tenantId && x.ProductId == productId) .Select(x => x.Id) .ToListAsync(cancellationToken); @@ -303,29 +298,29 @@ public sealed class EfProductRepository : IProductRepository return; } - var options = await _context.ProductAddonOptions + var options = await context.ProductAddonOptions .Where(x => x.TenantId == tenantId && groupIds.Contains(x.AddonGroupId)) .ToListAsync(cancellationToken); if (options.Count > 0) { - _context.ProductAddonOptions.RemoveRange(options); + context.ProductAddonOptions.RemoveRange(options); } - var groups = await _context.ProductAddonGroups + var groups = await context.ProductAddonGroups .Where(x => groupIds.Contains(x.Id)) .ToListAsync(cancellationToken); if (groups.Count > 0) { - _context.ProductAddonGroups.RemoveRange(groups); + context.ProductAddonGroups.RemoveRange(groups); } } /// public async Task RemoveAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var groupIds = await _context.ProductAttributeGroups + var groupIds = await context.ProductAttributeGroups .Where(x => x.TenantId == tenantId && x.ProductId == productId) .Select(x => x.Id) .ToListAsync(cancellationToken); @@ -335,29 +330,29 @@ public sealed class EfProductRepository : IProductRepository return; } - var options = await _context.ProductAttributeOptions + var options = await context.ProductAttributeOptions .Where(x => x.TenantId == tenantId && groupIds.Contains(x.AttributeGroupId)) .ToListAsync(cancellationToken); if (options.Count > 0) { - _context.ProductAttributeOptions.RemoveRange(options); + context.ProductAttributeOptions.RemoveRange(options); } - var groups = await _context.ProductAttributeGroups + var groups = await context.ProductAttributeGroups .Where(x => groupIds.Contains(x.Id)) .ToListAsync(cancellationToken); if (groups.Count > 0) { - _context.ProductAttributeGroups.RemoveRange(groups); + context.ProductAttributeGroups.RemoveRange(groups); } } /// public async Task RemoveMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var assets = await _context.ProductMediaAssets + var assets = await context.ProductMediaAssets .Where(x => x.TenantId == tenantId && x.ProductId == productId) .ToListAsync(cancellationToken); @@ -366,13 +361,13 @@ public sealed class EfProductRepository : IProductRepository return; } - _context.ProductMediaAssets.RemoveRange(assets); + context.ProductMediaAssets.RemoveRange(assets); } /// public async Task RemovePricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { - var rules = await _context.ProductPricingRules + var rules = await context.ProductPricingRules .Where(x => x.TenantId == tenantId && x.ProductId == productId) .ToListAsync(cancellationToken); @@ -381,6 +376,6 @@ public sealed class EfProductRepository : IProductRepository return; } - _context.ProductPricingRules.RemoveRange(rules); + context.ProductPricingRules.RemoveRange(rules); } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs index 4ed2412..3a0934a 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs @@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// /// 门店聚合的 EF Core 仓储实现。 /// -public sealed class EfStoreRepository : IStoreRepository +/// +/// 初始化仓储。 +/// +public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepository { - private readonly TakeoutAppDbContext _context; - /// - /// 初始化仓储。 - /// - public EfStoreRepository(TakeoutAppDbContext context) - { - _context = context; - } /// public Task FindByIdAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - return _context.Stores + return context.Stores .AsNoTracking() .Where(x => x.TenantId == tenantId && x.Id == storeId) .FirstOrDefaultAsync(cancellationToken); @@ -34,7 +29,7 @@ public sealed class EfStoreRepository : IStoreRepository /// public async Task> SearchAsync(long tenantId, StoreStatus? status, CancellationToken cancellationToken = default) { - var query = _context.Stores + var query = context.Stores .AsNoTracking() .Where(x => x.TenantId == tenantId); @@ -53,7 +48,7 @@ public sealed class EfStoreRepository : IStoreRepository /// public async Task> GetBusinessHoursAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var hours = await _context.StoreBusinessHours + var hours = await context.StoreBusinessHours .AsNoTracking() .Where(x => x.TenantId == tenantId && x.StoreId == storeId) .OrderBy(x => x.DayOfWeek) @@ -66,7 +61,7 @@ public sealed class EfStoreRepository : IStoreRepository /// public async Task> GetDeliveryZonesAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var zones = await _context.StoreDeliveryZones + var zones = await context.StoreDeliveryZones .AsNoTracking() .Where(x => x.TenantId == tenantId && x.StoreId == storeId) .OrderBy(x => x.SortOrder) @@ -78,7 +73,7 @@ public sealed class EfStoreRepository : IStoreRepository /// public async Task> GetHolidaysAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var holidays = await _context.StoreHolidays + var holidays = await context.StoreHolidays .AsNoTracking() .Where(x => x.TenantId == tenantId && x.StoreId == storeId) .OrderBy(x => x.Date) @@ -90,7 +85,7 @@ public sealed class EfStoreRepository : IStoreRepository /// public async Task> GetTableAreasAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var areas = await _context.StoreTableAreas + var areas = await context.StoreTableAreas .AsNoTracking() .Where(x => x.TenantId == tenantId && x.StoreId == storeId) .OrderBy(x => x.SortOrder) @@ -102,7 +97,7 @@ public sealed class EfStoreRepository : IStoreRepository /// public async Task> GetTablesAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var tables = await _context.StoreTables + var tables = await context.StoreTables .AsNoTracking() .Where(x => x.TenantId == tenantId && x.StoreId == storeId) .OrderBy(x => x.TableCode) @@ -114,7 +109,7 @@ public sealed class EfStoreRepository : IStoreRepository /// public async Task> GetShiftsAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var shifts = await _context.StoreEmployeeShifts + var shifts = await context.StoreEmployeeShifts .AsNoTracking() .Where(x => x.TenantId == tenantId && x.StoreId == storeId) .OrderBy(x => x.ShiftDate) @@ -127,62 +122,62 @@ public sealed class EfStoreRepository : IStoreRepository /// public Task AddStoreAsync(Store store, CancellationToken cancellationToken = default) { - return _context.Stores.AddAsync(store, cancellationToken).AsTask(); + return context.Stores.AddAsync(store, cancellationToken).AsTask(); } /// public Task AddBusinessHoursAsync(IEnumerable hours, CancellationToken cancellationToken = default) { - return _context.StoreBusinessHours.AddRangeAsync(hours, cancellationToken); + return context.StoreBusinessHours.AddRangeAsync(hours, cancellationToken); } /// public Task AddDeliveryZonesAsync(IEnumerable zones, CancellationToken cancellationToken = default) { - return _context.StoreDeliveryZones.AddRangeAsync(zones, cancellationToken); + return context.StoreDeliveryZones.AddRangeAsync(zones, cancellationToken); } /// public Task AddHolidaysAsync(IEnumerable holidays, CancellationToken cancellationToken = default) { - return _context.StoreHolidays.AddRangeAsync(holidays, cancellationToken); + return context.StoreHolidays.AddRangeAsync(holidays, cancellationToken); } /// public Task AddTableAreasAsync(IEnumerable areas, CancellationToken cancellationToken = default) { - return _context.StoreTableAreas.AddRangeAsync(areas, cancellationToken); + return context.StoreTableAreas.AddRangeAsync(areas, cancellationToken); } /// public Task AddTablesAsync(IEnumerable tables, CancellationToken cancellationToken = default) { - return _context.StoreTables.AddRangeAsync(tables, cancellationToken); + return context.StoreTables.AddRangeAsync(tables, cancellationToken); } /// public Task AddShiftsAsync(IEnumerable shifts, CancellationToken cancellationToken = default) { - return _context.StoreEmployeeShifts.AddRangeAsync(shifts, cancellationToken); + return context.StoreEmployeeShifts.AddRangeAsync(shifts, cancellationToken); } /// public Task SaveChangesAsync(CancellationToken cancellationToken = default) { - return _context.SaveChangesAsync(cancellationToken); + return context.SaveChangesAsync(cancellationToken); } /// public Task UpdateStoreAsync(Store store, CancellationToken cancellationToken = default) { - _context.Stores.Update(store); + context.Stores.Update(store); return Task.CompletedTask; } /// public async Task DeleteStoreAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var existing = await _context.Stores + var existing = await context.Stores .Where(x => x.TenantId == tenantId && x.Id == storeId) .FirstOrDefaultAsync(cancellationToken); @@ -191,6 +186,6 @@ public sealed class EfStoreRepository : IStoreRepository return; } - _context.Stores.Remove(existing); + context.Stores.Remove(existing); } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs index 358a7c7..232ad67 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs @@ -10,24 +10,18 @@ namespace TakeoutSaaS.Infrastructure.Dictionary.Repositories; /// /// EF Core 字典仓储实现。 /// -public sealed class EfDictionaryRepository : IDictionaryRepository +public sealed class EfDictionaryRepository(DictionaryDbContext context) : IDictionaryRepository { - private readonly DictionaryDbContext _context; - - public EfDictionaryRepository(DictionaryDbContext context) - { - _context = context; - } public Task FindGroupByIdAsync(long id, CancellationToken cancellationToken = default) - => _context.DictionaryGroups.FirstOrDefaultAsync(group => group.Id == id, cancellationToken); + => context.DictionaryGroups.FirstOrDefaultAsync(group => group.Id == id, cancellationToken); public Task FindGroupByCodeAsync(string code, CancellationToken cancellationToken = default) - => _context.DictionaryGroups.FirstOrDefaultAsync(group => group.Code == code, cancellationToken); + => context.DictionaryGroups.FirstOrDefaultAsync(group => group.Code == code, cancellationToken); public async Task> SearchGroupsAsync(DictionaryScope? scope, CancellationToken cancellationToken = default) { - var query = _context.DictionaryGroups.AsNoTracking(); + var query = context.DictionaryGroups.AsNoTracking(); if (scope.HasValue) { query = query.Where(group => group.Scope == scope.Value); @@ -40,22 +34,22 @@ public sealed class EfDictionaryRepository : IDictionaryRepository public Task AddGroupAsync(DictionaryGroup group, CancellationToken cancellationToken = default) { - _context.DictionaryGroups.Add(group); + context.DictionaryGroups.Add(group); return Task.CompletedTask; } public Task RemoveGroupAsync(DictionaryGroup group, CancellationToken cancellationToken = default) { - _context.DictionaryGroups.Remove(group); + context.DictionaryGroups.Remove(group); return Task.CompletedTask; } public Task FindItemByIdAsync(long id, CancellationToken cancellationToken = default) - => _context.DictionaryItems.FirstOrDefaultAsync(item => item.Id == id, cancellationToken); + => context.DictionaryItems.FirstOrDefaultAsync(item => item.Id == id, cancellationToken); public async Task> GetItemsByGroupIdAsync(long groupId, CancellationToken cancellationToken = default) { - return await _context.DictionaryItems + return await context.DictionaryItems .AsNoTracking() .Where(item => item.GroupId == groupId) .OrderBy(item => item.SortOrder) @@ -64,18 +58,18 @@ public sealed class EfDictionaryRepository : IDictionaryRepository public Task AddItemAsync(DictionaryItem item, CancellationToken cancellationToken = default) { - _context.DictionaryItems.Add(item); + context.DictionaryItems.Add(item); return Task.CompletedTask; } public Task RemoveItemAsync(DictionaryItem item, CancellationToken cancellationToken = default) { - _context.DictionaryItems.Remove(item); + context.DictionaryItems.Remove(item); return Task.CompletedTask; } public Task SaveChangesAsync(CancellationToken cancellationToken = default) - => _context.SaveChangesAsync(cancellationToken); + => context.SaveChangesAsync(cancellationToken); public async Task> GetItemsByCodesAsync(IEnumerable codes, long tenantId, bool includeSystem, CancellationToken cancellationToken = default) { @@ -90,7 +84,7 @@ public sealed class EfDictionaryRepository : IDictionaryRepository return Array.Empty(); } - var query = _context.DictionaryItems + var query = context.DictionaryItems .AsNoTracking() .IgnoreQueryFilters() .Include(item => item.Group) diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs index d2708ba..372c467 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs @@ -10,22 +10,15 @@ namespace TakeoutSaaS.Infrastructure.Dictionary.Services; /// /// 基于 IDistributedCache 的字典缓存实现。 /// -public sealed class DistributedDictionaryCache : IDictionaryCache +public sealed class DistributedDictionaryCache(IDistributedCache cache, IOptions options) : IDictionaryCache { - private readonly IDistributedCache _cache; - private readonly DictionaryCacheOptions _options; + private readonly DictionaryCacheOptions _options = options.Value; private readonly JsonSerializerOptions _serializerOptions = new(JsonSerializerDefaults.Web); - public DistributedDictionaryCache(IDistributedCache cache, IOptions options) - { - _cache = cache; - _options = options.Value; - } - public async Task?> GetAsync(long tenantId, string code, CancellationToken cancellationToken = default) { var cacheKey = BuildKey(tenantId, code); - var payload = await _cache.GetAsync(cacheKey, cancellationToken); + var payload = await cache.GetAsync(cacheKey, cancellationToken); if (payload == null || payload.Length == 0) { return null; @@ -42,13 +35,13 @@ public sealed class DistributedDictionaryCache : IDictionaryCache { SlidingExpiration = _options.SlidingExpiration }; - return _cache.SetAsync(cacheKey, payload, options, cancellationToken); + return cache.SetAsync(cacheKey, payload, options, cancellationToken); } public Task RemoveAsync(long tenantId, string code, CancellationToken cancellationToken = default) { var cacheKey = BuildKey(tenantId, code); - return _cache.RemoveAsync(cacheKey, cancellationToken); + return cache.RemoveAsync(cacheKey, cancellationToken); } private static string BuildKey(long tenantId, string code) diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs index f11a02d..a355874 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs @@ -10,18 +10,12 @@ namespace TakeoutSaaS.Infrastructure.Identity.Persistence; /// /// EF Core 后台用户仓储实现。 /// -public sealed class EfIdentityUserRepository : IIdentityUserRepository +public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIdentityUserRepository { - private readonly IdentityDbContext _dbContext; - - public EfIdentityUserRepository(IdentityDbContext dbContext) - { - _dbContext = dbContext; - } public Task FindByAccountAsync(string account, CancellationToken cancellationToken = default) - => _dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Account == account, cancellationToken); + => dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Account == account, cancellationToken); public Task FindByIdAsync(long userId, CancellationToken cancellationToken = default) - => _dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == userId, cancellationToken); + => dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == userId, cancellationToken); } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs index 9f843ca..3276793 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs @@ -10,24 +10,18 @@ namespace TakeoutSaaS.Infrastructure.Identity.Persistence; /// /// EF Core 小程序用户仓储实现。 /// -public sealed class EfMiniUserRepository : IMiniUserRepository +public sealed class EfMiniUserRepository(IdentityDbContext dbContext) : IMiniUserRepository { - private readonly IdentityDbContext _dbContext; - - public EfMiniUserRepository(IdentityDbContext dbContext) - { - _dbContext = dbContext; - } public Task FindByOpenIdAsync(string openId, CancellationToken cancellationToken = default) - => _dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken); + => dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken); public Task FindByIdAsync(long id, CancellationToken cancellationToken = default) - => _dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + => dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, cancellationToken); public async Task CreateOrUpdateAsync(string openId, string? unionId, string? nickname, string? avatar, long tenantId, CancellationToken cancellationToken = default) { - var user = await _dbContext.MiniUsers.FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken); + var user = await dbContext.MiniUsers.FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken); if (user == null) { user = new MiniUser @@ -39,7 +33,7 @@ public sealed class EfMiniUserRepository : IMiniUserRepository Avatar = avatar, TenantId = tenantId }; - _dbContext.MiniUsers.Add(user); + dbContext.MiniUsers.Add(user); } else { @@ -48,7 +42,7 @@ public sealed class EfMiniUserRepository : IMiniUserRepository user.Avatar = avatar ?? user.Avatar; } - await _dbContext.SaveChangesAsync(cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); return user; } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs index 9c7e339..e997c2e 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs @@ -13,21 +13,14 @@ namespace TakeoutSaaS.Infrastructure.Identity.Services; /// /// Redis 登录限流实现。 /// -public sealed class RedisLoginRateLimiter : ILoginRateLimiter +public sealed class RedisLoginRateLimiter(IDistributedCache cache, IOptions options) : ILoginRateLimiter { - private readonly IDistributedCache _cache; - private readonly LoginRateLimitOptions _options; - - public RedisLoginRateLimiter(IDistributedCache cache, IOptions options) - { - _cache = cache; - _options = options.Value; - } + private readonly LoginRateLimitOptions _options = options.Value; public async Task EnsureAllowedAsync(string key, CancellationToken cancellationToken = default) { var cacheKey = BuildKey(key); - var current = await _cache.GetStringAsync(cacheKey, cancellationToken); + var current = await cache.GetStringAsync(cacheKey, cancellationToken); var count = string.IsNullOrWhiteSpace(current) ? 0 : int.Parse(current); if (count >= _options.MaxAttempts) { @@ -35,7 +28,7 @@ public sealed class RedisLoginRateLimiter : ILoginRateLimiter } count++; - await _cache.SetStringAsync( + await cache.SetStringAsync( cacheKey, count.ToString(), new DistributedCacheEntryOptions @@ -46,7 +39,7 @@ public sealed class RedisLoginRateLimiter : ILoginRateLimiter } public Task ResetAsync(string key, CancellationToken cancellationToken = default) - => _cache.RemoveAsync(BuildKey(key), cancellationToken); + => cache.RemoveAsync(BuildKey(key), cancellationToken); private static string BuildKey(string key) => $"identity:login:{key}"; } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs index 3a670a9..36105e9 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs @@ -14,17 +14,10 @@ namespace TakeoutSaaS.Infrastructure.Identity.Services; /// /// Redis 刷新令牌存储。 /// -public sealed class RedisRefreshTokenStore : IRefreshTokenStore +public sealed class RedisRefreshTokenStore(IDistributedCache cache, IOptions options) : IRefreshTokenStore { private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web); - private readonly IDistributedCache _cache; - private readonly RefreshTokenStoreOptions _options; - - public RedisRefreshTokenStore(IDistributedCache cache, IOptions options) - { - _cache = cache; - _options = options.Value; - } + private readonly RefreshTokenStoreOptions _options = options.Value; public async Task IssueAsync(long userId, DateTime expiresAt, CancellationToken cancellationToken = default) { @@ -33,14 +26,14 @@ public sealed class RedisRefreshTokenStore : IRefreshTokenStore var key = BuildKey(token); var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = expiresAt }; - await _cache.SetStringAsync(key, JsonSerializer.Serialize(descriptor, JsonOptions), entryOptions, cancellationToken); + await cache.SetStringAsync(key, JsonSerializer.Serialize(descriptor, JsonOptions), entryOptions, cancellationToken); return descriptor; } public async Task GetAsync(string refreshToken, CancellationToken cancellationToken = default) { - var json = await _cache.GetStringAsync(BuildKey(refreshToken), cancellationToken); + var json = await cache.GetStringAsync(BuildKey(refreshToken), cancellationToken); return string.IsNullOrWhiteSpace(json) ? null : JsonSerializer.Deserialize(json, JsonOptions); @@ -56,7 +49,7 @@ public sealed class RedisRefreshTokenStore : IRefreshTokenStore var updated = descriptor with { Revoked = true }; var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = updated.ExpiresAt }; - await _cache.SetStringAsync(BuildKey(refreshToken), JsonSerializer.Serialize(updated, JsonOptions), entryOptions, cancellationToken); + await cache.SetStringAsync(BuildKey(refreshToken), JsonSerializer.Serialize(updated, JsonOptions), entryOptions, cancellationToken); } private string BuildKey(string token) => $"{_options.Prefix}{token}"; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs index 6fa0efb..1bd8b1b 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs @@ -15,21 +15,14 @@ namespace TakeoutSaaS.Infrastructure.Identity.Services; /// /// 微信 code2Session 实现 /// -public sealed class WeChatAuthService : IWeChatAuthService +public sealed class WeChatAuthService(HttpClient httpClient, IOptions options) : IWeChatAuthService { - private readonly HttpClient _httpClient; - private readonly WeChatMiniOptions _options; - - public WeChatAuthService(HttpClient httpClient, IOptions options) - { - _httpClient = httpClient; - _options = options.Value; - } + private readonly WeChatMiniOptions _options = options.Value; public async Task Code2SessionAsync(string code, CancellationToken cancellationToken = default) { var requestUri = $"sns/jscode2session?appid={Uri.EscapeDataString(_options.AppId)}&secret={Uri.EscapeDataString(_options.Secret)}&js_code={Uri.EscapeDataString(code)}&grant_type=authorization_code"; - using var response = await _httpClient.GetAsync(requestUri, cancellationToken); + using var response = await httpClient.GetAsync(requestUri, cancellationToken); response.EnsureSuccessStatusCode(); var payload = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs index 2d8df5b..80d6a81 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs @@ -3,40 +3,34 @@ namespace TakeoutSaaS.Module.Storage.Models; /// /// 直传(预签名上传)请求参数。 /// -public sealed class StorageDirectUploadRequest +/// +/// 初始化请求。 +/// +/// 对象键。 +/// 内容类型。 +/// 内容长度。 +/// 签名有效期。 +public sealed class StorageDirectUploadRequest(string objectKey, string contentType, long contentLength, TimeSpan expires) { - /// - /// 初始化请求。 - /// - /// 对象键。 - /// 内容类型。 - /// 内容长度。 - /// 签名有效期。 - public StorageDirectUploadRequest(string objectKey, string contentType, long contentLength, TimeSpan expires) - { - ObjectKey = objectKey; - ContentType = contentType; - ContentLength = contentLength; - Expires = expires; - } + /// /// 目标对象键。 /// - public string ObjectKey { get; } + public string ObjectKey { get; } = objectKey; /// /// 内容类型。 /// - public string ContentType { get; } + public string ContentType { get; } = contentType; /// /// 内容长度。 /// - public long ContentLength { get; } + public long ContentLength { get; } = contentLength; /// /// 签名有效期。 /// - public TimeSpan Expires { get; } + public TimeSpan Expires { get; } = expires; } diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs index 80a2644..3d055ab 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs @@ -6,70 +6,60 @@ namespace TakeoutSaaS.Module.Storage.Models; /// /// 对象存储上传请求参数。 /// -public sealed class StorageUploadRequest +/// +/// 初始化上传请求。 +/// +/// 对象键(含路径)。 +/// 文件流。 +/// 内容类型。 +/// 内容长度。 +/// 是否返回签名访问链接。 +/// 签名有效期。 +/// 附加元数据。 +public sealed class StorageUploadRequest( + string objectKey, + Stream content, + string contentType, + long contentLength, + bool generateSignedUrl, + TimeSpan signedUrlExpires, + IDictionary? metadata = null) { - /// - /// 初始化上传请求。 - /// - /// 对象键(含路径)。 - /// 文件流。 - /// 内容类型。 - /// 内容长度。 - /// 是否返回签名访问链接。 - /// 签名有效期。 - /// 附加元数据。 - public StorageUploadRequest( - string objectKey, - Stream content, - string contentType, - long contentLength, - bool generateSignedUrl, - TimeSpan signedUrlExpires, - IDictionary? metadata = null) - { - ObjectKey = objectKey; - Content = content; - ContentType = contentType; - ContentLength = contentLength; - GenerateSignedUrl = generateSignedUrl; - SignedUrlExpires = signedUrlExpires; - Metadata = metadata == null - ? new Dictionary() - : new Dictionary(metadata); - } /// /// 对象键。 /// - public string ObjectKey { get; } + public string ObjectKey { get; } = objectKey; /// /// 文件流。 /// - public Stream Content { get; } + public Stream Content { get; } = content; /// /// 内容类型。 /// - public string ContentType { get; } + public string ContentType { get; } = contentType; /// /// 内容长度。 /// - public long ContentLength { get; } + public long ContentLength { get; } = contentLength; /// /// 是否需要签名访问链接。 /// - public bool GenerateSignedUrl { get; } + public bool GenerateSignedUrl { get; } = generateSignedUrl; /// /// 签名有效期。 /// - public TimeSpan SignedUrlExpires { get; } + public TimeSpan SignedUrlExpires { get; } = signedUrlExpires; /// /// 元数据集合。 /// - public IReadOnlyDictionary Metadata { get; } + public IReadOnlyDictionary Metadata { get; } = metadata == null + ? new Dictionary() + : new Dictionary(metadata); } diff --git a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs index 7faff05..069202d 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs @@ -5,20 +5,15 @@ namespace TakeoutSaaS.Module.Tenancy; /// /// 默认租户提供者:基于租户上下文访问器暴露当前租户 ID。 /// -public sealed class TenantProvider : ITenantProvider +/// +/// 初始化租户提供者。 +/// +/// 租户上下文访问器 +public sealed class TenantProvider(ITenantContextAccessor tenantContextAccessor) : ITenantProvider { - private readonly ITenantContextAccessor _tenantContextAccessor; - /// - /// 初始化租户提供者。 - /// - /// 租户上下文访问器 - public TenantProvider(ITenantContextAccessor tenantContextAccessor) - { - _tenantContextAccessor = tenantContextAccessor; - } /// public long GetCurrentTenantId() - => _tenantContextAccessor.Current?.TenantId ?? 0; + => tenantContextAccessor.Current?.TenantId ?? 0; } diff --git a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs index a1066c1..59247c8 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs @@ -11,54 +11,43 @@ namespace TakeoutSaaS.Module.Tenancy; /// /// 多租户解析中间件:支持 Header、域名与 Token Claim 的优先级解析。 /// -public sealed class TenantResolutionMiddleware +/// +/// 初始化中间件。 +/// +public sealed class TenantResolutionMiddleware( + RequestDelegate next, + ILogger logger, + ITenantContextAccessor tenantContextAccessor, + IOptionsMonitor optionsMonitor) { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly ITenantContextAccessor _tenantContextAccessor; - private readonly IOptionsMonitor _optionsMonitor; - /// - /// 初始化中间件。 - /// - public TenantResolutionMiddleware( - RequestDelegate next, - ILogger logger, - ITenantContextAccessor tenantContextAccessor, - IOptionsMonitor optionsMonitor) - { - _next = next; - _logger = logger; - _tenantContextAccessor = tenantContextAccessor; - _optionsMonitor = optionsMonitor; - } /// /// 解析租户并将上下文注入请求。 /// public async Task InvokeAsync(HttpContext context) { - var options = _optionsMonitor.CurrentValue ?? new TenantResolutionOptions(); + var options = optionsMonitor.CurrentValue ?? new TenantResolutionOptions(); if (ShouldSkip(context.Request.Path, options)) { - await _next(context); + await next(context); return; } var tenantContext = ResolveTenant(context, options); - _tenantContextAccessor.Current = tenantContext; + tenantContextAccessor.Current = tenantContext; context.Items[TenantConstants.HttpContextItemKey] = tenantContext; if (!tenantContext.IsResolved) { - _logger.LogDebug("未能解析租户:{Path}", context.Request.Path); + logger.LogDebug("未能解析租户:{Path}", context.Request.Path); if (options.ThrowIfUnresolved) { var response = ApiResponse.Error(ErrorCodes.BadRequest, "缺少租户标识"); context.Response.StatusCode = StatusCodes.Status400BadRequest; await context.Response.WriteAsJsonAsync(response, cancellationToken: context.RequestAborted); - _tenantContextAccessor.Current = null; + tenantContextAccessor.Current = null; context.Items.Remove(TenantConstants.HttpContextItemKey); return; } @@ -66,11 +55,11 @@ public sealed class TenantResolutionMiddleware try { - await _next(context); + await next(context); } finally { - _tenantContextAccessor.Current = null; + tenantContextAccessor.Current = null; context.Items.Remove(TenantConstants.HttpContextItemKey); } }