From 8e4c2b0e45bcd8f2fb50e7a42ad4ca56ab55ae2e Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Thu, 4 Dec 2025 11:25:01 +0800 Subject: [PATCH] chore: add documentation comments and stylecop rules --- .editorconfig | 6 ++ Directory.Build.props | 3 + .../Controllers/AuthController.cs | 11 ++- .../Controllers/DeliveriesController.cs | 21 ++++-- .../Controllers/DictionaryController.cs | 29 ++++++-- .../Controllers/FilesController.cs | 9 ++- .../Controllers/HealthController.cs | 3 + .../MerchantCategoriesController.cs | 12 ++++ .../Controllers/MerchantsController.cs | 51 ++++++++++++-- .../Controllers/OrdersController.cs | 21 ++++-- .../Controllers/PaymentsController.cs | 21 ++++-- .../Controllers/PermissionsController.cs | 14 ++++ .../Controllers/ProductsController.cs | 21 ++++-- .../Controllers/RolesController.cs | 40 +++++++++++ .../Controllers/StoresController.cs | 21 ++++-- .../Controllers/SystemParametersController.cs | 15 +++++ .../TenantAnnouncementsController.cs | 22 ++++++ .../Controllers/TenantBillingsController.cs | 16 +++++ .../TenantNotificationsController.cs | 8 +++ .../Controllers/TenantPackagesController.cs | 17 +++++ .../Controllers/TenantsController.cs | 33 +++++++++ .../Controllers/UserPermissionsController.cs | 2 + src/Api/TakeoutSaaS.AdminApi/Program.cs | 19 ++++-- .../Controllers/AuthController.cs | 14 ++-- .../Controllers/FilesController.cs | 9 ++- .../Controllers/HealthController.cs | 3 + .../Controllers/MeController.cs | 10 ++- src/Api/TakeoutSaaS.MiniApi/Program.cs | 11 +++ .../Controllers/HealthController.cs | 3 + src/Api/TakeoutSaaS.UserApi/Program.cs | 11 +++ .../CreateDeliveryOrderCommandHandler.cs | 14 ++-- .../DeleteDeliveryOrderCommandHandler.cs | 18 ++--- .../GetDeliveryOrderByIdQueryHandler.cs | 15 +++-- .../SearchDeliveryOrdersQueryHandler.cs | 15 +++-- .../UpdateDeliveryOrderCommandHandler.cs | 24 ++++--- .../AddMerchantDocumentCommandHandler.cs | 25 ++++--- .../CreateMerchantCategoryCommandHandler.cs | 18 ++--- .../Handlers/CreateMerchantCommandHandler.cs | 12 ++-- .../CreateMerchantContractCommandHandler.cs | 27 ++++---- .../DeleteMerchantCategoryCommandHandler.cs | 13 ++-- .../Handlers/DeleteMerchantCommandHandler.cs | 14 ++-- .../GetMerchantAuditLogsQueryHandler.cs | 11 +-- .../Handlers/GetMerchantByIdQueryHandler.cs | 9 ++- .../GetMerchantCategoriesQueryHandler.cs | 9 ++- .../GetMerchantContractsQueryHandler.cs | 11 ++- .../Handlers/GetMerchantDetailQueryHandler.cs | 14 ++-- .../GetMerchantDocumentsQueryHandler.cs | 11 ++- .../ListMerchantCategoriesQueryHandler.cs | 10 +-- ...ReorderMerchantCategoriesCommandHandler.cs | 14 ++-- .../Handlers/ReviewMerchantCommandHandler.cs | 23 ++++--- .../ReviewMerchantDocumentCommandHandler.cs | 23 ++++--- .../Handlers/SearchMerchantsQueryHandler.cs | 11 +-- .../Handlers/UpdateMerchantCommandHandler.cs | 14 ++-- ...ateMerchantContractStatusCommandHandler.cs | 23 ++++--- .../Handlers/CreateOrderCommandHandler.cs | 18 +++-- .../Handlers/DeleteOrderCommandHandler.cs | 17 +++-- .../Handlers/GetOrderByIdQueryHandler.cs | 18 ++--- .../Handlers/SearchOrdersQueryHandler.cs | 13 ++-- .../Handlers/UpdateOrderCommandHandler.cs | 24 +++---- ...ngeTenantSubscriptionPlanCommandHandler.cs | 22 +++--- .../CheckTenantQuotaCommandHandler.cs | 8 ++- .../CreateTenantAnnouncementCommandHandler.cs | 3 + .../CreateTenantBillingCommandHandler.cs | 4 ++ .../CreateTenantPackageCommandHandler.cs | 3 + .../CreateTenantSubscriptionCommandHandler.cs | 26 ++++--- .../DeleteTenantAnnouncementCommandHandler.cs | 3 + .../DeleteTenantPackageCommandHandler.cs | 3 + .../GetTenantAnnouncementQueryHandler.cs | 3 + .../GetTenantAuditLogsQueryHandler.cs | 7 +- .../Handlers/GetTenantBillQueryHandler.cs | 3 + .../Handlers/GetTenantByIdQueryHandler.cs | 11 +-- .../GetTenantPackageByIdQueryHandler.cs | 3 + ...arkTenantAnnouncementReadCommandHandler.cs | 4 ++ .../MarkTenantBillingPaidCommandHandler.cs | 4 ++ ...arkTenantNotificationReadCommandHandler.cs | 3 + .../Handlers/RegisterTenantCommandHandler.cs | 28 ++++---- .../Handlers/ReviewTenantCommandHandler.cs | 34 +++++----- .../SearchTenantAnnouncementsQueryHandler.cs | 7 ++ .../Handlers/SearchTenantBillsQueryHandler.cs | 3 + .../SearchTenantNotificationsQueryHandler.cs | 3 + .../SearchTenantPackagesQueryHandler.cs | 3 + .../Handlers/SearchTenantsQueryHandler.cs | 12 ++-- .../SubmitTenantVerificationCommandHandler.cs | 20 +++--- .../UpdateTenantAnnouncementCommandHandler.cs | 5 ++ .../UpdateTenantPackageCommandHandler.cs | 4 ++ .../Services/DictionaryAppService.cs | 27 +++++++- .../Handlers/AssignUserRolesCommandHandler.cs | 5 ++ .../BindRolePermissionsCommandHandler.cs | 5 ++ .../CopyRoleTemplateCommandHandler.cs | 6 +- .../CreatePermissionCommandHandler.cs | 5 ++ .../Handlers/CreateRoleCommandHandler.cs | 5 ++ .../CreateRoleTemplateCommandHandler.cs | 5 ++ .../DeletePermissionCommandHandler.cs | 5 ++ .../Handlers/DeleteRoleCommandHandler.cs | 5 ++ .../DeleteRoleTemplateCommandHandler.cs | 4 ++ .../Handlers/GetRoleTemplateQueryHandler.cs | 4 ++ .../GetUserPermissionsQueryHandler.cs | 29 ++++---- .../Handlers/ListRoleTemplatesQueryHandler.cs | 3 + .../Handlers/SearchPermissionsQueryHandler.cs | 5 ++ .../Handlers/SearchRolesQueryHandler.cs | 5 ++ .../SearchUserPermissionsQueryHandler.cs | 27 ++++---- .../UpdatePermissionCommandHandler.cs | 4 ++ .../Handlers/UpdateRoleCommandHandler.cs | 4 ++ .../Contracts/SendVerificationCodeRequest.cs | 2 - .../VerifyVerificationCodeRequest.cs | 2 - .../Sms/Services/VerificationCodeService.cs | 8 +++ .../Storage/Contracts/DirectUploadRequest.cs | 2 - .../Storage/Contracts/UploadFileRequest.cs | 2 - .../Storage/Services/FileStorageService.cs | 10 +++ .../Constants/DatabaseConstants.cs | 2 +- .../Constants/ErrorCodes.cs | 32 ++++++++- .../Results/ApiResponse.cs | 21 +++++- .../Middleware/RequestLoggingMiddleware.cs | 1 - .../HttpContextCurrentUserAccessor.cs | 2 - .../Deliveries/Entities/DeliveryOrder.cs | 3 + .../Repositories/IPermissionRepository.cs | 67 +++++++++++++++++++ .../Repositories/IRolePermissionRepository.cs | 29 ++++++++ .../Identity/Repositories/IRoleRepository.cs | 59 ++++++++++++++++ .../Repositories/IRoleTemplateRepository.cs | 49 ++++++++++++++ .../Repositories/IUserRoleRepository.cs | 30 +++++++++ .../Repositories/IMerchantRepository.cs | 32 +++++++++ .../Queues/Entities/QueueTicket.cs | 3 + src/Gateway/TakeoutSaaS.ApiGateway/Program.cs | 14 +++- .../App/Repositories/EfDeliveryRepository.cs | 2 - .../App/Repositories/EfMerchantRepository.cs | 2 - .../App/Repositories/EfOrderRepository.cs | 2 - .../App/Repositories/EfPaymentRepository.cs | 2 - .../App/Repositories/EfProductRepository.cs | 2 - .../App/Repositories/EfStoreRepository.cs | 2 - .../Repositories/EfDictionaryRepository.cs | 1 - .../Persistence/EfIdentityUserRepository.cs | 1 - .../Persistence/EfMiniUserRepository.cs | 1 - .../Services/RabbitMqMessagePublisher.cs | 4 ++ .../Services/RabbitMqMessageSubscriber.cs | 4 ++ .../Services/AliyunSmsSender.cs | 2 - .../Services/TencentSmsSender.cs | 5 ++ .../Models/StorageDirectUploadRequest.cs | 2 - .../Models/StorageUploadRequest.cs | 1 - .../Providers/AliyunOssStorageProvider.cs | 8 +++ .../Providers/S3StorageProviderBase.cs | 7 ++ .../TenantProvider.cs | 2 - .../TenantResolutionMiddleware.cs | 2 - 142 files changed, 1309 insertions(+), 439 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..32193b8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +# EditorConfig +root = true + +[*.cs] +dotnet_diagnostic.SA1600.severity = error +dotnet_diagnostic.SA1601.severity = error diff --git a/Directory.Build.props b/Directory.Build.props index 59c4f40..f83743e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,5 +6,8 @@ latest false + + + diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs index 72f23f4..b38d018 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs @@ -13,21 +13,16 @@ using TakeoutSaaS.Shared.Web.Api; using TakeoutSaaS.Shared.Web.Security; namespace TakeoutSaaS.AdminApi.Controllers; - /// /// 管理后台认证接口 /// -/// -/// -/// -/// +/// 提供登录、刷新 Token 以及用户权限查询能力。 +/// 认证服务 [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/auth")] public sealed class AuthController(IAdminAuthService authService) : BaseApiController { - - /// /// 登录获取 Token /// @@ -84,12 +79,14 @@ public sealed class AuthController(IAdminAuthService authService) : BaseApiContr [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status401Unauthorized)] public async Task> GetProfile(CancellationToken cancellationToken) { + // 1. 从 JWT 中获取当前用户标识 var userId = User.GetUserId(); if (userId == 0) { return ApiResponse.Error(ErrorCodes.Unauthorized, "Token 缺少有效的用户标识"); } + // 2. 读取用户档案并返回 var profile = await authService.GetProfileAsync(userId, cancellationToken); return ApiResponse.Ok(profile); } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs index fe5d2cf..ae7c092 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs @@ -16,16 +16,11 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 配送单管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/deliveries")] public sealed class DeliveriesController(IMediator mediator) : BaseApiController { - - /// /// 创建配送单。 /// @@ -34,7 +29,10 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateDeliveryOrderCommand command, CancellationToken cancellationToken) { + // 1. 创建配送单 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -53,6 +51,7 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { + // 1. 组装查询参数 var result = await mediator.Send(new SearchDeliveryOrdersQuery { OrderId = orderId, @@ -63,6 +62,7 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } @@ -75,7 +75,10 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long deliveryOrderId, CancellationToken cancellationToken) { + // 1. 查询配送单详情 var result = await mediator.Send(new GetDeliveryOrderByIdQuery { DeliveryOrderId = deliveryOrderId }, cancellationToken); + + // 2. 返回详情或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "配送单不存在") : ApiResponse.Ok(result); @@ -90,11 +93,16 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long deliveryOrderId, [FromBody] UpdateDeliveryOrderCommand command, CancellationToken cancellationToken) { + // 1. 确保命令携带配送单标识 if (command.DeliveryOrderId == 0) { command = command with { DeliveryOrderId = deliveryOrderId }; } + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "配送单不存在") : ApiResponse.Ok(result); @@ -109,7 +117,10 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long deliveryOrderId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeleteDeliveryOrderCommand { DeliveryOrderId = deliveryOrderId }, cancellationToken); + + // 2. 返回结果或 404 return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "配送单不存在"); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs index d98e420..28e0d61 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs @@ -12,17 +12,12 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 参数字典管理。 /// -/// -/// 初始化字典控制器。 -/// /// 字典服务 [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/dictionaries")] public sealed class DictionaryController(IDictionaryAppService dictionaryAppService) : BaseApiController { - - /// /// 查询字典分组。 /// @@ -31,7 +26,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> GetGroups([FromQuery] DictionaryGroupQuery query, CancellationToken cancellationToken) { + // 1. 查询字典分组 var groups = await dictionaryAppService.SearchGroupsAsync(query, cancellationToken); + + // 2. 返回分组列表 return ApiResponse>.Ok(groups); } @@ -43,7 +41,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> CreateGroup([FromBody] CreateDictionaryGroupRequest request, CancellationToken cancellationToken) { + // 1. 创建字典分组 var group = await dictionaryAppService.CreateGroupAsync(request, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(group); } @@ -55,7 +56,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> UpdateGroup(long groupId, [FromBody] UpdateDictionaryGroupRequest request, CancellationToken cancellationToken) { + // 1. 更新字典分组 var group = await dictionaryAppService.UpdateGroupAsync(groupId, request, cancellationToken); + + // 2. 返回更新结果 return ApiResponse.Ok(group); } @@ -67,7 +71,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> DeleteGroup(long groupId, CancellationToken cancellationToken) { + // 1. 删除字典分组 await dictionaryAppService.DeleteGroupAsync(groupId, cancellationToken); + + // 2. 返回成功响应 return ApiResponse.Success(); } @@ -79,7 +86,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> CreateItem(long groupId, [FromBody] CreateDictionaryItemRequest request, CancellationToken cancellationToken) { + // 1. 绑定分组标识 request.GroupId = groupId; + + // 2. 创建字典项 var item = await dictionaryAppService.CreateItemAsync(request, cancellationToken); return ApiResponse.Ok(item); } @@ -92,7 +102,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> UpdateItem(long itemId, [FromBody] UpdateDictionaryItemRequest request, CancellationToken cancellationToken) { + // 1. 更新字典项 var item = await dictionaryAppService.UpdateItemAsync(itemId, request, cancellationToken); + + // 2. 返回更新结果 return ApiResponse.Ok(item); } @@ -104,7 +117,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> DeleteItem(long itemId, CancellationToken cancellationToken) { + // 1. 删除字典项 await dictionaryAppService.DeleteItemAsync(itemId, cancellationToken); + + // 2. 返回成功响应 return ApiResponse.Success(); } @@ -115,7 +131,10 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ [ProducesResponseType(typeof(ApiResponse>>), StatusCodes.Status200OK)] public async Task>>> BatchGet([FromBody] DictionaryBatchQueryRequest request, CancellationToken cancellationToken) { + // 1. 批量读取并命中缓存 var dictionaries = await dictionaryAppService.GetCachedItemsAsync(request, cancellationToken); + + // 2. 返回批量结果 return ApiResponse>>.Ok(dictionaries); } } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/FilesController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/FilesController.cs index f53d344..34091de 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/FilesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/FilesController.cs @@ -19,8 +19,6 @@ namespace TakeoutSaaS.AdminApi.Controllers; [Route("api/admin/v{version:apiVersion}/files")] public sealed class FilesController(IFileStorageService fileStorageService) : BaseApiController { - private readonly IFileStorageService _fileStorageService = fileStorageService; - /// /// 上传图片或文件。 /// @@ -30,23 +28,28 @@ public sealed class FilesController(IFileStorageService fileStorageService) : Ba [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] public async Task> Upload([FromForm] IFormFile? file, [FromForm] string? type, CancellationToken cancellationToken) { + // 1. 校验文件有效性 if (file == null || file.Length == 0) { return ApiResponse.Error(ErrorCodes.BadRequest, "文件不能为空"); } + // 2. 解析上传类型 if (!UploadFileTypeParser.TryParse(type, out var uploadType)) { return ApiResponse.Error(ErrorCodes.BadRequest, "上传类型不合法"); } + // 3. 提取请求来源 var origin = Request.Headers["Origin"].FirstOrDefault() ?? Request.Headers["Referer"].FirstOrDefault(); await using var stream = file.OpenReadStream(); - var result = await _fileStorageService.UploadAsync( + // 4. 调用存储服务执行上传 + var result = await fileStorageService.UploadAsync( new UploadFileRequest(uploadType, stream, file.FileName, file.ContentType ?? string.Empty, file.Length, origin), cancellationToken); + // 5. 返回上传结果 return ApiResponse.Ok(result); } } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/HealthController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/HealthController.cs index 4db5f17..25edb1b 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/HealthController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/HealthController.cs @@ -23,7 +23,10 @@ public class HealthController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public ApiResponse Get() { + // 1. 构造健康状态 var payload = new { status = "OK", service = "AdminApi", time = DateTime.UtcNow }; + + // 2. 返回健康响应 return ApiResponse.Ok(payload); } } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantCategoriesController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantCategoriesController.cs index 72684f0..8a930be 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantCategoriesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantCategoriesController.cs @@ -29,7 +29,10 @@ public sealed class MerchantCategoriesController(IMediator mediator) : BaseApiCo [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> List(CancellationToken cancellationToken) { + // 1. 查询所有类目 var result = await mediator.Send(new ListMerchantCategoriesQuery(), cancellationToken); + + // 2. 返回类目列表 return ApiResponse>.Ok(result); } @@ -41,7 +44,10 @@ public sealed class MerchantCategoriesController(IMediator mediator) : BaseApiCo [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateMerchantCategoryCommand command, CancellationToken cancellationToken) { + // 1. 创建类目 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -54,7 +60,10 @@ public sealed class MerchantCategoriesController(IMediator mediator) : BaseApiCo [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long categoryId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeleteMerchantCategoryCommand(categoryId), cancellationToken); + + // 2. 返回删除结果或 404 return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "类目不存在"); @@ -68,7 +77,10 @@ public sealed class MerchantCategoriesController(IMediator mediator) : BaseApiCo [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Reorder([FromBody] ReorderMerchantCategoriesCommand command, CancellationToken cancellationToken) { + // 1. 执行排序调整 await mediator.Send(command, cancellationToken); + + // 2. 返回成功结果 return ApiResponse.Ok(null); } } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantsController.cs index d63bf56..828d98b 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantsController.cs @@ -16,16 +16,11 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 商户管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/merchants")] public sealed class MerchantsController(IMediator mediator) : BaseApiController { - - /// /// 创建商户。 /// @@ -34,7 +29,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateMerchantCommand command, CancellationToken cancellationToken) { + // 1. 创建商户 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -52,6 +50,7 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { + // 1. 组装查询参数并执行查询 var result = await mediator.Send(new SearchMerchantsQuery { Status = status, @@ -60,6 +59,8 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController SortBy = sortBy, SortDescending = sortDesc }, cancellationToken); + + // 2. 返回分页结果 return ApiResponse>.Ok(result); } @@ -72,12 +73,16 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long merchantId, [FromBody] UpdateMerchantCommand command, CancellationToken cancellationToken) { + // 1. 绑定商户标识 if (command.MerchantId == 0) { command = command with { MerchantId = merchantId }; } + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商户不存在") : ApiResponse.Ok(result); @@ -92,7 +97,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long merchantId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeleteMerchantCommand { MerchantId = merchantId }, cancellationToken); + + // 2. 返回删除结果或 404 return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "商户不存在"); @@ -107,7 +115,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long merchantId, CancellationToken cancellationToken) { + // 1. 查询商户概览 var result = await mediator.Send(new GetMerchantByIdQuery { MerchantId = merchantId }, cancellationToken); + + // 2. 返回结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商户不存在") : ApiResponse.Ok(result); @@ -121,7 +132,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> FullDetail(long merchantId, CancellationToken cancellationToken) { + // 1. 查询商户详细资料 var result = await mediator.Send(new GetMerchantDetailQuery(merchantId), cancellationToken); + + // 2. 返回详情 return ApiResponse.Ok(result); } @@ -136,7 +150,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [FromBody] AddMerchantDocumentCommand body, CancellationToken cancellationToken) { + // 1. 绑定商户标识 var command = body with { MerchantId = merchantId }; + + // 2. 创建证照记录 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -149,7 +166,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Documents(long merchantId, CancellationToken cancellationToken) { + // 1. 查询证照列表 var result = await mediator.Send(new GetMerchantDocumentsQuery(merchantId), cancellationToken); + + // 2. 返回证照集合 return ApiResponse>.Ok(result); } @@ -165,7 +185,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [FromBody] ReviewMerchantDocumentCommand body, CancellationToken cancellationToken) { + // 1. 绑定商户与证照标识 var command = body with { MerchantId = merchantId, DocumentId = documentId }; + + // 2. 执行审核 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -181,7 +204,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [FromBody] CreateMerchantContractCommand body, CancellationToken cancellationToken) { + // 1. 绑定商户标识 var command = body with { MerchantId = merchantId }; + + // 2. 创建合同 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -194,7 +220,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Contracts(long merchantId, CancellationToken cancellationToken) { + // 1. 查询合同列表 var result = await mediator.Send(new GetMerchantContractsQuery(merchantId), cancellationToken); + + // 2. 返回合同集合 return ApiResponse>.Ok(result); } @@ -210,7 +239,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [FromBody] UpdateMerchantContractStatusCommand body, CancellationToken cancellationToken) { + // 1. 绑定商户与合同标识 var command = body with { MerchantId = merchantId, ContractId = contractId }; + + // 2. 更新合同状态 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -223,7 +255,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Review(long merchantId, [FromBody] ReviewMerchantCommand body, CancellationToken cancellationToken) { + // 1. 绑定商户标识 var command = body with { MerchantId = merchantId }; + + // 2. 执行审核 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -240,7 +275,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default) { + // 1. 查询审核日志 var result = await mediator.Send(new GetMerchantAuditLogsQuery(merchantId, page, pageSize), cancellationToken); + + // 2. 返回日志分页 return ApiResponse>.Ok(result); } @@ -252,7 +290,10 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Categories(CancellationToken cancellationToken) { + // 1. 查询可选类目 var result = await mediator.Send(new GetMerchantCategoriesQuery(), cancellationToken); + + // 2. 返回类目列表 return ApiResponse>.Ok(result); } } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/OrdersController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/OrdersController.cs index 4d04390..85f69e4 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/OrdersController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/OrdersController.cs @@ -17,16 +17,11 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 订单管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/orders")] public sealed class OrdersController(IMediator mediator) : BaseApiController { - - /// /// 创建订单。 /// @@ -35,7 +30,10 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateOrderCommand command, CancellationToken cancellationToken) { + // 1. 创建订单 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -56,6 +54,7 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { + // 1. 组装查询参数并执行查询 var result = await mediator.Send(new SearchOrdersQuery { StoreId = storeId, @@ -68,6 +67,7 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } @@ -80,7 +80,10 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long orderId, CancellationToken cancellationToken) { + // 1. 查询订单详情 var result = await mediator.Send(new GetOrderByIdQuery { OrderId = orderId }, cancellationToken); + + // 2. 返回详情或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "订单不存在") : ApiResponse.Ok(result); @@ -95,11 +98,16 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long orderId, [FromBody] UpdateOrderCommand command, CancellationToken cancellationToken) { + // 1. 确保命令包含订单标识 if (command.OrderId == 0) { command = command with { OrderId = orderId }; } + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "订单不存在") : ApiResponse.Ok(result); @@ -114,7 +122,10 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long orderId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeleteOrderCommand { OrderId = orderId }, cancellationToken); + + // 2. 返回结果或 404 return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "订单不存在"); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/PaymentsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/PaymentsController.cs index de8f322..83df2fe 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/PaymentsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/PaymentsController.cs @@ -16,16 +16,11 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 支付记录管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/payments")] public sealed class PaymentsController(IMediator mediator) : BaseApiController { - - /// /// 创建支付记录。 /// @@ -34,7 +29,10 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreatePaymentCommand command, CancellationToken cancellationToken) { + // 1. 创建支付记录 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -53,6 +51,7 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { + // 1. 组装查询参数并执行查询 var result = await mediator.Send(new SearchPaymentsQuery { OrderId = orderId, @@ -63,6 +62,7 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } @@ -75,7 +75,10 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long paymentId, CancellationToken cancellationToken) { + // 1. 查询支付记录详情 var result = await mediator.Send(new GetPaymentByIdQuery { PaymentId = paymentId }, cancellationToken); + + // 2. 返回详情或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "支付记录不存在") : ApiResponse.Ok(result); @@ -90,11 +93,16 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long paymentId, [FromBody] UpdatePaymentCommand command, CancellationToken cancellationToken) { + // 1. 确保命令包含支付记录标识 if (command.PaymentId == 0) { command = command with { PaymentId = paymentId }; } + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "支付记录不存在") : ApiResponse.Ok(result); @@ -109,7 +117,10 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long paymentId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeletePaymentCommand { PaymentId = paymentId }, cancellationToken); + + // 2. 返回结果或 404 return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "支付记录不存在"); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/PermissionsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/PermissionsController.cs index 26b8518..c3646b4 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/PermissionsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/PermissionsController.cs @@ -31,7 +31,10 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Search([FromQuery] SearchPermissionsQuery query, CancellationToken cancellationToken) { + // 1. 查询权限分页 var result = await mediator.Send(query, cancellationToken); + + // 2. 返回分页数据 return ApiResponse>.Ok(result); } @@ -43,7 +46,10 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody, Required] CreatePermissionCommand command, CancellationToken cancellationToken) { + // 1. 创建权限 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -56,8 +62,13 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long permissionId, [FromBody, Required] UpdatePermissionCommand command, CancellationToken cancellationToken) { + // 1. 绑定权限标识 command = command with { PermissionId = permissionId }; + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "权限不存在") : ApiResponse.Ok(result); @@ -71,7 +82,10 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Delete(long permissionId, CancellationToken cancellationToken) { + // 1. 构建删除命令 var command = new DeletePermissionCommand { PermissionId = permissionId }; + + // 2. 执行删除 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs index 24e334c..5cd7e8e 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs @@ -16,16 +16,11 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 商品管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/products")] public sealed class ProductsController(IMediator mediator) : BaseApiController { - - /// /// 创建商品。 /// @@ -34,7 +29,10 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateProductCommand command, CancellationToken cancellationToken) { + // 1. 创建商品 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -54,6 +52,7 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { + // 1. 组装查询参数并执行查询 var result = await mediator.Send(new SearchProductsQuery { StoreId = storeId, @@ -65,6 +64,7 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } @@ -77,7 +77,10 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long productId, CancellationToken cancellationToken) { + // 1. 查询商品详情 var result = await mediator.Send(new GetProductByIdQuery { ProductId = productId }, cancellationToken); + + // 2. 返回详情或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商品不存在") : ApiResponse.Ok(result); @@ -92,11 +95,16 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long productId, [FromBody] UpdateProductCommand command, CancellationToken cancellationToken) { + // 1. 确保命令包含商品标识 if (command.ProductId == 0) { command = command with { ProductId = productId }; } + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商品不存在") : ApiResponse.Ok(result); @@ -111,7 +119,10 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long productId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeleteProductCommand { ProductId = productId }, cancellationToken); + + // 2. 返回结果或 404 return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/RolesController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/RolesController.cs index 1033f5c..6a46937 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/RolesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/RolesController.cs @@ -32,7 +32,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> ListTemplates([FromQuery] bool? isActive, CancellationToken cancellationToken) { + // 1. 查询模板列表 var result = await mediator.Send(new ListRoleTemplatesQuery { IsActive = isActive }, cancellationToken); + + // 2. 返回模板集合 return ApiResponse>.Ok(result); } @@ -48,7 +51,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> GetTemplate(string templateCode, CancellationToken cancellationToken) { + // 1. 查询指定模板 var result = await mediator.Send(new GetRoleTemplateQuery { TemplateCode = templateCode }, cancellationToken); + + // 2. 返回模板或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "角色模板不存在") : ApiResponse.Ok(result); @@ -62,7 +68,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> CreateTemplate([FromBody, Required] CreateRoleTemplateCommand command, CancellationToken cancellationToken) { + // 1. 创建模板 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -78,8 +87,13 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [FromBody, Required] UpdateRoleTemplateCommand command, CancellationToken cancellationToken) { + // 1. 绑定模板编码 command = command with { TemplateCode = templateCode }; + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "角色模板不存在") : ApiResponse.Ok(result); @@ -93,7 +107,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> DeleteTemplate(string templateCode, CancellationToken cancellationToken) { + // 1. 删除模板 var result = await mediator.Send(new DeleteRoleTemplateCommand { TemplateCode = templateCode }, cancellationToken); + + // 2. 返回执行结果 return ApiResponse.Ok(result); } @@ -112,7 +129,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [FromBody, Required] CopyRoleTemplateCommand command, CancellationToken cancellationToken) { + // 1. 绑定模板编码 command = command with { TemplateCode = templateCode }; + + // 2. 复制模板并返回角色 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -131,7 +151,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [FromBody] InitializeRoleTemplatesCommand? command, CancellationToken cancellationToken) { + // 1. 确保命令实例存在 command ??= new InitializeRoleTemplatesCommand(); + + // 2. 执行初始化 var result = await mediator.Send(command, cancellationToken); return ApiResponse>.Ok(result); } @@ -149,7 +172,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Search([FromQuery] SearchRolesQuery query, CancellationToken cancellationToken) { + // 1. 查询角色分页 var result = await mediator.Send(query, cancellationToken); + + // 2. 返回分页数据 return ApiResponse>.Ok(result); } @@ -161,7 +187,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody, Required] CreateRoleCommand command, CancellationToken cancellationToken) { + // 1. 创建角色 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -174,8 +203,13 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long roleId, [FromBody, Required] UpdateRoleCommand command, CancellationToken cancellationToken) { + // 1. 绑定角色标识 command = command with { RoleId = roleId }; + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "角色不存在") : ApiResponse.Ok(result); @@ -189,7 +223,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Delete(long roleId, CancellationToken cancellationToken) { + // 1. 构建删除命令 var command = new DeleteRoleCommand { RoleId = roleId }; + + // 2. 执行删除 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -202,7 +239,10 @@ public sealed class RolesController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> BindPermissions(long roleId, [FromBody, Required] BindRolePermissionsCommand command, CancellationToken cancellationToken) { + // 1. 绑定角色标识 command = command with { RoleId = roleId }; + + // 2. 执行覆盖式授权 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/StoresController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/StoresController.cs index 6abdac2..7ae4958 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/StoresController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/StoresController.cs @@ -16,16 +16,11 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 门店管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/stores")] public sealed class StoresController(IMediator mediator) : BaseApiController { - - /// /// 创建门店。 /// @@ -34,7 +29,10 @@ public sealed class StoresController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateStoreCommand command, CancellationToken cancellationToken) { + // 1. 创建门店 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -53,6 +51,7 @@ public sealed class StoresController(IMediator mediator) : BaseApiController [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { + // 1. 组装查询参数并执行查询 var result = await mediator.Send(new SearchStoresQuery { MerchantId = merchantId, @@ -63,6 +62,7 @@ public sealed class StoresController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } @@ -75,7 +75,10 @@ public sealed class StoresController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long storeId, CancellationToken cancellationToken) { + // 1. 查询门店详情 var result = await mediator.Send(new GetStoreByIdQuery { StoreId = storeId }, cancellationToken); + + // 2. 返回详情或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "门店不存在") : ApiResponse.Ok(result); @@ -90,11 +93,16 @@ public sealed class StoresController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long storeId, [FromBody] UpdateStoreCommand command, CancellationToken cancellationToken) { + // 1. 确保命令包含门店标识 if (command.StoreId == 0) { command = command with { StoreId = storeId }; } + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "门店不存在") : ApiResponse.Ok(result); @@ -109,7 +117,10 @@ public sealed class StoresController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long storeId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeleteStoreCommand { StoreId = storeId }, cancellationToken); + + // 2. 返回结果或 404 return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "门店不存在"); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/SystemParametersController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/SystemParametersController.cs index 045d649..f1f2e8d 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/SystemParametersController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/SystemParametersController.cs @@ -31,7 +31,10 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateSystemParameterCommand command, CancellationToken cancellationToken) { + // 1. 创建系统参数 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -50,6 +53,7 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { + // 1. 组合查询参数 var result = await mediator.Send(new SearchSystemParametersQuery { Keyword = keyword, @@ -60,6 +64,7 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } @@ -72,7 +77,10 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long parameterId, CancellationToken cancellationToken) { + // 1. 查询参数详情 var result = await mediator.Send(new GetSystemParameterByIdQuery(parameterId), cancellationToken); + + // 2. 返回详情或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "系统参数不存在") : ApiResponse.Ok(result); @@ -87,12 +95,16 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long parameterId, [FromBody] UpdateSystemParameterCommand command, CancellationToken cancellationToken) { + // 1. 确保命令包含参数标识 if (command.ParameterId == 0) { command = command with { ParameterId = parameterId }; } + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回结果或 404 return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "系统参数不存在") : ApiResponse.Ok(result); @@ -107,7 +119,10 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long parameterId, CancellationToken cancellationToken) { + // 1. 执行删除 var success = await mediator.Send(new DeleteSystemParameterCommand { ParameterId = parameterId }, cancellationToken); + + // 2. 返回成功或 404 return success ? ApiResponse.Success() : ApiResponse.Error(ErrorCodes.NotFound, "系统参数不存在"); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantAnnouncementsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantAnnouncementsController.cs index e556b3d..d64b89f 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantAnnouncementsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantAnnouncementsController.cs @@ -28,8 +28,13 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Search(long tenantId, [FromQuery] SearchTenantAnnouncementsQuery query, CancellationToken cancellationToken) { + // 1. 绑定租户标识 query = query with { TenantId = tenantId }; + + // 2. 查询公告列表 var result = await mediator.Send(query, cancellationToken); + + // 3. 返回分页结果 return ApiResponse>.Ok(result); } @@ -42,7 +47,10 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long tenantId, long announcementId, CancellationToken cancellationToken) { + // 1. 查询指定公告 var result = await mediator.Send(new GetTenantAnnouncementQuery { TenantId = tenantId, AnnouncementId = announcementId }, cancellationToken); + + // 2. 返回详情或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "公告不存在") : ApiResponse.Ok(result); @@ -56,7 +64,10 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create(long tenantId, [FromBody, Required] CreateTenantAnnouncementCommand command, CancellationToken cancellationToken) { + // 1. 绑定租户标识 command = command with { TenantId = tenantId }; + + // 2. 创建公告并返回 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -70,8 +81,13 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long tenantId, long announcementId, [FromBody, Required] UpdateTenantAnnouncementCommand command, CancellationToken cancellationToken) { + // 1. 绑定租户与公告标识 command = command with { TenantId = tenantId, AnnouncementId = announcementId }; + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "公告不存在") : ApiResponse.Ok(result); @@ -85,7 +101,10 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Delete(long tenantId, long announcementId, CancellationToken cancellationToken) { + // 1. 删除公告 var result = await mediator.Send(new DeleteTenantAnnouncementCommand { TenantId = tenantId, AnnouncementId = announcementId }, cancellationToken); + + // 2. 返回执行结果 return ApiResponse.Ok(result); } @@ -98,7 +117,10 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> MarkRead(long tenantId, long announcementId, CancellationToken cancellationToken) { + // 1. 标记公告已读 var result = await mediator.Send(new MarkTenantAnnouncementReadCommand { TenantId = tenantId, AnnouncementId = announcementId }, cancellationToken); + + // 2. 返回结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "公告不存在") : ApiResponse.Ok(result); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantBillingsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantBillingsController.cs index fb2093f..85ca964 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantBillingsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantBillingsController.cs @@ -28,8 +28,13 @@ public sealed class TenantBillingsController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Search(long tenantId, [FromQuery] SearchTenantBillsQuery query, CancellationToken cancellationToken) { + // 1. 绑定租户标识 query = query with { TenantId = tenantId }; + + // 2. 查询账单列表 var result = await mediator.Send(query, cancellationToken); + + // 3. 返回分页结果 return ApiResponse>.Ok(result); } @@ -42,7 +47,10 @@ public sealed class TenantBillingsController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long tenantId, long billingId, CancellationToken cancellationToken) { + // 1. 查询账单详情 var result = await mediator.Send(new GetTenantBillQuery { TenantId = tenantId, BillingId = billingId }, cancellationToken); + + // 2. 返回详情或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "账单不存在") : ApiResponse.Ok(result); @@ -56,7 +64,10 @@ public sealed class TenantBillingsController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create(long tenantId, [FromBody, Required] CreateTenantBillingCommand command, CancellationToken cancellationToken) { + // 1. 绑定租户标识 command = command with { TenantId = tenantId }; + + // 2. 创建账单 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -70,8 +81,13 @@ public sealed class TenantBillingsController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> MarkPaid(long tenantId, long billingId, [FromBody, Required] MarkTenantBillingPaidCommand command, CancellationToken cancellationToken) { + // 1. 绑定租户与账单标识 command = command with { TenantId = tenantId, BillingId = billingId }; + + // 2. 标记支付状态 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "账单不存在") : ApiResponse.Ok(result); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantNotificationsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantNotificationsController.cs index 84babb5..dd79892 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantNotificationsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantNotificationsController.cs @@ -27,8 +27,13 @@ public sealed class TenantNotificationsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Search(long tenantId, [FromQuery] SearchTenantNotificationsQuery query, CancellationToken cancellationToken) { + // 1. 绑定租户标识 query = query with { TenantId = tenantId }; + + // 2. 查询通知列表 var result = await mediator.Send(query, cancellationToken); + + // 3. 返回分页结果 return ApiResponse>.Ok(result); } @@ -41,7 +46,10 @@ public sealed class TenantNotificationsController(IMediator mediator) : BaseApiC [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> MarkRead(long tenantId, long notificationId, CancellationToken cancellationToken) { + // 1. 标记通知为已读 var result = await mediator.Send(new MarkTenantNotificationReadCommand { TenantId = tenantId, NotificationId = notificationId }, cancellationToken); + + // 2. 返回结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "通知不存在") : ApiResponse.Ok(result); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantPackagesController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantPackagesController.cs index 6f5d77a..3504bd9 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantPackagesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantPackagesController.cs @@ -28,7 +28,10 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Search([FromQuery] SearchTenantPackagesQuery query, CancellationToken cancellationToken) { + // 1. 查询租户套餐分页 var result = await mediator.Send(query, cancellationToken); + + // 2. 返回结果 return ApiResponse>.Ok(result); } @@ -41,7 +44,10 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long tenantPackageId, CancellationToken cancellationToken) { + // 1. 查询套餐详情 var result = await mediator.Send(new GetTenantPackageByIdQuery { TenantPackageId = tenantPackageId }, cancellationToken); + + // 2. 返回查询结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "套餐不存在") : ApiResponse.Ok(result); @@ -55,7 +61,10 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody, Required] CreateTenantPackageCommand command, CancellationToken cancellationToken) { + // 1. 执行创建 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回创建结果 return ApiResponse.Ok(result); } @@ -68,8 +77,13 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long tenantPackageId, [FromBody, Required] UpdateTenantPackageCommand command, CancellationToken cancellationToken) { + // 1. 绑定路由 ID command = command with { TenantPackageId = tenantPackageId }; + + // 2. 执行更新 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回更新结果或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "套餐不存在") : ApiResponse.Ok(result); @@ -83,7 +97,10 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Delete(long tenantPackageId, CancellationToken cancellationToken) { + // 1. 构建删除命令 var command = new DeleteTenantPackageCommand { TenantPackageId = tenantPackageId }; + + // 2. 执行删除并返回 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantsController.cs index f1c3bcd..0d91ee2 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantsController.cs @@ -29,7 +29,10 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Register([FromBody] RegisterTenantCommand command, CancellationToken cancellationToken) { + // 1. 注册租户并初始化套餐 var result = await mediator.Send(command, cancellationToken); + + // 2. 返回注册结果 return ApiResponse.Ok(result); } @@ -41,7 +44,10 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Search([FromQuery] SearchTenantsQuery query, CancellationToken cancellationToken) { + // 1. 查询租户分页 var result = await mediator.Send(query, cancellationToken); + + // 2. 返回分页数据 return ApiResponse>.Ok(result); } @@ -53,7 +59,10 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Detail(long tenantId, CancellationToken cancellationToken) { + // 1. 查询租户详情 var result = await mediator.Send(new GetTenantByIdQuery(tenantId), cancellationToken); + + // 2. 返回租户信息 return ApiResponse.Ok(result); } @@ -68,8 +77,13 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [FromBody] SubmitTenantVerificationCommand body, CancellationToken cancellationToken) { + // 1. 合并路由中的租户标识 var command = body with { TenantId = tenantId }; + + // 2. 提交或更新认证资料 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回认证结果 return ApiResponse.Ok(result); } @@ -81,8 +95,13 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Review(long tenantId, [FromBody] ReviewTenantCommand body, CancellationToken cancellationToken) { + // 1. 绑定租户标识 var command = body with { TenantId = tenantId }; + + // 2. 执行审核 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回审核结果 return ApiResponse.Ok(result); } @@ -97,7 +116,10 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [FromBody] CreateTenantSubscriptionCommand body, CancellationToken cancellationToken) { + // 1. 绑定租户并创建或续费订阅 var command = body with { TenantId = tenantId }; + + // 2. 返回订阅结果 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } @@ -114,8 +136,13 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [FromBody] ChangeTenantSubscriptionPlanCommand body, CancellationToken cancellationToken) { + // 1. 绑定租户与订阅标识 var command = body with { TenantId = tenantId, TenantSubscriptionId = subscriptionId }; + + // 2. 执行升降配 var result = await mediator.Send(command, cancellationToken); + + // 3. 返回调整后的订阅 return ApiResponse.Ok(result); } @@ -131,7 +158,10 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default) { + // 1. 构造审核日志查询 var query = new GetTenantAuditLogsQuery(tenantId, page, pageSize); + + // 2. 查询并返回分页结果 var result = await mediator.Send(query, cancellationToken); return ApiResponse>.Ok(result); } @@ -148,7 +178,10 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController [FromBody, Required] CheckTenantQuotaCommand body, CancellationToken cancellationToken) { + // 1. 绑定租户标识 var command = body with { TenantId = tenantId }; + + // 2. 校验并占用配额 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/UserPermissionsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/UserPermissionsController.cs index 328499a..f2f54e2 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/UserPermissionsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/UserPermissionsController.cs @@ -58,6 +58,7 @@ public sealed class UserPermissionsController(IAdminAuthService authService) : B [FromQuery] SearchUserPermissionsQuery query, CancellationToken cancellationToken) { + // 1. 查询当前租户的用户权限概览 var result = await authService.SearchUserPermissionsAsync( query.Keyword, query.Page, @@ -66,6 +67,7 @@ public sealed class UserPermissionsController(IAdminAuthService authService) : B query.SortDescending, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } } diff --git a/src/Api/TakeoutSaaS.AdminApi/Program.cs b/src/Api/TakeoutSaaS.AdminApi/Program.cs index 32b6036..432bd87 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Program.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Program.cs @@ -26,13 +26,16 @@ using TakeoutSaaS.Module.Tenancy.Extensions; using TakeoutSaaS.Shared.Web.Extensions; using TakeoutSaaS.Shared.Web.Swagger; +// 1. 创建构建器与日志模板 var builder = WebApplication.CreateBuilder(args); const string logTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [TraceId:{TraceId}] [SpanId:{SpanId}] [Service:{Service}] {SourceContext} {Message:lj}{NewLine}{Exception}"; +// 2. 加载种子配置文件 builder.Configuration .AddJsonFile("appsettings.Seed.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.Seed.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true); +// 3. 配置 Serilog 输出 builder.Host.UseSerilog((context, _, configuration) => { configuration @@ -47,6 +50,7 @@ builder.Host.UseSerilog((context, _, configuration) => outputTemplate: logTemplate); }); +// 4. 注册通用 Web 能力与 Swagger builder.Services.AddSharedWebCore(); builder.Services.AddSharedSwagger(options => { @@ -54,6 +58,8 @@ builder.Services.AddSharedSwagger(options => options.Description = "管理后台 API 文档"; options.EnableAuthorization = true; }); + +// 5. 注册领域与基础设施模块 builder.Services.AddIdentityApplication(); builder.Services.AddIdentityInfrastructure(builder.Configuration, enableAdminSeed: true); builder.Services.AddAppInfrastructure(builder.Configuration); @@ -71,6 +77,8 @@ builder.Services.AddMessagingModule(builder.Configuration); builder.Services.AddMessagingApplication(); builder.Services.AddSchedulerModule(builder.Configuration); builder.Services.AddHealthChecks(); + +// 6. 配置 OpenTelemetry 采集 var otelSection = builder.Configuration.GetSection("Otel"); var otelEndpoint = otelSection.GetValue("Endpoint"); var useConsoleExporter = otelSection.GetValue("UseConsoleExporter") ?? builder.Environment.IsDevelopment(); @@ -86,7 +94,6 @@ builder.Services.AddOpenTelemetry() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddEntityFrameworkCoreInstrumentation(); - if (!string.IsNullOrWhiteSpace(otelEndpoint)) { tracing.AddOtlpExporter(exporter => @@ -94,7 +101,6 @@ builder.Services.AddOpenTelemetry() exporter.Endpoint = new Uri(otelEndpoint); }); } - if (useConsoleExporter) { tracing.AddConsoleExporter(); @@ -107,7 +113,6 @@ builder.Services.AddOpenTelemetry() .AddHttpClientInstrumentation() .AddRuntimeInstrumentation() .AddPrometheusExporter(); - if (!string.IsNullOrWhiteSpace(otelEndpoint)) { metrics.AddOtlpExporter(exporter => @@ -115,13 +120,13 @@ builder.Services.AddOpenTelemetry() exporter.Endpoint = new Uri(otelEndpoint); }); } - if (useConsoleExporter) { metrics.AddConsoleExporter(); } }); +// 7. 解析并配置 CORS var adminOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:Admin"); builder.Services.AddCors(options => { @@ -131,8 +136,8 @@ builder.Services.AddCors(options => }); }); +// 8. 构建应用并配置中间件管道 var app = builder.Build(); - app.UseCors("AdminApiCors"); app.UseTenantResolution(); app.UseSharedWebCore(); @@ -140,12 +145,12 @@ app.UseAuthentication(); app.UseAuthorization(); app.UseSharedSwagger(); app.UseSchedulerDashboard(builder.Configuration); - app.MapHealthChecks("/healthz"); app.MapPrometheusScrapingEndpoint(); app.MapControllers(); app.Run(); +// 9. 解析配置中的 CORS 域名 static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionKey) { var origins = configuration.GetSection(sectionKey).Get(); @@ -155,6 +160,7 @@ static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionK .ToArray() ?? []; } +// 10. 构建 CORS 策略 static void ConfigureCorsPolicy(CorsPolicyBuilder policy, string[] origins) { if (origins.Length == 0) @@ -166,7 +172,6 @@ static void ConfigureCorsPolicy(CorsPolicyBuilder policy, string[] origins) policy.WithOrigins(origins) .AllowCredentials(); } - policy .AllowAnyHeader() .AllowAnyMethod(); diff --git a/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs b/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs index 332dec2..5062afd 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs @@ -10,17 +10,13 @@ namespace TakeoutSaaS.MiniApi.Controllers; /// /// 小程序登录认证 /// -/// -/// 小程序登录认证 -/// -/// +/// 提供小程序端的微信登录与 Token 刷新能力。 +/// 小程序认证服务 [ApiVersion("1.0")] [Authorize] [Route("api/mini/v{version:apiVersion}/auth")] public sealed class AuthController(IMiniAuthService authService) : BaseApiController { - - /// /// 微信登录 /// @@ -29,7 +25,10 @@ public sealed class AuthController(IMiniAuthService authService) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> LoginWithWeChat([FromBody] WeChatLoginRequest request, CancellationToken cancellationToken) { + // 1. 调用认证服务完成微信登录 var response = await authService.LoginWithWeChatAsync(request, cancellationToken); + + // 2. 返回访问与刷新令牌 return ApiResponse.Ok(response); } @@ -41,7 +40,10 @@ public sealed class AuthController(IMiniAuthService authService) : BaseApiContro [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> RefreshToken([FromBody] RefreshTokenRequest request, CancellationToken cancellationToken) { + // 1. 调用认证服务刷新 Token var response = await authService.RefreshTokenAsync(request, cancellationToken); + + // 2. 返回新的令牌 return ApiResponse.Ok(response); } } diff --git a/src/Api/TakeoutSaaS.MiniApi/Controllers/FilesController.cs b/src/Api/TakeoutSaaS.MiniApi/Controllers/FilesController.cs index a795c9e..67d5961 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/FilesController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/FilesController.cs @@ -19,8 +19,6 @@ namespace TakeoutSaaS.MiniApi.Controllers; [Route("api/mini/v{version:apiVersion}/files")] public sealed class FilesController(IFileStorageService fileStorageService) : BaseApiController { - private readonly IFileStorageService _fileStorageService = fileStorageService; - /// /// 上传图片或文件。 /// @@ -30,23 +28,28 @@ public sealed class FilesController(IFileStorageService fileStorageService) : Ba [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] public async Task> Upload([FromForm] IFormFile? file, [FromForm] string? type, CancellationToken cancellationToken) { + // 1. 校验文件有效性 if (file == null || file.Length == 0) { return ApiResponse.Error(ErrorCodes.BadRequest, "文件不能为空"); } + // 2. 解析上传类型 if (!UploadFileTypeParser.TryParse(type, out var uploadType)) { return ApiResponse.Error(ErrorCodes.BadRequest, "上传类型不合法"); } + // 3. 提取请求来源 var origin = Request.Headers["Origin"].FirstOrDefault() ?? Request.Headers["Referer"].FirstOrDefault(); await using var stream = file.OpenReadStream(); - var result = await _fileStorageService.UploadAsync( + // 4. 调用存储服务执行上传 + var result = await fileStorageService.UploadAsync( new UploadFileRequest(uploadType, stream, file.FileName, file.ContentType ?? string.Empty, file.Length, origin), cancellationToken); + // 5. 返回上传结果 return ApiResponse.Ok(result); } } diff --git a/src/Api/TakeoutSaaS.MiniApi/Controllers/HealthController.cs b/src/Api/TakeoutSaaS.MiniApi/Controllers/HealthController.cs index c2673f8..d4e9920 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/HealthController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/HealthController.cs @@ -23,7 +23,10 @@ public class HealthController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public ApiResponse Get() { + // 1. 构造健康状态 var payload = new { status = "OK", service = "MiniApi", time = DateTime.UtcNow }; + + // 2. 返回健康响应 return ApiResponse.Ok(payload); } } diff --git a/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs b/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs index 4bd29e4..4c29f25 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs @@ -16,17 +16,13 @@ namespace TakeoutSaaS.MiniApi.Controllers; /// /// 当前用户信息 /// -/// -/// -/// -/// +/// 提供小程序端当前用户档案查询。 +/// 小程序认证服务 [ApiVersion("1.0")] [Authorize] [Route("api/mini/v{version:apiVersion}/me")] public sealed class MeController(IMiniAuthService authService) : BaseApiController { - - /// /// 获取用户档案 /// @@ -35,12 +31,14 @@ public sealed class MeController(IMiniAuthService authService) : BaseApiControll [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status401Unauthorized)] public async Task> Get(CancellationToken cancellationToken) { + // 1. 从 JWT 中解析用户标识 var userId = User.GetUserId(); if (userId == 0) { return ApiResponse.Error(ErrorCodes.Unauthorized, "Token 缺少有效的用户标识"); } + // 2. 查询用户档案并返回 var profile = await authService.GetProfileAsync(userId, cancellationToken); return ApiResponse.Ok(profile); } diff --git a/src/Api/TakeoutSaaS.MiniApi/Program.cs b/src/Api/TakeoutSaaS.MiniApi/Program.cs index 3c5adac..d4df34c 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Program.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Program.cs @@ -18,9 +18,11 @@ using TakeoutSaaS.Shared.Kernel.Ids; using TakeoutSaaS.Shared.Web.Extensions; using TakeoutSaaS.Shared.Web.Swagger; +// 1. 创建构建器与日志模板 var builder = WebApplication.CreateBuilder(args); const string logTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [TraceId:{TraceId}] [SpanId:{SpanId}] [Service:{Service}] {SourceContext} {Message:lj}{NewLine}{Exception}"; +// 2. 注册雪花 ID 生成器与 Serilog builder.Services.AddSingleton(_ => new SnowflakeIdGenerator()); builder.Host.UseSerilog((_, _, configuration) => { @@ -36,6 +38,7 @@ builder.Host.UseSerilog((_, _, configuration) => outputTemplate: logTemplate); }); +// 3. 注册通用 Web 能力与 Swagger builder.Services.AddSharedWebCore(); builder.Services.AddSharedSwagger(options => { @@ -43,6 +46,8 @@ builder.Services.AddSharedSwagger(options => options.Description = "小程序 API 文档"; options.EnableAuthorization = true; }); + +// 4. 注册多租户与业务模块 builder.Services.AddTenantResolution(builder.Configuration); builder.Services.AddStorageModule(builder.Configuration); builder.Services.AddStorageApplication(); @@ -51,6 +56,8 @@ builder.Services.AddSmsApplication(builder.Configuration); builder.Services.AddMessagingModule(builder.Configuration); builder.Services.AddMessagingApplication(); builder.Services.AddHealthChecks(); + +// 5. 配置 OpenTelemetry 采集 var otelSection = builder.Configuration.GetSection("Otel"); var otelEndpoint = otelSection.GetValue("Endpoint"); var useConsoleExporter = otelSection.GetValue("UseConsoleExporter") ?? builder.Environment.IsDevelopment(); @@ -102,6 +109,7 @@ builder.Services.AddOpenTelemetry() } }); +// 6. 配置 CORS var miniOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:Mini"); builder.Services.AddCors(options => { @@ -111,6 +119,7 @@ builder.Services.AddCors(options => }); }); +// 7. 构建应用并配置中间件管道 var app = builder.Build(); app.UseCors("MiniApiCors"); @@ -123,6 +132,7 @@ app.MapPrometheusScrapingEndpoint(); app.MapControllers(); app.Run(); +// 8. 解析配置中的 CORS 域名 static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionKey) { var origins = configuration.GetSection(sectionKey).Get(); @@ -132,6 +142,7 @@ static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionK .ToArray() ?? []; } +// 9. 构建 CORS 策略 static void ConfigureCorsPolicy(CorsPolicyBuilder policy, string[] origins) { if (origins.Length == 0) diff --git a/src/Api/TakeoutSaaS.UserApi/Controllers/HealthController.cs b/src/Api/TakeoutSaaS.UserApi/Controllers/HealthController.cs index e1fa6d5..0056def 100644 --- a/src/Api/TakeoutSaaS.UserApi/Controllers/HealthController.cs +++ b/src/Api/TakeoutSaaS.UserApi/Controllers/HealthController.cs @@ -23,7 +23,10 @@ public class HealthController : BaseApiController [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public ApiResponse Get() { + // 1. 构造健康状态 var payload = new { status = "OK", service = "UserApi", time = DateTime.UtcNow }; + + // 2. 返回健康响应 return ApiResponse.Ok(payload); } } diff --git a/src/Api/TakeoutSaaS.UserApi/Program.cs b/src/Api/TakeoutSaaS.UserApi/Program.cs index 5c93992..a531e11 100644 --- a/src/Api/TakeoutSaaS.UserApi/Program.cs +++ b/src/Api/TakeoutSaaS.UserApi/Program.cs @@ -12,9 +12,11 @@ using TakeoutSaaS.Shared.Kernel.Ids; using TakeoutSaaS.Shared.Web.Extensions; using TakeoutSaaS.Shared.Web.Swagger; +// 1. 创建构建器与日志模板 var builder = WebApplication.CreateBuilder(args); const string logTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [TraceId:{TraceId}] [SpanId:{SpanId}] [Service:{Service}] {SourceContext} {Message:lj}{NewLine}{Exception}"; +// 2. 注册雪花 ID 生成器与 Serilog builder.Services.AddSingleton(_ => new SnowflakeIdGenerator()); builder.Host.UseSerilog((_, _, configuration) => { @@ -30,6 +32,7 @@ builder.Host.UseSerilog((_, _, configuration) => outputTemplate: logTemplate); }); +// 3. 注册通用 Web 能力与 Swagger builder.Services.AddSharedWebCore(); builder.Services.AddSharedSwagger(options => { @@ -37,8 +40,12 @@ builder.Services.AddSharedSwagger(options => options.Description = "C 端用户 API 文档"; options.EnableAuthorization = true; }); + +// 4. 注册多租户与健康检查 builder.Services.AddTenantResolution(builder.Configuration); builder.Services.AddHealthChecks(); + +// 5. 配置 OpenTelemetry 采集 var otelSection = builder.Configuration.GetSection("Otel"); var otelEndpoint = otelSection.GetValue("Endpoint"); var useConsoleExporter = otelSection.GetValue("UseConsoleExporter") ?? builder.Environment.IsDevelopment(); @@ -90,6 +97,7 @@ builder.Services.AddOpenTelemetry() } }); +// 6. 配置 CORS var userOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:User"); builder.Services.AddCors(options => { @@ -99,6 +107,7 @@ builder.Services.AddCors(options => }); }); +// 7. 构建应用并配置中间件管道 var app = builder.Build(); app.UseCors("UserApiCors"); @@ -111,6 +120,7 @@ app.MapPrometheusScrapingEndpoint(); app.MapControllers(); app.Run(); +// 8. 解析配置中的 CORS 域名 static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionKey) { var origins = configuration.GetSection(sectionKey).Get(); @@ -120,6 +130,7 @@ static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionK .ToArray() ?? []; } +// 9. 构建 CORS 策略 static void ConfigureCorsPolicy(CorsPolicyBuilder policy, string[] origins) { if (origins.Length == 0) diff --git a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/CreateDeliveryOrderCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/CreateDeliveryOrderCommandHandler.cs index 1a67240..ad4149f 100644 --- a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/CreateDeliveryOrderCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/CreateDeliveryOrderCommandHandler.cs @@ -13,12 +13,10 @@ namespace TakeoutSaaS.Application.App.Deliveries.Handlers; public sealed class CreateDeliveryOrderCommandHandler(IDeliveryRepository deliveryRepository, ILogger logger) : IRequestHandler { - private readonly IDeliveryRepository _deliveryRepository = deliveryRepository; - private readonly ILogger _logger = logger; - /// public async Task Handle(CreateDeliveryOrderCommand request, CancellationToken cancellationToken) { + // 1. 构建配送单实体 var deliveryOrder = new DeliveryOrder { OrderId = request.OrderId, @@ -34,10 +32,14 @@ public sealed class CreateDeliveryOrderCommandHandler(IDeliveryRepository delive FailureReason = request.FailureReason?.Trim() }; - await _deliveryRepository.AddDeliveryOrderAsync(deliveryOrder, cancellationToken); - await _deliveryRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("创建配送单 {DeliveryOrderId} 对应订单 {OrderId}", deliveryOrder.Id, deliveryOrder.OrderId); + // 2. 持久化配送单 + await deliveryRepository.AddDeliveryOrderAsync(deliveryOrder, cancellationToken); + await deliveryRepository.SaveChangesAsync(cancellationToken); + // 3. 记录日志 + logger.LogInformation("创建配送单 {DeliveryOrderId} 对应订单 {OrderId}", deliveryOrder.Id, deliveryOrder.OrderId); + + // 4. 映射 DTO 返回 return MapToDto(deliveryOrder, []); } diff --git a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/DeleteDeliveryOrderCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/DeleteDeliveryOrderCommandHandler.cs index 40974f1..308c942 100644 --- a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/DeleteDeliveryOrderCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/DeleteDeliveryOrderCommandHandler.cs @@ -15,23 +15,23 @@ public sealed class DeleteDeliveryOrderCommandHandler( ILogger logger) : IRequestHandler { - private readonly IDeliveryRepository _deliveryRepository = deliveryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ILogger _logger = logger; - /// public async Task Handle(DeleteDeliveryOrderCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _deliveryRepository.FindByIdAsync(request.DeliveryOrderId, tenantId, cancellationToken); + // 1. 获取租户并定位配送单 + var tenantId = tenantProvider.GetCurrentTenantId(); + var existing = await deliveryRepository.FindByIdAsync(request.DeliveryOrderId, tenantId, cancellationToken); if (existing == null) { return false; } - await _deliveryRepository.DeleteDeliveryOrderAsync(request.DeliveryOrderId, tenantId, cancellationToken); - await _deliveryRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("删除配送单 {DeliveryOrderId}", request.DeliveryOrderId); + // 2. 删除并保存 + await deliveryRepository.DeleteDeliveryOrderAsync(request.DeliveryOrderId, tenantId, cancellationToken); + await deliveryRepository.SaveChangesAsync(cancellationToken); + + // 3. 记录删除日志 + logger.LogInformation("删除配送单 {DeliveryOrderId}", request.DeliveryOrderId); return true; } diff --git a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/GetDeliveryOrderByIdQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/GetDeliveryOrderByIdQueryHandler.cs index 24e425f..d863d4b 100644 --- a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/GetDeliveryOrderByIdQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/GetDeliveryOrderByIdQueryHandler.cs @@ -15,20 +15,23 @@ public sealed class GetDeliveryOrderByIdQueryHandler( ITenantProvider tenantProvider) : IRequestHandler { - private readonly IDeliveryRepository _deliveryRepository = deliveryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task Handle(GetDeliveryOrderByIdQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var order = await _deliveryRepository.FindByIdAsync(request.DeliveryOrderId, tenantId, cancellationToken); + // 1. 读取当前租户标识 + var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 查询配送单主体 + var order = await deliveryRepository.FindByIdAsync(request.DeliveryOrderId, tenantId, cancellationToken); if (order == null) { return null; } - var events = await _deliveryRepository.GetEventsAsync(order.Id, tenantId, cancellationToken); + // 3. 查询配送事件明细 + var events = await deliveryRepository.GetEventsAsync(order.Id, tenantId, cancellationToken); + + // 4. 映射为 DTO 返回 return MapToDto(order, events); } diff --git a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/SearchDeliveryOrdersQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/SearchDeliveryOrdersQueryHandler.cs index cc748db..7fcdd5a 100644 --- a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/SearchDeliveryOrdersQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/SearchDeliveryOrdersQueryHandler.cs @@ -15,21 +15,25 @@ public sealed class SearchDeliveryOrdersQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IDeliveryRepository _deliveryRepository = deliveryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task> Handle(SearchDeliveryOrdersQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var orders = await _deliveryRepository.SearchAsync(tenantId, request.Status, request.OrderId, cancellationToken); + // 1. 获取当前租户标识 + var tenantId = tenantProvider.GetCurrentTenantId(); + // 2. 查询配送单列表(租户隔离) + var orders = await deliveryRepository.SearchAsync(tenantId, request.Status, request.OrderId, cancellationToken); + + // 3. 本地排序 var sorted = ApplySorting(orders, request.SortBy, request.SortDescending); + + // 4. 本地分页 var paged = sorted .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); + // 5. 映射 DTO var items = paged.Select(order => new DeliveryOrderDto { Id = order.Id, @@ -48,6 +52,7 @@ public sealed class SearchDeliveryOrdersQueryHandler( CreatedAt = order.CreatedAt }).ToList(); + // 6. 返回分页结果 return new PagedResult(items, request.Page, request.PageSize, orders.Count); } diff --git a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/UpdateDeliveryOrderCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/UpdateDeliveryOrderCommandHandler.cs index c8df929..0c3f2c2 100644 --- a/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/UpdateDeliveryOrderCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Deliveries/Handlers/UpdateDeliveryOrderCommandHandler.cs @@ -17,20 +17,20 @@ public sealed class UpdateDeliveryOrderCommandHandler( ILogger logger) : IRequestHandler { - private readonly IDeliveryRepository _deliveryRepository = deliveryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ILogger _logger = logger; - /// public async Task Handle(UpdateDeliveryOrderCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _deliveryRepository.FindByIdAsync(request.DeliveryOrderId, tenantId, cancellationToken); + // 1. 获取当前租户标识 + var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 查询目标配送单 + var existing = await deliveryRepository.FindByIdAsync(request.DeliveryOrderId, tenantId, cancellationToken); if (existing == null) { return null; } + // 3. 更新字段 existing.OrderId = request.OrderId; existing.Provider = request.Provider; existing.ProviderOrderId = request.ProviderOrderId?.Trim(); @@ -43,11 +43,15 @@ public sealed class UpdateDeliveryOrderCommandHandler( existing.DeliveredAt = request.DeliveredAt; existing.FailureReason = request.FailureReason?.Trim(); - await _deliveryRepository.UpdateDeliveryOrderAsync(existing, cancellationToken); - await _deliveryRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("更新配送单 {DeliveryOrderId}", existing.Id); + // 4. 持久化变更 + await deliveryRepository.UpdateDeliveryOrderAsync(existing, cancellationToken); + await deliveryRepository.SaveChangesAsync(cancellationToken); - var events = await _deliveryRepository.GetEventsAsync(existing.Id, tenantId, cancellationToken); + // 5. 记录更新日志 + logger.LogInformation("更新配送单 {DeliveryOrderId}", existing.Id); + + // 6. 查询事件并返回映射结果 + var events = await deliveryRepository.GetEventsAsync(existing.Id, tenantId, cancellationToken); return MapToDto(existing, events); } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/AddMerchantDocumentCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/AddMerchantDocumentCommandHandler.cs index 46e2923..2acfd29 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/AddMerchantDocumentCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/AddMerchantDocumentCommandHandler.cs @@ -22,20 +22,17 @@ public sealed class AddMerchantDocumentCommandHandler( ICurrentUserAccessor currentUserAccessor) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly IIdGenerator _idGenerator = idGenerator; - private readonly ICurrentUserAccessor _currentUserAccessor = currentUserAccessor; - public async Task Handle(AddMerchantDocumentCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var merchant = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) + // 1. 获取租户并查询商户 + var tenantId = tenantProvider.GetCurrentTenantId(); + var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "商户不存在"); + // 2. 构建证照记录 var document = new MerchantDocument { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), MerchantId = merchant.Id, DocumentType = request.DocumentType, Status = MerchantDocumentStatus.Pending, @@ -45,8 +42,9 @@ public sealed class AddMerchantDocumentCommandHandler( ExpiresAt = request.ExpiresAt }; - await _merchantRepository.AddDocumentAsync(document, cancellationToken); - await _merchantRepository.AddAuditLogAsync(new MerchantAuditLog + // 3. 持久化与审计 + await merchantRepository.AddDocumentAsync(document, cancellationToken); + await merchantRepository.AddAuditLogAsync(new MerchantAuditLog { TenantId = tenantId, MerchantId = merchant.Id, @@ -57,20 +55,21 @@ public sealed class AddMerchantDocumentCommandHandler( OperatorName = ResolveOperatorName() }, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); + // 4. 返回 DTO return MerchantMapping.ToDto(document); } private long? ResolveOperatorId() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? null : id; } private string ResolveOperatorName() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? "system" : $"user:{id}"; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCategoryCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCategoryCommandHandler.cs index 84dd79e..c94e995 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCategoryCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCategoryCommandHandler.cs @@ -18,22 +18,23 @@ public sealed class CreateMerchantCategoryCommandHandler( ITenantProvider tenantProvider) : IRequestHandler { - private readonly IMerchantCategoryRepository _categoryRepository = categoryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task Handle(CreateMerchantCategoryCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); + // 1. 获取租户上下文 + var tenantId = tenantProvider.GetCurrentTenantId(); var normalizedName = request.Name.Trim(); - if (await _categoryRepository.ExistsAsync(normalizedName, tenantId, cancellationToken)) + // 2. 检查重名 + if (await categoryRepository.ExistsAsync(normalizedName, tenantId, cancellationToken)) { throw new BusinessException(ErrorCodes.Conflict, $"类目“{normalizedName}”已存在"); } - var categories = await _categoryRepository.ListAsync(tenantId, cancellationToken); + // 3. 计算排序 + var categories = await categoryRepository.ListAsync(tenantId, cancellationToken); var targetOrder = request.DisplayOrder ?? (categories.Count == 0 ? 1 : categories.Max(x => x.DisplayOrder) + 1); + // 4. 构建实体 var entity = new MerchantCategory { Name = normalizedName, @@ -41,8 +42,9 @@ public sealed class CreateMerchantCategoryCommandHandler( IsActive = request.IsActive }; - await _categoryRepository.AddAsync(entity, cancellationToken); - await _categoryRepository.SaveChangesAsync(cancellationToken); + // 5. 持久化并返回 + await categoryRepository.AddAsync(entity, cancellationToken); + await categoryRepository.SaveChangesAsync(cancellationToken); return MerchantMapping.ToDto(entity); } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCommandHandler.cs index 70c0982..54a5bba 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCommandHandler.cs @@ -13,12 +13,10 @@ namespace TakeoutSaaS.Application.App.Merchants.Handlers; public sealed class CreateMerchantCommandHandler(IMerchantRepository merchantRepository, ILogger logger) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ILogger _logger = logger; - /// public async Task Handle(CreateMerchantCommand request, CancellationToken cancellationToken) { + // 1. 构建商户实体 var merchant = new Merchant { BrandName = request.BrandName.Trim(), @@ -31,10 +29,12 @@ public sealed class CreateMerchantCommandHandler(IMerchantRepository merchantRep JoinedAt = DateTime.UtcNow }; - await _merchantRepository.AddMerchantAsync(merchant, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); + // 2. 持久化 + await merchantRepository.AddMerchantAsync(merchant, cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("创建商户 {MerchantId} - {BrandName}", merchant.Id, merchant.BrandName); + // 3. 记录日志 + logger.LogInformation("创建商户 {MerchantId} - {BrandName}", merchant.Id, merchant.BrandName); return MapToDto(merchant); } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantContractCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantContractCommandHandler.cs index 0c4aecb..ff809dc 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantContractCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantContractCommandHandler.cs @@ -22,25 +22,23 @@ public sealed class CreateMerchantContractCommandHandler( ICurrentUserAccessor currentUserAccessor) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly IIdGenerator _idGenerator = idGenerator; - private readonly ICurrentUserAccessor _currentUserAccessor = currentUserAccessor; - public async Task Handle(CreateMerchantContractCommand request, CancellationToken cancellationToken) { + // 1. 校验时间 if (request.EndDate <= request.StartDate) { throw new BusinessException(ErrorCodes.BadRequest, "合同结束时间必须晚于开始时间"); } - var tenantId = _tenantProvider.GetCurrentTenantId(); - var merchant = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) + // 2. 查询商户 + var tenantId = tenantProvider.GetCurrentTenantId(); + var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "商户不存在"); + // 3. 构建合同 var contract = new MerchantContract { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), MerchantId = merchant.Id, ContractNumber = request.ContractNumber.Trim(), StartDate = request.StartDate, @@ -48,8 +46,9 @@ public sealed class CreateMerchantContractCommandHandler( FileUrl = request.FileUrl.Trim() }; - await _merchantRepository.AddContractAsync(contract, cancellationToken); - await _merchantRepository.AddAuditLogAsync(new MerchantAuditLog + // 4. 持久化与审计 + await merchantRepository.AddContractAsync(contract, cancellationToken); + await merchantRepository.AddAuditLogAsync(new MerchantAuditLog { TenantId = tenantId, MerchantId = merchant.Id, @@ -60,19 +59,21 @@ public sealed class CreateMerchantContractCommandHandler( OperatorName = ResolveOperatorName() }, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); + + // 5. 返回 DTO return MerchantMapping.ToDto(contract); } private long? ResolveOperatorId() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? null : id; } private string ResolveOperatorName() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? "system" : $"user:{id}"; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCategoryCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCategoryCommandHandler.cs index f0084b5..e4cf48e 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCategoryCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCategoryCommandHandler.cs @@ -13,21 +13,20 @@ public sealed class DeleteMerchantCategoryCommandHandler( ITenantProvider tenantProvider) : IRequestHandler { - private readonly IMerchantCategoryRepository _categoryRepository = categoryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task Handle(DeleteMerchantCategoryCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _categoryRepository.FindByIdAsync(request.CategoryId, tenantId, cancellationToken); + // 1. 获取租户上下文 + var tenantId = tenantProvider.GetCurrentTenantId(); + var existing = await categoryRepository.FindByIdAsync(request.CategoryId, tenantId, cancellationToken); if (existing == null) { return false; } - await _categoryRepository.RemoveAsync(existing, cancellationToken); - await _categoryRepository.SaveChangesAsync(cancellationToken); + // 2. 删除并保存 + await categoryRepository.RemoveAsync(existing, cancellationToken); + await categoryRepository.SaveChangesAsync(cancellationToken); return true; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCommandHandler.cs index 8f69ff0..c79f74f 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/DeleteMerchantCommandHandler.cs @@ -15,25 +15,21 @@ public sealed class DeleteMerchantCommandHandler( ILogger logger) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ILogger _logger = logger; - /// public async Task Handle(DeleteMerchantCommand request, CancellationToken cancellationToken) { // 1. 校验存在性 - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken); + var tenantId = tenantProvider.GetCurrentTenantId(); + var existing = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken); if (existing == null) { return false; } // 2. 删除 - await _merchantRepository.DeleteMerchantAsync(request.MerchantId, tenantId, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("删除商户 {MerchantId}", request.MerchantId); + await merchantRepository.DeleteMerchantAsync(request.MerchantId, tenantId, cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); + logger.LogInformation("删除商户 {MerchantId}", request.MerchantId); return true; } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantAuditLogsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantAuditLogsQueryHandler.cs index f43146c..cfb010d 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantAuditLogsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantAuditLogsQueryHandler.cs @@ -16,20 +16,21 @@ public sealed class GetMerchantAuditLogsQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task> Handle(GetMerchantAuditLogsQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var logs = await _merchantRepository.GetAuditLogsAsync(request.MerchantId, tenantId, cancellationToken); + // 1. 获取租户上下文并查询日志 + var tenantId = tenantProvider.GetCurrentTenantId(); + var logs = await merchantRepository.GetAuditLogsAsync(request.MerchantId, tenantId, cancellationToken); var total = logs.Count; + + // 2. 分页映射 var paged = logs .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .Select(MerchantMapping.ToDto) .ToList(); + // 3. 返回结果 return new PagedResult(paged, request.Page, request.PageSize, total); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantByIdQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantByIdQueryHandler.cs index 3c2313f..8272c6c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantByIdQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantByIdQueryHandler.cs @@ -12,19 +12,18 @@ namespace TakeoutSaaS.Application.App.Merchants.Handlers; public sealed class GetMerchantByIdQueryHandler(IMerchantRepository merchantRepository, ITenantProvider tenantProvider) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task Handle(GetMerchantByIdQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var merchant = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken); + // 1. 获取租户上下文 + var tenantId = tenantProvider.GetCurrentTenantId(); + var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken); if (merchant == null) { return null; } + // 2. 返回 DTO return new MerchantDto { Id = merchant.Id, diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantCategoriesQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantCategoriesQueryHandler.cs index e0f9e89..19c9b2f 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantCategoriesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantCategoriesQueryHandler.cs @@ -15,14 +15,13 @@ public sealed class GetMerchantCategoriesQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IMerchantCategoryRepository _categoryRepository = categoryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task> Handle(GetMerchantCategoriesQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var categories = await _categoryRepository.ListAsync(tenantId, cancellationToken); + // 1. 获取租户上下文并读取类目 + var tenantId = tenantProvider.GetCurrentTenantId(); + var categories = await categoryRepository.ListAsync(tenantId, cancellationToken); + // 2. 过滤启用类目并去重 return categories .Where(x => x.IsActive) .Select(x => x.Name.Trim()) diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantContractsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantContractsQueryHandler.cs index 50b20ab..46842e1 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantContractsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantContractsQueryHandler.cs @@ -17,16 +17,15 @@ public sealed class GetMerchantContractsQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task> Handle(GetMerchantContractsQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - _ = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) + // 1. 获取租户上下文并校验商户存在 + var tenantId = tenantProvider.GetCurrentTenantId(); + _ = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "商户不存在"); - var contracts = await _merchantRepository.GetContractsAsync(request.MerchantId, tenantId, cancellationToken); + // 2. 查询合同列表 + var contracts = await merchantRepository.GetContractsAsync(request.MerchantId, tenantId, cancellationToken); return MerchantMapping.ToContractDtos(contracts); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDetailQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDetailQueryHandler.cs index f21f1d0..0468a29 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDetailQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDetailQueryHandler.cs @@ -16,18 +16,18 @@ public sealed class GetMerchantDetailQueryHandler( ITenantProvider tenantProvider) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task Handle(GetMerchantDetailQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var merchant = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) + // 1. 获取租户上下文并查询商户 + var tenantId = tenantProvider.GetCurrentTenantId(); + var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "商户不存在"); - var documents = await _merchantRepository.GetDocumentsAsync(request.MerchantId, tenantId, cancellationToken); - var contracts = await _merchantRepository.GetContractsAsync(request.MerchantId, tenantId, cancellationToken); + // 2. 查询证照与合同 + var documents = await merchantRepository.GetDocumentsAsync(request.MerchantId, tenantId, cancellationToken); + var contracts = await merchantRepository.GetContractsAsync(request.MerchantId, tenantId, cancellationToken); + // 3. 返回明细 DTO return new MerchantDetailDto { Merchant = MerchantMapping.ToDto(merchant), diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDocumentsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDocumentsQueryHandler.cs index f8ceeb4..3be839c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDocumentsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDocumentsQueryHandler.cs @@ -17,16 +17,15 @@ public sealed class GetMerchantDocumentsQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task> Handle(GetMerchantDocumentsQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - _ = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) + // 1. 获取租户上下文并校验商户存在 + var tenantId = tenantProvider.GetCurrentTenantId(); + _ = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "商户不存在"); - var documents = await _merchantRepository.GetDocumentsAsync(request.MerchantId, tenantId, cancellationToken); + // 2. 查询证照列表 + var documents = await merchantRepository.GetDocumentsAsync(request.MerchantId, tenantId, cancellationToken); return MerchantMapping.ToDocumentDtos(documents); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ListMerchantCategoriesQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ListMerchantCategoriesQueryHandler.cs index c3de6cd..7da5375 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ListMerchantCategoriesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ListMerchantCategoriesQueryHandler.cs @@ -15,13 +15,13 @@ public sealed class ListMerchantCategoriesQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IMerchantCategoryRepository _categoryRepository = categoryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task> Handle(ListMerchantCategoriesQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var categories = await _categoryRepository.ListAsync(tenantId, cancellationToken); + // 1. 获取租户上下文 + var tenantId = tenantProvider.GetCurrentTenantId(); + var categories = await categoryRepository.ListAsync(tenantId, cancellationToken); + + // 2. 映射 DTO return MerchantMapping.ToCategoryDtos(categories); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReorderMerchantCategoriesCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReorderMerchantCategoriesCommandHandler.cs index a9be94d..e06ea65 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReorderMerchantCategoriesCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReorderMerchantCategoriesCommandHandler.cs @@ -16,15 +16,14 @@ public sealed class ReorderMerchantCategoriesCommandHandler( ITenantProvider tenantProvider) : IRequestHandler { - private readonly IMerchantCategoryRepository _categoryRepository = categoryRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - public async Task Handle(ReorderMerchantCategoriesCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var categories = await _categoryRepository.ListAsync(tenantId, cancellationToken); + // 1. 获取租户并查询类目 + var tenantId = tenantProvider.GetCurrentTenantId(); + var categories = await categoryRepository.ListAsync(tenantId, cancellationToken); var map = categories.ToDictionary(x => x.Id); + // 2. 更新排序 foreach (var item in request.Items) { if (!map.TryGetValue(item.CategoryId, out var category)) @@ -35,8 +34,9 @@ public sealed class ReorderMerchantCategoriesCommandHandler( category.DisplayOrder = item.DisplayOrder; } - await _categoryRepository.UpdateRangeAsync(map.Values, cancellationToken); - await _categoryRepository.SaveChangesAsync(cancellationToken); + // 3. 持久化 + await categoryRepository.UpdateRangeAsync(map.Values, cancellationToken); + await categoryRepository.SaveChangesAsync(cancellationToken); return true; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantCommandHandler.cs index 02ea882..c684c09 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantCommandHandler.cs @@ -20,21 +20,20 @@ public sealed class ReviewMerchantCommandHandler( ICurrentUserAccessor currentUserAccessor) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ICurrentUserAccessor _currentUserAccessor = currentUserAccessor; - public async Task Handle(ReviewMerchantCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var merchant = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) + // 1. 读取商户 + var tenantId = tenantProvider.GetCurrentTenantId(); + var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "商户不存在"); + // 2. 已审核通过则直接返回 if (request.Approve && merchant.Status == MerchantStatus.Approved) { return MerchantMapping.ToDto(merchant); } + // 3. 更新审核状态 var previousStatus = merchant.Status; merchant.Status = request.Approve ? MerchantStatus.Approved : MerchantStatus.Rejected; merchant.ReviewRemarks = request.Remarks; @@ -44,8 +43,9 @@ public sealed class ReviewMerchantCommandHandler( merchant.JoinedAt = DateTime.UtcNow; } - await _merchantRepository.UpdateMerchantAsync(merchant, cancellationToken); - await _merchantRepository.AddAuditLogAsync(new MerchantAuditLog + // 4. 持久化与审计 + await merchantRepository.UpdateMerchantAsync(merchant, cancellationToken); + await merchantRepository.AddAuditLogAsync(new MerchantAuditLog { TenantId = tenantId, MerchantId = merchant.Id, @@ -55,20 +55,21 @@ public sealed class ReviewMerchantCommandHandler( OperatorId = ResolveOperatorId(), OperatorName = ResolveOperatorName() }, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); + // 5. 返回 DTO return MerchantMapping.ToDto(merchant); } private long? ResolveOperatorId() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? null : id; } private string ResolveOperatorName() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? "system" : $"user:{id}"; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantDocumentCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantDocumentCommandHandler.cs index 20c1a3a..a8fe365 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantDocumentCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantDocumentCommandHandler.cs @@ -20,27 +20,27 @@ public sealed class ReviewMerchantDocumentCommandHandler( ICurrentUserAccessor currentUserAccessor) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ICurrentUserAccessor _currentUserAccessor = currentUserAccessor; - public async Task Handle(ReviewMerchantDocumentCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var document = await _merchantRepository.FindDocumentByIdAsync(request.MerchantId, tenantId, request.DocumentId, cancellationToken) + // 1. 读取证照 + var tenantId = tenantProvider.GetCurrentTenantId(); + var document = await merchantRepository.FindDocumentByIdAsync(request.MerchantId, tenantId, request.DocumentId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "证照不存在"); + // 2. 若状态无变化且备注相同,直接返回 var targetStatus = request.Approve ? MerchantDocumentStatus.Approved : MerchantDocumentStatus.Rejected; if (document.Status == targetStatus && document.Remarks == request.Remarks) { return MerchantMapping.ToDto(document); } + // 3. 更新状态 document.Status = targetStatus; document.Remarks = request.Remarks; - await _merchantRepository.UpdateDocumentAsync(document, cancellationToken); - await _merchantRepository.AddAuditLogAsync(new MerchantAuditLog + // 4. 持久化与审计 + await merchantRepository.UpdateDocumentAsync(document, cancellationToken); + await merchantRepository.AddAuditLogAsync(new MerchantAuditLog { TenantId = tenantId, MerchantId = document.MerchantId, @@ -50,20 +50,21 @@ public sealed class ReviewMerchantDocumentCommandHandler( OperatorId = ResolveOperatorId(), OperatorName = ResolveOperatorName() }, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); + // 5. 返回 DTO return MerchantMapping.ToDto(document); } private long? ResolveOperatorId() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? null : id; } private string ResolveOperatorName() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? "system" : $"user:{id}"; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/SearchMerchantsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/SearchMerchantsQueryHandler.cs index 8d46242..b545129 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/SearchMerchantsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/SearchMerchantsQueryHandler.cs @@ -15,21 +15,21 @@ public sealed class SearchMerchantsQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task> Handle(SearchMerchantsQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var merchants = await _merchantRepository.SearchAsync(tenantId, request.Status, cancellationToken); + // 1. 获取租户并查询商户 + var tenantId = tenantProvider.GetCurrentTenantId(); + var merchants = await merchantRepository.SearchAsync(tenantId, request.Status, cancellationToken); + // 2. 排序与分页 var sorted = ApplySorting(merchants, request.SortBy, request.SortDescending); var paged = sorted .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); + // 3. 映射 DTO var items = paged.Select(merchant => new MerchantDto { Id = merchant.Id, @@ -45,6 +45,7 @@ public sealed class SearchMerchantsQueryHandler( CreatedAt = merchant.CreatedAt }).ToList(); + // 4. 返回分页结果 return new PagedResult(items, request.Page, request.PageSize, merchants.Count); } diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantCommandHandler.cs index 38753c9..ed1a879 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantCommandHandler.cs @@ -16,16 +16,12 @@ public sealed class UpdateMerchantCommandHandler( ILogger logger) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ILogger _logger = logger; - /// public async Task Handle(UpdateMerchantCommand request, CancellationToken cancellationToken) { // 1. 读取现有商户 - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken); + var tenantId = tenantProvider.GetCurrentTenantId(); + var existing = await merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken); if (existing == null) { return null; @@ -41,9 +37,9 @@ public sealed class UpdateMerchantCommandHandler( existing.Status = request.Status; // 3. 持久化 - await _merchantRepository.UpdateMerchantAsync(existing, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("更新商户 {MerchantId} - {BrandName}", existing.Id, existing.BrandName); + await merchantRepository.UpdateMerchantAsync(existing, cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); + logger.LogInformation("更新商户 {MerchantId} - {BrandName}", existing.Id, existing.BrandName); // 4. 返回 DTO return MapToDto(existing); diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantContractStatusCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantContractStatusCommandHandler.cs index 5e368f8..2d2ba95 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantContractStatusCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/UpdateMerchantContractStatusCommandHandler.cs @@ -20,16 +20,14 @@ public sealed class UpdateMerchantContractStatusCommandHandler( ICurrentUserAccessor currentUserAccessor) : IRequestHandler { - private readonly IMerchantRepository _merchantRepository = merchantRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ICurrentUserAccessor _currentUserAccessor = currentUserAccessor; - public async Task Handle(UpdateMerchantContractStatusCommand request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var contract = await _merchantRepository.FindContractByIdAsync(request.MerchantId, tenantId, request.ContractId, cancellationToken) + // 1. 查询合同 + var tenantId = tenantProvider.GetCurrentTenantId(); + var contract = await merchantRepository.FindContractByIdAsync(request.MerchantId, tenantId, request.ContractId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "合同不存在"); + // 2. 更新状态 if (request.Status == ContractStatus.Active) { contract.Status = ContractStatus.Active; @@ -46,8 +44,9 @@ public sealed class UpdateMerchantContractStatusCommandHandler( contract.Status = request.Status; } - await _merchantRepository.UpdateContractAsync(contract, cancellationToken); - await _merchantRepository.AddAuditLogAsync(new MerchantAuditLog + // 3. 持久化与审计 + await merchantRepository.UpdateContractAsync(contract, cancellationToken); + await merchantRepository.AddAuditLogAsync(new MerchantAuditLog { TenantId = tenantId, MerchantId = contract.MerchantId, @@ -58,19 +57,21 @@ public sealed class UpdateMerchantContractStatusCommandHandler( OperatorName = ResolveOperatorName() }, cancellationToken); - await _merchantRepository.SaveChangesAsync(cancellationToken); + await merchantRepository.SaveChangesAsync(cancellationToken); + + // 4. 返回 DTO return MerchantMapping.ToDto(contract); } private long? ResolveOperatorId() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? null : id; } private string ResolveOperatorName() { - var id = _currentUserAccessor.UserId; + var id = currentUserAccessor.UserId; return id == 0 ? "system" : $"user:{id}"; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/CreateOrderCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/CreateOrderCommandHandler.cs index a7b1289..8d38f2c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/CreateOrderCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/CreateOrderCommandHandler.cs @@ -17,17 +17,13 @@ public sealed class CreateOrderCommandHandler( ILogger logger) : IRequestHandler { - private readonly IOrderRepository _orderRepository = orderRepository; - private readonly IIdGenerator _idGenerator = idGenerator; - private readonly ILogger _logger = logger; - /// public async Task Handle(CreateOrderCommand request, CancellationToken cancellationToken) { // 1. 构建订单 var order = new Order { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), OrderNo = request.OrderNo.Trim(), StoreId = request.StoreId, Channel = request.Channel, @@ -77,15 +73,17 @@ public sealed class CreateOrderCommandHandler( } // 4. 持久化 - await _orderRepository.AddOrderAsync(order, cancellationToken); + await orderRepository.AddOrderAsync(order, cancellationToken); if (items.Count > 0) { - await _orderRepository.AddItemsAsync(items, cancellationToken); + await orderRepository.AddItemsAsync(items, cancellationToken); } - await _orderRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("创建订单 {OrderNo} ({OrderId})", order.OrderNo, order.Id); + await orderRepository.SaveChangesAsync(cancellationToken); - // 5. 返回 DTO + // 5. 记录日志 + logger.LogInformation("创建订单 {OrderNo} ({OrderId})", order.OrderNo, order.Id); + + // 6. 返回 DTO return MapToDto(order, items, [], []); } diff --git a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/DeleteOrderCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/DeleteOrderCommandHandler.cs index d376e47..77e6e95 100644 --- a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/DeleteOrderCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/DeleteOrderCommandHandler.cs @@ -15,26 +15,25 @@ public sealed class DeleteOrderCommandHandler( ILogger logger) : IRequestHandler { - private readonly IOrderRepository _orderRepository = orderRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ILogger _logger = logger; - /// public async Task Handle(DeleteOrderCommand request, CancellationToken cancellationToken) { // 1. 校验存在性 - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _orderRepository.FindByIdAsync(request.OrderId, tenantId, cancellationToken); + var tenantId = tenantProvider.GetCurrentTenantId(); + var existing = await orderRepository.FindByIdAsync(request.OrderId, tenantId, cancellationToken); if (existing == null) { return false; } // 2. 删除 - await _orderRepository.DeleteOrderAsync(request.OrderId, tenantId, cancellationToken); - await _orderRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("删除订单 {OrderId}", request.OrderId); + await orderRepository.DeleteOrderAsync(request.OrderId, tenantId, cancellationToken); + await orderRepository.SaveChangesAsync(cancellationToken); + // 3. 记录日志 + logger.LogInformation("删除订单 {OrderId}", request.OrderId); + + // 4. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/GetOrderByIdQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/GetOrderByIdQueryHandler.cs index 5d6b791..3a45377 100644 --- a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/GetOrderByIdQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/GetOrderByIdQueryHandler.cs @@ -15,23 +15,25 @@ public sealed class GetOrderByIdQueryHandler( ITenantProvider tenantProvider) : IRequestHandler { - private readonly IOrderRepository _orderRepository = orderRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task Handle(GetOrderByIdQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var order = await _orderRepository.FindByIdAsync(request.OrderId, tenantId, cancellationToken); + // 1. 获取当前租户 + var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 查询订单主体 + var order = await orderRepository.FindByIdAsync(request.OrderId, tenantId, cancellationToken); if (order == null) { return null; } - var items = await _orderRepository.GetItemsAsync(order.Id, tenantId, cancellationToken); - var histories = await _orderRepository.GetStatusHistoryAsync(order.Id, tenantId, cancellationToken); - var refunds = await _orderRepository.GetRefundsAsync(order.Id, tenantId, cancellationToken); + // 3. 查询关联明细 + var items = await orderRepository.GetItemsAsync(order.Id, tenantId, cancellationToken); + var histories = await orderRepository.GetStatusHistoryAsync(order.Id, tenantId, cancellationToken); + var refunds = await orderRepository.GetRefundsAsync(order.Id, tenantId, cancellationToken); + // 4. 映射并返回 return MapToDto(order, items, histories, refunds); } diff --git a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/SearchOrdersQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/SearchOrdersQueryHandler.cs index 867332b..90844c1 100644 --- a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/SearchOrdersQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/SearchOrdersQueryHandler.cs @@ -15,20 +15,20 @@ public sealed class SearchOrdersQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IOrderRepository _orderRepository = orderRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task> Handle(SearchOrdersQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var orders = await _orderRepository.SearchAsync(tenantId, request.Status, request.PaymentStatus, cancellationToken); + // 1. 获取当前租户并查询订单 + var tenantId = tenantProvider.GetCurrentTenantId(); + var orders = await orderRepository.SearchAsync(tenantId, request.Status, request.PaymentStatus, cancellationToken); + // 2. 可选过滤:门店 if (request.StoreId.HasValue) { orders = orders.Where(x => x.StoreId == request.StoreId.Value).ToList(); } + // 3. 可选过滤:订单号模糊 if (!string.IsNullOrWhiteSpace(request.OrderNo)) { var orderNo = request.OrderNo.Trim(); @@ -37,12 +37,14 @@ public sealed class SearchOrdersQueryHandler( .ToList(); } + // 4. 排序与分页 var sorted = ApplySorting(orders, request.SortBy, request.SortDescending); var paged = sorted .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); + // 5. 映射 DTO var items = paged.Select(order => new OrderDto { Id = order.Id, @@ -70,6 +72,7 @@ public sealed class SearchOrdersQueryHandler( CreatedAt = order.CreatedAt }).ToList(); + // 6. 返回分页结果 return new PagedResult(items, request.Page, request.PageSize, orders.Count); } diff --git a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/UpdateOrderCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/UpdateOrderCommandHandler.cs index 46df4ed..53f6c88 100644 --- a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/UpdateOrderCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/UpdateOrderCommandHandler.cs @@ -17,16 +17,12 @@ public sealed class UpdateOrderCommandHandler( ILogger logger) : IRequestHandler { - private readonly IOrderRepository _orderRepository = orderRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - private readonly ILogger _logger = logger; - /// public async Task Handle(UpdateOrderCommand request, CancellationToken cancellationToken) { // 1. 读取订单 - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _orderRepository.FindByIdAsync(request.OrderId, tenantId, cancellationToken); + var tenantId = tenantProvider.GetCurrentTenantId(); + var existing = await orderRepository.FindByIdAsync(request.OrderId, tenantId, cancellationToken); if (existing == null) { return null; @@ -55,14 +51,16 @@ public sealed class UpdateOrderCommandHandler( existing.Remark = request.Remark?.Trim(); // 3. 持久化 - await _orderRepository.UpdateOrderAsync(existing, cancellationToken); - await _orderRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("更新订单 {OrderNo} ({OrderId})", existing.OrderNo, existing.Id); + await orderRepository.UpdateOrderAsync(existing, cancellationToken); + await orderRepository.SaveChangesAsync(cancellationToken); - // 4. 读取关联数据并返回 - var items = await _orderRepository.GetItemsAsync(existing.Id, tenantId, cancellationToken); - var histories = await _orderRepository.GetStatusHistoryAsync(existing.Id, tenantId, cancellationToken); - var refunds = await _orderRepository.GetRefundsAsync(existing.Id, tenantId, cancellationToken); + // 4. 记录更新日志 + logger.LogInformation("更新订单 {OrderNo} ({OrderId})", existing.OrderNo, existing.Id); + + // 5. 读取关联数据并返回 + var items = await orderRepository.GetItemsAsync(existing.Id, tenantId, cancellationToken); + var histories = await orderRepository.GetStatusHistoryAsync(existing.Id, tenantId, cancellationToken); + var refunds = await orderRepository.GetRefundsAsync(existing.Id, tenantId, cancellationToken); return MapToDto(existing, items, histories, refunds); } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ChangeTenantSubscriptionPlanCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ChangeTenantSubscriptionPlanCommandHandler.cs index bc7f60f..de73ccf 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ChangeTenantSubscriptionPlanCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ChangeTenantSubscriptionPlanCommandHandler.cs @@ -18,20 +18,19 @@ public sealed class ChangeTenantSubscriptionPlanCommandHandler( IIdGenerator idGenerator) : IRequestHandler { - private readonly ITenantRepository _tenantRepository = tenantRepository; - private readonly IIdGenerator _idGenerator = idGenerator; - /// public async Task Handle(ChangeTenantSubscriptionPlanCommand request, CancellationToken cancellationToken) { - _ = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) + // 1. 校验租户与订阅存在性 + _ = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); - var subscription = await _tenantRepository.FindSubscriptionByIdAsync(request.TenantId, request.TenantSubscriptionId, cancellationToken) + var subscription = await tenantRepository.FindSubscriptionByIdAsync(request.TenantId, request.TenantSubscriptionId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "订阅不存在"); var previousPackage = subscription.TenantPackageId; + // 2. 根据立即生效或排期设置目标套餐 if (request.Immediate) { subscription.TenantPackageId = request.TargetPackageId; @@ -42,10 +41,11 @@ public sealed class ChangeTenantSubscriptionPlanCommandHandler( subscription.ScheduledPackageId = request.TargetPackageId; } - await _tenantRepository.UpdateSubscriptionAsync(subscription, cancellationToken); - await _tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory + // 3. 更新订阅并记录变更历史 + await tenantRepository.UpdateSubscriptionAsync(subscription, cancellationToken); + await tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), TenantId = subscription.TenantId, TenantSubscriptionId = subscription.Id, FromPackageId = previousPackage, @@ -56,7 +56,8 @@ public sealed class ChangeTenantSubscriptionPlanCommandHandler( Notes = request.Notes }, cancellationToken); - await _tenantRepository.AddAuditLogAsync(new TenantAuditLog + // 4. 记录审计日志 + await tenantRepository.AddAuditLogAsync(new TenantAuditLog { TenantId = subscription.TenantId, Action = TenantAuditAction.SubscriptionPlanChanged, @@ -66,7 +67,8 @@ public sealed class ChangeTenantSubscriptionPlanCommandHandler( CurrentStatus = null }, cancellationToken); - await _tenantRepository.SaveChangesAsync(cancellationToken); + // 5. 保存并返回 DTO + await tenantRepository.SaveChangesAsync(cancellationToken); return subscription.ToSubscriptionDto() ?? throw new BusinessException(ErrorCodes.InternalServerError, "订阅更新失败"); diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CheckTenantQuotaCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CheckTenantQuotaCommandHandler.cs index 77cae64..5fc67aa 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CheckTenantQuotaCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CheckTenantQuotaCommandHandler.cs @@ -24,18 +24,20 @@ public sealed class CheckTenantQuotaCommandHandler( /// public async Task Handle(CheckTenantQuotaCommand request, CancellationToken cancellationToken) { + // 1. 校验请求参数 if (request.Delta <= 0) { throw new BusinessException(ErrorCodes.BadRequest, "配额消耗量必须大于 0"); } + // 2. 校验租户上下文 var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId == 0 || currentTenantId != request.TenantId) { throw new BusinessException(ErrorCodes.Forbidden, "租户上下文不匹配,请在请求头 X-Tenant-Id 指定目标租户"); } - // 1. 获取租户与当前订阅。 + // 3. 获取租户与当前订阅 _ = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); @@ -50,7 +52,7 @@ public sealed class CheckTenantQuotaCommandHandler( var limit = ResolveLimit(package, request.QuotaType); - // 2. 加载配额使用记录并计算。 + // 4. 加载配额使用记录并计算 var usage = await quotaUsageRepository.FindAsync(request.TenantId, request.QuotaType, cancellationToken) ?? new TenantQuotaUsage { @@ -69,12 +71,14 @@ public sealed class CheckTenantQuotaCommandHandler( throw new BusinessException(ErrorCodes.Conflict, $"{request.QuotaType} 配额不足"); } + // 5. 更新使用并保存 usage.LimitValue = limit ?? usage.LimitValue; usage.UsedValue = usedAfter; usage.ResetCycle ??= ResolveResetCycle(request.QuotaType); await PersistUsageAsync(usage, quotaUsageRepository, cancellationToken); + // 6. 返回结果 return new QuotaCheckResultDto { QuotaType = request.QuotaType, diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAnnouncementCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAnnouncementCommandHandler.cs index ba48bb6..4894acb 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAnnouncementCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantAnnouncementCommandHandler.cs @@ -16,11 +16,13 @@ public sealed class CreateTenantAnnouncementCommandHandler(ITenantAnnouncementRe { public async Task Handle(CreateTenantAnnouncementCommand request, CancellationToken cancellationToken) { + // 1. 校验标题与内容 if (string.IsNullOrWhiteSpace(request.Title) || string.IsNullOrWhiteSpace(request.Content)) { throw new BusinessException(ErrorCodes.BadRequest, "公告标题和内容不能为空"); } + // 2. 构建公告实体 var announcement = new TenantAnnouncement { TenantId = request.TenantId, @@ -33,6 +35,7 @@ public sealed class CreateTenantAnnouncementCommandHandler(ITenantAnnouncementRe IsActive = request.IsActive }; + // 3. 持久化并返回 DTO await announcementRepository.AddAsync(announcement, cancellationToken); await announcementRepository.SaveChangesAsync(cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantBillingCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantBillingCommandHandler.cs index f07f889..42b629c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantBillingCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantBillingCommandHandler.cs @@ -16,11 +16,13 @@ public sealed class CreateTenantBillingCommandHandler(ITenantBillingRepository b { public async Task Handle(CreateTenantBillingCommand request, CancellationToken cancellationToken) { + // 1. 校验账单编号 if (string.IsNullOrWhiteSpace(request.StatementNo)) { throw new BusinessException(ErrorCodes.BadRequest, "账单编号不能为空"); } + // 2. 构建账单实体 var bill = new TenantBillingStatement { TenantId = request.TenantId, @@ -34,9 +36,11 @@ public sealed class CreateTenantBillingCommandHandler(ITenantBillingRepository b LineItemsJson = request.LineItemsJson }; + // 3. 持久化账单 await billingRepository.AddAsync(bill, cancellationToken); await billingRepository.SaveChangesAsync(cancellationToken); + // 4. 返回 DTO return bill.ToDto(); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantPackageCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantPackageCommandHandler.cs index bc8cd90..c14a993 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantPackageCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantPackageCommandHandler.cs @@ -17,11 +17,13 @@ public sealed class CreateTenantPackageCommandHandler(ITenantPackageRepository p /// public async Task Handle(CreateTenantPackageCommand request, CancellationToken cancellationToken) { + // 1. 校验套餐名称 if (string.IsNullOrWhiteSpace(request.Name)) { throw new BusinessException(ErrorCodes.BadRequest, "套餐名称不能为空"); } + // 2. 构建套餐实体 var package = new TenantPackage { Name = request.Name.Trim(), @@ -38,6 +40,7 @@ public sealed class CreateTenantPackageCommandHandler(ITenantPackageRepository p IsActive = request.IsActive }; + // 3. 持久化并返回 await packageRepository.AddAsync(package, cancellationToken); await packageRepository.SaveChangesAsync(cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantSubscriptionCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantSubscriptionCommandHandler.cs index df52305..4d59ffe 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantSubscriptionCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantSubscriptionCommandHandler.cs @@ -18,28 +18,28 @@ public sealed class CreateTenantSubscriptionCommandHandler( IIdGenerator idGenerator) : IRequestHandler { - private readonly ITenantRepository _tenantRepository = tenantRepository; - private readonly IIdGenerator _idGenerator = idGenerator; - /// public async Task Handle(CreateTenantSubscriptionCommand request, CancellationToken cancellationToken) { + // 1. 校验订阅时长 if (request.DurationMonths <= 0) { throw new BusinessException(ErrorCodes.BadRequest, "订阅时长必须大于 0"); } - var tenant = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) + // 2. 获取租户与当前订阅 + var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); - var current = await _tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken); + var current = await tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken); var from = current?.EffectiveTo ?? tenant.EffectiveTo ?? DateTime.UtcNow; var effectiveFrom = from > DateTime.UtcNow ? from : DateTime.UtcNow; var effectiveTo = effectiveFrom.AddMonths(request.DurationMonths); + // 3. 创建订阅实体 var subscription = new TenantSubscription { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), TenantId = tenant.Id, TenantPackageId = request.TenantPackageId, EffectiveFrom = effectiveFrom, @@ -50,10 +50,11 @@ public sealed class CreateTenantSubscriptionCommandHandler( Notes = request.Notes }; - await _tenantRepository.AddSubscriptionAsync(subscription, cancellationToken); - await _tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory + // 4. 记录订阅与历史 + await tenantRepository.AddSubscriptionAsync(subscription, cancellationToken); + await tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), TenantId = tenant.Id, TenantSubscriptionId = subscription.Id, FromPackageId = current?.TenantPackageId ?? request.TenantPackageId, @@ -66,7 +67,8 @@ public sealed class CreateTenantSubscriptionCommandHandler( Notes = request.Notes }, cancellationToken); - await _tenantRepository.AddAuditLogAsync(new TenantAuditLog + // 5. 记录审计 + await tenantRepository.AddAuditLogAsync(new TenantAuditLog { TenantId = tenant.Id, Action = TenantAuditAction.SubscriptionUpdated, @@ -74,8 +76,10 @@ public sealed class CreateTenantSubscriptionCommandHandler( Description = $"套餐 {request.TenantPackageId} 时长 {request.DurationMonths} 月" }, cancellationToken); - await _tenantRepository.SaveChangesAsync(cancellationToken); + // 6. 保存变更 + await tenantRepository.SaveChangesAsync(cancellationToken); + // 7. 返回 DTO return subscription.ToSubscriptionDto() ?? throw new BusinessException(ErrorCodes.InternalServerError, "订阅生成失败"); } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantAnnouncementCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantAnnouncementCommandHandler.cs index 5464299..d479dfd 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantAnnouncementCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantAnnouncementCommandHandler.cs @@ -12,8 +12,11 @@ public sealed class DeleteTenantAnnouncementCommandHandler(ITenantAnnouncementRe { public async Task Handle(DeleteTenantAnnouncementCommand request, CancellationToken cancellationToken) { + // 1. 删除公告 await announcementRepository.DeleteAsync(request.TenantId, request.AnnouncementId, cancellationToken); await announcementRepository.SaveChangesAsync(cancellationToken); + + // 2. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantPackageCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantPackageCommandHandler.cs index 232ea5e..c0f6fb4 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantPackageCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/DeleteTenantPackageCommandHandler.cs @@ -13,8 +13,11 @@ public sealed class DeleteTenantPackageCommandHandler(ITenantPackageRepository p /// public async Task Handle(DeleteTenantPackageCommand request, CancellationToken cancellationToken) { + // 1. 删除套餐 await packageRepository.DeleteAsync(request.TenantPackageId, cancellationToken); await packageRepository.SaveChangesAsync(cancellationToken); + + // 2. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAnnouncementQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAnnouncementQueryHandler.cs index 70c6201..90fa4b1 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAnnouncementQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAnnouncementQueryHandler.cs @@ -17,12 +17,14 @@ public sealed class GetTenantAnnouncementQueryHandler( { public async Task Handle(GetTenantAnnouncementQuery request, CancellationToken cancellationToken) { + // 1. 查询公告主体 var announcement = await announcementRepository.FindByIdAsync(request.TenantId, request.AnnouncementId, cancellationToken); if (announcement == null) { return null; } + // 2. 优先查用户级已读 var userId = currentUserAccessor?.UserId ?? 0; var reads = await readRepository.GetByAnnouncementAsync( request.TenantId, @@ -37,6 +39,7 @@ public sealed class GetTenantAnnouncementQueryHandler( reads = tenantReads; } + // 3. 返回 DTO 并附带已读状态 var readRecord = reads.FirstOrDefault(); return announcement.ToDto(readRecord != null, readRecord?.ReadAt); } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAuditLogsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAuditLogsQueryHandler.cs index bfa51ee..41d308f 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAuditLogsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAuditLogsQueryHandler.cs @@ -13,20 +13,21 @@ namespace TakeoutSaaS.Application.App.Tenants.Handlers; public sealed class GetTenantAuditLogsQueryHandler(ITenantRepository tenantRepository) : IRequestHandler> { - private readonly ITenantRepository _tenantRepository = tenantRepository; - /// public async Task> Handle(GetTenantAuditLogsQuery request, CancellationToken cancellationToken) { - var logs = await _tenantRepository.GetAuditLogsAsync(request.TenantId, cancellationToken); + // 1. 查询审核日志 + var logs = await tenantRepository.GetAuditLogsAsync(request.TenantId, cancellationToken); var total = logs.Count; + // 2. 分页映射 var paged = logs .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .Select(TenantMapping.ToDto) .ToList(); + // 3. 返回分页结果 return new PagedResult(paged, request.Page, request.PageSize, total); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantBillQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantBillQueryHandler.cs index c3e96d6..5dbd0e2 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantBillQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantBillQueryHandler.cs @@ -13,7 +13,10 @@ public sealed class GetTenantBillQueryHandler(ITenantBillingRepository billingRe { public async Task Handle(GetTenantBillQuery request, CancellationToken cancellationToken) { + // 1. 查询账单 var bill = await billingRepository.FindByIdAsync(request.TenantId, request.BillingId, cancellationToken); + + // 2. 返回 DTO 或 null return bill?.ToDto(); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantByIdQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantByIdQueryHandler.cs index f3cd41b..c4f89f9 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantByIdQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantByIdQueryHandler.cs @@ -13,17 +13,18 @@ namespace TakeoutSaaS.Application.App.Tenants.Handlers; public sealed class GetTenantByIdQueryHandler(ITenantRepository tenantRepository) : IRequestHandler { - private readonly ITenantRepository _tenantRepository = tenantRepository; - /// public async Task Handle(GetTenantByIdQuery request, CancellationToken cancellationToken) { - var tenant = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) + // 1. 查询租户 + var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); - var subscription = await _tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken); - var verification = await _tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken); + // 2. 查询订阅与认证 + var subscription = await tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken); + var verification = await tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken); + // 3. 组装返回 return new TenantDetailDto { Tenant = TenantMapping.ToDto(tenant, subscription, verification), diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageByIdQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageByIdQueryHandler.cs index ac6edac..4b6b898 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageByIdQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageByIdQueryHandler.cs @@ -14,7 +14,10 @@ public sealed class GetTenantPackageByIdQueryHandler(ITenantPackageRepository pa /// public async Task Handle(GetTenantPackageByIdQuery request, CancellationToken cancellationToken) { + // 1. 查询套餐 var package = await packageRepository.FindByIdAsync(request.TenantPackageId, cancellationToken); + + // 2. 返回 DTO 或 null return package?.ToDto(); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantAnnouncementReadCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantAnnouncementReadCommandHandler.cs index 63f9604..8c0a06b 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantAnnouncementReadCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantAnnouncementReadCommandHandler.cs @@ -20,15 +20,18 @@ public sealed class MarkTenantAnnouncementReadCommandHandler( { public async Task Handle(MarkTenantAnnouncementReadCommand request, CancellationToken cancellationToken) { + // 1. 查询公告 var announcement = await announcementRepository.FindByIdAsync(request.TenantId, request.AnnouncementId, cancellationToken); if (announcement == null) { return null; } + // 2. 确定用户标识 var userId = currentUserAccessor?.UserId ?? 0; var existing = await readRepository.FindAsync(request.TenantId, request.AnnouncementId, userId == 0 ? null : userId, cancellationToken); + // 3. 如未读则写入已读记录 if (existing == null) { var record = new TenantAnnouncementRead @@ -44,6 +47,7 @@ public sealed class MarkTenantAnnouncementReadCommandHandler( existing = record; } + // 4. 返回带已读时间的公告 DTO return announcement.ToDto(true, existing.ReadAt); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantBillingPaidCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantBillingPaidCommandHandler.cs index 3d708af..6727959 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantBillingPaidCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantBillingPaidCommandHandler.cs @@ -14,19 +14,23 @@ public sealed class MarkTenantBillingPaidCommandHandler(ITenantBillingRepository { public async Task Handle(MarkTenantBillingPaidCommand request, CancellationToken cancellationToken) { + // 1. 查询账单 var bill = await billingRepository.FindByIdAsync(request.TenantId, request.BillingId, cancellationToken); if (bill == null) { return null; } + // 2. 更新支付状态 bill.AmountPaid = request.AmountPaid; bill.Status = TenantBillingStatus.Paid; bill.DueDate = bill.DueDate; + // 3. 持久化变更 await billingRepository.UpdateAsync(bill, cancellationToken); await billingRepository.SaveChangesAsync(cancellationToken); + // 4. 返回 DTO return bill.ToDto(); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantNotificationReadCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantNotificationReadCommandHandler.cs index 48a8400..1d91e66 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantNotificationReadCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantNotificationReadCommandHandler.cs @@ -13,12 +13,14 @@ public sealed class MarkTenantNotificationReadCommandHandler(ITenantNotification { public async Task Handle(MarkTenantNotificationReadCommand request, CancellationToken cancellationToken) { + // 1. 查询通知 var notification = await notificationRepository.FindByIdAsync(request.TenantId, request.NotificationId, cancellationToken); if (notification == null) { return null; } + // 2. 若未读则标记已读 if (notification.ReadAt == null) { notification.ReadAt = DateTime.UtcNow; @@ -26,6 +28,7 @@ public sealed class MarkTenantNotificationReadCommandHandler(ITenantNotification await notificationRepository.SaveChangesAsync(cancellationToken); } + // 3. 返回 DTO return notification.ToDto(); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/RegisterTenantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/RegisterTenantCommandHandler.cs index 228bded..e236ab1 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/RegisterTenantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/RegisterTenantCommandHandler.cs @@ -20,30 +20,30 @@ public sealed class RegisterTenantCommandHandler( ILogger logger) : IRequestHandler { - private readonly ITenantRepository _tenantRepository = tenantRepository; - private readonly IIdGenerator _idGenerator = idGenerator; - private readonly ILogger _logger = logger; - /// public async Task Handle(RegisterTenantCommand request, CancellationToken cancellationToken) { + // 1. 校验订阅时长 if (request.DurationMonths <= 0) { throw new BusinessException(ErrorCodes.BadRequest, "订阅时长必须大于 0"); } - if (await _tenantRepository.ExistsByCodeAsync(request.Code, cancellationToken)) + // 2. 检查租户编码唯一性 + if (await tenantRepository.ExistsByCodeAsync(request.Code, cancellationToken)) { throw new BusinessException(ErrorCodes.Conflict, $"租户编码 {request.Code} 已存在"); } + // 3. 计算生效时间 var now = DateTime.UtcNow; var effectiveFrom = request.EffectiveFrom ?? now; var effectiveTo = effectiveFrom.AddMonths(request.DurationMonths); + // 4. 构建租户实体 var tenant = new Tenant { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), Code = request.Code.Trim(), Name = request.Name, ShortName = request.ShortName, @@ -56,9 +56,10 @@ public sealed class RegisterTenantCommandHandler( EffectiveTo = effectiveTo }; + // 5. 构建订阅实体 var subscription = new TenantSubscription { - Id = _idGenerator.NextId(), + Id = idGenerator.NextId(), TenantId = tenant.Id, TenantPackageId = request.TenantPackageId, EffectiveFrom = effectiveFrom, @@ -69,9 +70,10 @@ public sealed class RegisterTenantCommandHandler( Notes = "Init subscription" }; - await _tenantRepository.AddTenantAsync(tenant, cancellationToken); - await _tenantRepository.AddSubscriptionAsync(subscription, cancellationToken); - await _tenantRepository.AddAuditLogAsync(new TenantAuditLog + // 6. 持久化租户、订阅和审计日志 + await tenantRepository.AddTenantAsync(tenant, cancellationToken); + await tenantRepository.AddSubscriptionAsync(subscription, cancellationToken); + await tenantRepository.AddAuditLogAsync(new TenantAuditLog { TenantId = tenant.Id, Action = TenantAuditAction.RegistrationSubmitted, @@ -79,10 +81,12 @@ public sealed class RegisterTenantCommandHandler( Description = $"提交套餐 {request.TenantPackageId},时长 {request.DurationMonths} 月" }, cancellationToken); - await _tenantRepository.SaveChangesAsync(cancellationToken); + await tenantRepository.SaveChangesAsync(cancellationToken); - _logger.LogInformation("已注册租户 {TenantCode}", tenant.Code); + // 7. 记录日志 + logger.LogInformation("已注册租户 {TenantCode}", tenant.Code); + // 8. 返回 DTO return TenantMapping.ToDto(tenant, subscription, null); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ReviewTenantCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ReviewTenantCommandHandler.cs index 97df442..022d1e1 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ReviewTenantCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/ReviewTenantCommandHandler.cs @@ -17,31 +17,32 @@ public sealed class ReviewTenantCommandHandler( ICurrentUserAccessor currentUserAccessor) : IRequestHandler { - private readonly ITenantRepository _tenantRepository = tenantRepository; - private readonly ICurrentUserAccessor _currentUserAccessor = currentUserAccessor; - /// public async Task Handle(ReviewTenantCommand request, CancellationToken cancellationToken) { - var tenant = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) + // 1. 获取租户与认证资料 + var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); - var verification = await _tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken) + var verification = await tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.BadRequest, "请先提交实名认证资料"); - var subscription = await _tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken); + var subscription = await tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken); - var actorName = _currentUserAccessor.IsAuthenticated - ? $"user:{_currentUserAccessor.UserId}" + // 2. 记录审核人 + var actorName = currentUserAccessor.IsAuthenticated + ? $"user:{currentUserAccessor.UserId}" : "system"; + // 3. 写入审核信息 verification.ReviewedAt = DateTime.UtcNow; - verification.ReviewedBy = _currentUserAccessor.UserId == 0 ? null : _currentUserAccessor.UserId; + verification.ReviewedBy = currentUserAccessor.UserId == 0 ? null : currentUserAccessor.UserId; verification.ReviewedByName = actorName; verification.ReviewRemarks = request.Reason; var previousStatus = tenant.Status; + // 4. 更新租户与订阅状态 if (request.Approve) { verification.Status = TenantVerificationStatus.Approved; @@ -61,26 +62,29 @@ public sealed class ReviewTenantCommandHandler( } } - await _tenantRepository.UpdateTenantAsync(tenant, cancellationToken); - await _tenantRepository.UpsertVerificationProfileAsync(verification, cancellationToken); + // 5. 持久化租户与认证资料 + await tenantRepository.UpdateTenantAsync(tenant, cancellationToken); + await tenantRepository.UpsertVerificationProfileAsync(verification, cancellationToken); if (subscription != null) { - await _tenantRepository.UpdateSubscriptionAsync(subscription, cancellationToken); + await tenantRepository.UpdateSubscriptionAsync(subscription, cancellationToken); } - await _tenantRepository.AddAuditLogAsync(new Domain.Tenants.Entities.TenantAuditLog + // 6. 记录审核日志 + await tenantRepository.AddAuditLogAsync(new Domain.Tenants.Entities.TenantAuditLog { TenantId = tenant.Id, Action = request.Approve ? TenantAuditAction.VerificationApproved : TenantAuditAction.VerificationRejected, Title = request.Approve ? "审核通过" : "审核驳回", Description = request.Reason, - OperatorId = _currentUserAccessor.UserId == 0 ? null : _currentUserAccessor.UserId, + OperatorId = currentUserAccessor.UserId == 0 ? null : currentUserAccessor.UserId, OperatorName = actorName, PreviousStatus = previousStatus, CurrentStatus = tenant.Status }, cancellationToken); - await _tenantRepository.SaveChangesAsync(cancellationToken); + // 7. 保存并返回 DTO + await tenantRepository.SaveChangesAsync(cancellationToken); return TenantMapping.ToDto(tenant, subscription, verification); } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantAnnouncementsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantAnnouncementsQueryHandler.cs index 1734531..be2b952 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantAnnouncementsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantAnnouncementsQueryHandler.cs @@ -20,22 +20,27 @@ public sealed class SearchTenantAnnouncementsQueryHandler( { public async Task> Handle(SearchTenantAnnouncementsQuery request, CancellationToken cancellationToken) { + // 1. 过滤有效期条件 var effectiveAt = request.OnlyEffective == true ? DateTime.UtcNow : (DateTime?)null; var announcements = await announcementRepository.SearchAsync(request.TenantId, request.AnnouncementType, request.IsActive, effectiveAt, cancellationToken); + // 2. 排序(优先级/时间) var ordered = announcements .OrderByDescending(x => x.Priority) .ThenByDescending(x => x.CreatedAt) .ToList(); + // 3. 计算分页参数 var page = request.Page <= 0 ? 1 : request.Page; var size = request.PageSize <= 0 ? 20 : request.PageSize; + // 4. 分页 var pageItems = ordered .Skip((page - 1) * size) .Take(size) .ToList(); + // 5. 构建已读映射 var announcementIds = pageItems.Select(x => x.Id).ToArray(); var userId = currentUserAccessor?.UserId ?? 0; @@ -65,6 +70,7 @@ public sealed class SearchTenantAnnouncementsQueryHandler( } } + // 6. 映射 DTO 并带上已读状态 var items = pageItems .Select(a => { @@ -73,6 +79,7 @@ public sealed class SearchTenantAnnouncementsQueryHandler( }) .ToList(); + // 7. 返回分页结果 return new PagedResult(items, page, size, ordered.Count); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantBillsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantBillsQueryHandler.cs index 5bd2ec7..05b11cb 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantBillsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantBillsQueryHandler.cs @@ -15,13 +15,16 @@ public sealed class SearchTenantBillsQueryHandler(ITenantBillingRepository billi { public async Task> Handle(SearchTenantBillsQuery request, CancellationToken cancellationToken) { + // 1. 查询账单 var bills = await billingRepository.SearchAsync(request.TenantId, request.Status, request.From, request.To, cancellationToken); + // 2. 排序与分页 var ordered = bills.OrderByDescending(x => x.PeriodEnd).ToList(); var page = request.Page <= 0 ? 1 : request.Page; var size = request.PageSize <= 0 ? 20 : request.PageSize; var items = ordered.Skip((page - 1) * size).Take(size).Select(x => x.ToDto()).ToList(); + // 3. 返回分页结果 return new PagedResult(items, page, size, ordered.Count); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantNotificationsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantNotificationsQueryHandler.cs index ec05cd6..bab5aca 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantNotificationsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantNotificationsQueryHandler.cs @@ -15,6 +15,7 @@ public sealed class SearchTenantNotificationsQueryHandler(ITenantNotificationRep { public async Task> Handle(SearchTenantNotificationsQuery request, CancellationToken cancellationToken) { + // 1. 查询通知 var notifications = await notificationRepository.SearchAsync( request.TenantId, request.Severity, @@ -23,11 +24,13 @@ public sealed class SearchTenantNotificationsQueryHandler(ITenantNotificationRep null, cancellationToken); + // 2. 排序与分页 var ordered = notifications.OrderByDescending(x => x.SentAt).ToList(); var page = request.Page <= 0 ? 1 : request.Page; var size = request.PageSize <= 0 ? 20 : request.PageSize; var items = ordered.Skip((page - 1) * size).Take(size).Select(x => x.ToDto()).ToList(); + // 3. 返回分页结果 return new PagedResult(items, page, size, ordered.Count); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantPackagesQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantPackagesQueryHandler.cs index 5b4993c..12a2318 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantPackagesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantPackagesQueryHandler.cs @@ -16,8 +16,10 @@ public sealed class SearchTenantPackagesQueryHandler(ITenantPackageRepository pa /// public async Task> Handle(SearchTenantPackagesQuery request, CancellationToken cancellationToken) { + // 1. 查询套餐 var packages = await packageRepository.SearchAsync(request.Keyword, request.IsActive, cancellationToken); + // 2. 排序与分页 var ordered = packages.OrderByDescending(x => x.CreatedAt).ToList(); var pageIndex = request.Page <= 0 ? 1 : request.Page; var size = request.PageSize <= 0 ? 20 : request.PageSize; @@ -28,6 +30,7 @@ public sealed class SearchTenantPackagesQueryHandler(ITenantPackageRepository pa .Select(x => x.ToDto()) .ToList(); + // 3. 返回分页结果 return new PagedResult(pagedItems, pageIndex, size, ordered.Count); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantsQueryHandler.cs index e13b9fc..2813e31 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantsQueryHandler.cs @@ -13,27 +13,29 @@ namespace TakeoutSaaS.Application.App.Tenants.Handlers; public sealed class SearchTenantsQueryHandler(ITenantRepository tenantRepository) : IRequestHandler> { - private readonly ITenantRepository _tenantRepository = tenantRepository; - /// public async Task> Handle(SearchTenantsQuery request, CancellationToken cancellationToken) { - var tenants = await _tenantRepository.SearchAsync(request.Status, request.Keyword, cancellationToken); + // 1. 查询租户列表 + var tenants = await tenantRepository.SearchAsync(request.Status, request.Keyword, cancellationToken); var total = tenants.Count; + // 2. 分页 var paged = tenants .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); + // 3. 映射 DTO(带订阅与认证) var result = new List(paged.Count); foreach (var tenant in paged) { - var subscription = await _tenantRepository.GetActiveSubscriptionAsync(tenant.Id, cancellationToken); - var verification = await _tenantRepository.GetVerificationProfileAsync(tenant.Id, cancellationToken); + var subscription = await tenantRepository.GetActiveSubscriptionAsync(tenant.Id, cancellationToken); + var verification = await tenantRepository.GetVerificationProfileAsync(tenant.Id, cancellationToken); result.Add(TenantMapping.ToDto(tenant, subscription, verification)); } + // 4. 返回分页结果 return new PagedResult(result, request.Page, request.PageSize, total); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SubmitTenantVerificationCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SubmitTenantVerificationCommandHandler.cs index dc6be8f..8771c72 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SubmitTenantVerificationCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SubmitTenantVerificationCommandHandler.cs @@ -18,18 +18,18 @@ public sealed class SubmitTenantVerificationCommandHandler( IIdGenerator idGenerator) : IRequestHandler { - private readonly ITenantRepository _tenantRepository = tenantRepository; - private readonly IIdGenerator _idGenerator = idGenerator; - /// public async Task Handle(SubmitTenantVerificationCommand request, CancellationToken cancellationToken) { - var tenant = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) + // 1. 获取租户 + var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); - var profile = await _tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken) - ?? new TenantVerificationProfile { Id = _idGenerator.NextId(), TenantId = tenant.Id }; + // 2. 读取或初始化实名资料 + var profile = await tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken) + ?? new TenantVerificationProfile { Id = idGenerator.NextId(), TenantId = tenant.Id }; + // 3. 填充资料 profile.BusinessLicenseNumber = request.BusinessLicenseNumber; profile.BusinessLicenseUrl = request.BusinessLicenseUrl; profile.LegalPersonName = request.LegalPersonName; @@ -47,16 +47,18 @@ public sealed class SubmitTenantVerificationCommandHandler( profile.ReviewedBy = null; profile.ReviewedByName = null; - await _tenantRepository.UpsertVerificationProfileAsync(profile, cancellationToken); - await _tenantRepository.AddAuditLogAsync(new TenantAuditLog + // 4. 保存资料并记录审计 + await tenantRepository.UpsertVerificationProfileAsync(profile, cancellationToken); + await tenantRepository.AddAuditLogAsync(new TenantAuditLog { TenantId = tenant.Id, Action = TenantAuditAction.VerificationSubmitted, Title = "提交实名认证资料", Description = request.BusinessLicenseNumber }, cancellationToken); - await _tenantRepository.SaveChangesAsync(cancellationToken); + await tenantRepository.SaveChangesAsync(cancellationToken); + // 5. 返回 DTO return profile.ToVerificationDto() ?? throw new BusinessException(ErrorCodes.InternalServerError, "实名资料保存失败"); } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantAnnouncementCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantAnnouncementCommandHandler.cs index ea76e80..c802631 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantAnnouncementCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantAnnouncementCommandHandler.cs @@ -15,17 +15,20 @@ public sealed class UpdateTenantAnnouncementCommandHandler(ITenantAnnouncementRe { public async Task Handle(UpdateTenantAnnouncementCommand request, CancellationToken cancellationToken) { + // 1. 校验输入 if (string.IsNullOrWhiteSpace(request.Title) || string.IsNullOrWhiteSpace(request.Content)) { throw new BusinessException(ErrorCodes.BadRequest, "公告标题和内容不能为空"); } + // 2. 查询公告 var announcement = await announcementRepository.FindByIdAsync(request.TenantId, request.AnnouncementId, cancellationToken); if (announcement == null) { return null; } + // 3. 更新字段 announcement.Title = request.Title.Trim(); announcement.Content = request.Content; announcement.AnnouncementType = request.AnnouncementType; @@ -34,9 +37,11 @@ public sealed class UpdateTenantAnnouncementCommandHandler(ITenantAnnouncementRe announcement.EffectiveTo = request.EffectiveTo; announcement.IsActive = request.IsActive; + // 4. 持久化 await announcementRepository.UpdateAsync(announcement, cancellationToken); await announcementRepository.SaveChangesAsync(cancellationToken); + // 5. 返回 DTO return announcement.ToDto(false, null); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantPackageCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantPackageCommandHandler.cs index 77a1664..7821bbf 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantPackageCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantPackageCommandHandler.cs @@ -16,17 +16,20 @@ public sealed class UpdateTenantPackageCommandHandler(ITenantPackageRepository p /// public async Task Handle(UpdateTenantPackageCommand request, CancellationToken cancellationToken) { + // 1. 校验必填项 if (string.IsNullOrWhiteSpace(request.Name)) { throw new BusinessException(ErrorCodes.BadRequest, "套餐名称不能为空"); } + // 2. 查询套餐 var package = await packageRepository.FindByIdAsync(request.TenantPackageId, cancellationToken); if (package == null) { return null; } + // 3. 更新字段 package.Name = request.Name.Trim(); package.Description = request.Description; package.PackageType = request.PackageType; @@ -40,6 +43,7 @@ public sealed class UpdateTenantPackageCommandHandler(ITenantPackageRepository p package.FeaturePoliciesJson = request.FeaturePoliciesJson; package.IsActive = request.IsActive; + // 4. 持久化并返回 await packageRepository.UpdateAsync(package, cancellationToken); await packageRepository.SaveChangesAsync(cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs index 5ff8e9a..1c8f628 100644 --- a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs +++ b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs @@ -21,18 +21,20 @@ public sealed class DictionaryAppService( ITenantProvider tenantProvider, ILogger logger) : IDictionaryAppService { - public async Task CreateGroupAsync(CreateDictionaryGroupRequest request, CancellationToken cancellationToken = default) { + // 1. 规范化编码并确定租户 var normalizedCode = NormalizeCode(request.Code); var targetTenant = ResolveTargetTenant(request.Scope); + // 2. 校验编码唯一 var existing = await repository.FindGroupByCodeAsync(normalizedCode, cancellationToken); if (existing != null) { throw new BusinessException(ErrorCodes.Conflict, $"字典分组编码 {normalizedCode} 已存在"); } + // 3. 构建分组实体 var group = new DictionaryGroup { Id = 0, @@ -44,6 +46,7 @@ public sealed class DictionaryAppService( IsEnabled = true }; + // 4. 持久化并返回 await repository.AddGroupAsync(group, cancellationToken); await repository.SaveChangesAsync(cancellationToken); logger.LogInformation("创建字典分组:{Code}({Scope})", group.Code, group.Scope); @@ -52,13 +55,16 @@ public sealed class DictionaryAppService( public async Task UpdateGroupAsync(long groupId, UpdateDictionaryGroupRequest request, CancellationToken cancellationToken = default) { + // 1. 读取分组并校验权限 var group = await RequireGroupAsync(groupId, cancellationToken); EnsureScopePermission(group.Scope); + // 2. 更新字段 group.Name = request.Name.Trim(); group.Description = request.Description?.Trim(); group.IsEnabled = request.IsEnabled; + // 3. 持久化并失效缓存 await repository.SaveChangesAsync(cancellationToken); await InvalidateCacheAsync(group, cancellationToken); logger.LogInformation("更新字典分组:{GroupId}", group.Id); @@ -67,9 +73,11 @@ public sealed class DictionaryAppService( public async Task DeleteGroupAsync(long groupId, CancellationToken cancellationToken = default) { + // 1. 读取分组并校验权限 var group = await RequireGroupAsync(groupId, cancellationToken); EnsureScopePermission(group.Scope); + // 2. 删除并失效缓存 await repository.RemoveGroupAsync(group, cancellationToken); await repository.SaveChangesAsync(cancellationToken); await InvalidateCacheAsync(group, cancellationToken); @@ -78,10 +86,12 @@ public sealed class DictionaryAppService( public async Task> SearchGroupsAsync(DictionaryGroupQuery request, CancellationToken cancellationToken = default) { + // 1. 确定查询范围并校验权限 var tenantId = tenantProvider.GetCurrentTenantId(); var scope = ResolveScopeForQuery(request.Scope, tenantId); EnsureScopePermission(scope); + // 2. 查询分组及可选项 var groups = await repository.SearchGroupsAsync(scope, cancellationToken); var includeItems = request.IncludeItems; var result = new List(groups.Count); @@ -91,6 +101,7 @@ public sealed class DictionaryAppService( IReadOnlyList items = Array.Empty(); if (includeItems) { + // 查询分组下字典项 var itemEntities = await repository.GetItemsByGroupIdAsync(group.Id, cancellationToken); items = itemEntities.Select(MapItem).ToList(); } @@ -103,9 +114,11 @@ public sealed class DictionaryAppService( public async Task CreateItemAsync(CreateDictionaryItemRequest request, CancellationToken cancellationToken = default) { + // 1. 校验分组与权限 var group = await RequireGroupAsync(request.GroupId, cancellationToken); EnsureScopePermission(group.Scope); + // 2. 构建字典项 var item = new DictionaryItem { Id = 0, @@ -119,6 +132,7 @@ public sealed class DictionaryAppService( IsEnabled = request.IsEnabled }; + // 3. 持久化并失效缓存 await repository.AddItemAsync(item, cancellationToken); await repository.SaveChangesAsync(cancellationToken); await InvalidateCacheAsync(group, cancellationToken); @@ -128,16 +142,19 @@ public sealed class DictionaryAppService( public async Task UpdateItemAsync(long itemId, UpdateDictionaryItemRequest request, CancellationToken cancellationToken = default) { + // 1. 读取字典项与分组并校验权限 var item = await RequireItemAsync(itemId, cancellationToken); var group = await RequireGroupAsync(item.GroupId, cancellationToken); EnsureScopePermission(group.Scope); + // 2. 更新字段 item.Value = request.Value.Trim(); item.Description = request.Description?.Trim(); item.SortOrder = request.SortOrder; item.IsDefault = request.IsDefault; item.IsEnabled = request.IsEnabled; + // 3. 持久化并失效缓存 await repository.SaveChangesAsync(cancellationToken); await InvalidateCacheAsync(group, cancellationToken); logger.LogInformation("更新字典项:{ItemId}", item.Id); @@ -146,10 +163,12 @@ public sealed class DictionaryAppService( public async Task DeleteItemAsync(long itemId, CancellationToken cancellationToken = default) { + // 1. 读取字典项与分组并校验权限 var item = await RequireItemAsync(itemId, cancellationToken); var group = await RequireGroupAsync(item.GroupId, cancellationToken); EnsureScopePermission(group.Scope); + // 2. 删除并失效缓存 await repository.RemoveItemAsync(item, cancellationToken); await repository.SaveChangesAsync(cancellationToken); await InvalidateCacheAsync(group, cancellationToken); @@ -158,6 +177,7 @@ public sealed class DictionaryAppService( public async Task>> GetCachedItemsAsync(DictionaryBatchQueryRequest request, CancellationToken cancellationToken = default) { + // 1. 规范化编码 var normalizedCodes = request.Codes .Where(code => !string.IsNullOrWhiteSpace(code)) .Select(NormalizeCode) @@ -169,6 +189,7 @@ public sealed class DictionaryAppService( return new Dictionary>(StringComparer.OrdinalIgnoreCase); } + // 2. 按租户合并系统与业务字典 var tenantId = tenantProvider.GetCurrentTenantId(); var result = new Dictionary>(StringComparer.OrdinalIgnoreCase); @@ -190,6 +211,7 @@ public sealed class DictionaryAppService( private async Task RequireGroupAsync(long groupId, CancellationToken cancellationToken) { + // 1. 读取分组,找不到抛异常 var group = await repository.FindGroupByIdAsync(groupId, cancellationToken); if (group == null) { @@ -201,6 +223,7 @@ public sealed class DictionaryAppService( private async Task RequireItemAsync(long itemId, CancellationToken cancellationToken) { + // 1. 读取字典项,找不到抛异常 var item = await repository.FindItemByIdAsync(itemId, cancellationToken); if (item == null) { @@ -269,12 +292,14 @@ public sealed class DictionaryAppService( private async Task> GetOrLoadCacheAsync(long tenantId, string code, CancellationToken cancellationToken) { + // 1. 先查缓存 var cached = await cache.GetAsync(tenantId, code, cancellationToken); if (cached != null) { return cached; } + // 2. 从仓储加载并写入缓存 var entities = await repository.GetItemsByCodesAsync(new[] { code }, tenantId, includeSystem: false, cancellationToken); var items = entities .Where(item => item.IsEnabled && (item.Group?.IsEnabled ?? true)) diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/AssignUserRolesCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/AssignUserRolesCommandHandler.cs index 8105f69..adaea12 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/AssignUserRolesCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/AssignUserRolesCommandHandler.cs @@ -15,9 +15,14 @@ public sealed class AssignUserRolesCommandHandler( { public async Task Handle(AssignUserRolesCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文 var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 覆盖式绑定角色 await userRoleRepository.ReplaceUserRolesAsync(tenantId, request.UserId, request.RoleIds, cancellationToken); await userRoleRepository.SaveChangesAsync(cancellationToken); + + // 3. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/BindRolePermissionsCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/BindRolePermissionsCommandHandler.cs index eee1e9e..d5a6d0e 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/BindRolePermissionsCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/BindRolePermissionsCommandHandler.cs @@ -15,9 +15,14 @@ public sealed class BindRolePermissionsCommandHandler( { public async Task Handle(BindRolePermissionsCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文 var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 覆盖式绑定权限 await rolePermissionRepository.ReplaceRolePermissionsAsync(tenantId, request.RoleId, request.PermissionIds, cancellationToken); await rolePermissionRepository.SaveChangesAsync(cancellationToken); + + // 3. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CopyRoleTemplateCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CopyRoleTemplateCommandHandler.cs index 54b658b..e254b5f 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CopyRoleTemplateCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CopyRoleTemplateCommandHandler.cs @@ -26,6 +26,7 @@ public sealed class CopyRoleTemplateCommandHandler( /// public async Task Handle(CopyRoleTemplateCommand request, CancellationToken cancellationToken) { + // 1. 查询模板与模板权限 var template = await roleTemplateRepository.FindByCodeAsync(request.TemplateCode, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, $"角色模板 {request.TemplateCode} 不存在"); @@ -36,6 +37,7 @@ public sealed class CopyRoleTemplateCommandHandler( .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); + // 2. 计算角色名称/编码与描述 var tenantId = tenantProvider.GetCurrentTenantId(); var roleCode = string.IsNullOrWhiteSpace(request.RoleCode) ? template.TemplateCode : request.RoleCode.Trim(); var roleName = string.IsNullOrWhiteSpace(request.RoleName) ? template.Name : request.RoleName.Trim(); @@ -69,7 +71,7 @@ public sealed class CopyRoleTemplateCommandHandler( await roleRepository.UpdateAsync(role, cancellationToken); } - // 2. 确保模板权限全部存在,不存在则按模板定义创建。 + // 3. 确保模板权限全部存在,不存在则按模板定义创建。 var existingPermissions = await permissionRepository.GetByCodesAsync(tenantId, permissionCodes, cancellationToken); var permissionMap = existingPermissions.ToDictionary(x => x.Code, StringComparer.OrdinalIgnoreCase); @@ -94,7 +96,7 @@ public sealed class CopyRoleTemplateCommandHandler( await roleRepository.SaveChangesAsync(cancellationToken); - // 3. 绑定缺失的权限,保留租户自定义的已有授权。 + // 4. 绑定缺失的权限,保留租户自定义的已有授权。 var rolePermissions = await rolePermissionRepository.GetByRoleIdsAsync(tenantId, new[] { role.Id }, cancellationToken); var existingPermissionIds = rolePermissions .Select(x => x.PermissionId) diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreatePermissionCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreatePermissionCommandHandler.cs index 275946e..1e5cfc0 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreatePermissionCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreatePermissionCommandHandler.cs @@ -17,7 +17,10 @@ public sealed class CreatePermissionCommandHandler( { public async Task Handle(CreatePermissionCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文 var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 构建权限实体 var permission = new Permission { TenantId = tenantId, @@ -26,9 +29,11 @@ public sealed class CreatePermissionCommandHandler( Description = request.Description }; + // 3. 持久化 await permissionRepository.AddAsync(permission, cancellationToken); await permissionRepository.SaveChangesAsync(cancellationToken); + // 4. 返回 DTO return new PermissionDto { Id = permission.Id, diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleCommandHandler.cs index 717393a..71fdcca 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleCommandHandler.cs @@ -17,7 +17,10 @@ public sealed class CreateRoleCommandHandler( { public async Task Handle(CreateRoleCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文 var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 构建角色实体 var role = new Role { TenantId = tenantId, @@ -26,9 +29,11 @@ public sealed class CreateRoleCommandHandler( Description = request.Description }; + // 3. 持久化 await roleRepository.AddAsync(role, cancellationToken); await roleRepository.SaveChangesAsync(cancellationToken); + // 4. 返回 DTO return new RoleDto { Id = role.Id, diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleTemplateCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleTemplateCommandHandler.cs index 64460ef..b418306 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleTemplateCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleTemplateCommandHandler.cs @@ -19,17 +19,20 @@ public sealed class CreateRoleTemplateCommandHandler(IRoleTemplateRepository rol /// public async Task Handle(CreateRoleTemplateCommand request, CancellationToken cancellationToken) { + // 1. 校验必填 if (string.IsNullOrWhiteSpace(request.TemplateCode) || string.IsNullOrWhiteSpace(request.Name)) { throw new BusinessException(ErrorCodes.BadRequest, "模板编码与名称不能为空"); } + // 2. 检查编码唯一 var existing = await roleTemplateRepository.FindByCodeAsync(request.TemplateCode, cancellationToken); if (existing != null) { throw new BusinessException(ErrorCodes.Conflict, $"模板编码 {request.TemplateCode} 已存在"); } + // 3. 构建模板实体 var template = new RoleTemplate { TemplateCode = request.TemplateCode.Trim(), @@ -38,12 +41,14 @@ public sealed class CreateRoleTemplateCommandHandler(IRoleTemplateRepository rol IsActive = request.IsActive }; + // 4. 清洗权限编码 var permissions = request.PermissionCodes .Where(code => !string.IsNullOrWhiteSpace(code)) .Select(code => code.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); + // 5. 持久化并返回 DTO await roleTemplateRepository.AddAsync(template, permissions, cancellationToken); await roleTemplateRepository.SaveChangesAsync(cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeletePermissionCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeletePermissionCommandHandler.cs index 9dc2ce8..a494786 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeletePermissionCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeletePermissionCommandHandler.cs @@ -15,9 +15,14 @@ public sealed class DeletePermissionCommandHandler( { public async Task Handle(DeletePermissionCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文 var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 删除权限 await permissionRepository.DeleteAsync(request.PermissionId, tenantId, cancellationToken); await permissionRepository.SaveChangesAsync(cancellationToken); + + // 3. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleCommandHandler.cs index c45241a..66ab38c 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleCommandHandler.cs @@ -15,9 +15,14 @@ public sealed class DeleteRoleCommandHandler( { public async Task Handle(DeleteRoleCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文 var tenantId = tenantProvider.GetCurrentTenantId(); + + // 2. 删除角色 await roleRepository.DeleteAsync(request.RoleId, tenantId, cancellationToken); await roleRepository.SaveChangesAsync(cancellationToken); + + // 3. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleTemplateCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleTemplateCommandHandler.cs index 9947217..b641553 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleTemplateCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/DeleteRoleTemplateCommandHandler.cs @@ -12,14 +12,18 @@ public sealed class DeleteRoleTemplateCommandHandler(IRoleTemplateRepository rol { public async Task Handle(DeleteRoleTemplateCommand request, CancellationToken cancellationToken) { + // 1. 查询模板 var template = await roleTemplateRepository.FindByCodeAsync(request.TemplateCode, cancellationToken); if (template == null) { return false; } + // 2. 删除并保存 await roleTemplateRepository.DeleteAsync(template.Id, cancellationToken); await roleTemplateRepository.SaveChangesAsync(cancellationToken); + + // 3. 返回执行结果 return true; } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetRoleTemplateQueryHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetRoleTemplateQueryHandler.cs index b8b7bbe..2895c7d 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetRoleTemplateQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetRoleTemplateQueryHandler.cs @@ -15,14 +15,18 @@ public sealed class GetRoleTemplateQueryHandler(IRoleTemplateRepository roleTemp /// public async Task Handle(GetRoleTemplateQuery request, CancellationToken cancellationToken) { + // 1. 查询模板 var template = await roleTemplateRepository.FindByCodeAsync(request.TemplateCode, cancellationToken); if (template == null) { return null; } + // 2. 查询模板权限 var permissions = await roleTemplateRepository.GetPermissionsAsync(template.Id, cancellationToken); var codes = permissions.Select(x => x.PermissionCode).ToArray(); + + // 3. 返回 DTO return TemplateMapper.ToDto(template, codes); } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetUserPermissionsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetUserPermissionsQueryHandler.cs index a5a2a42..d50be4b 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetUserPermissionsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetUserPermissionsQueryHandler.cs @@ -20,26 +20,22 @@ public sealed class GetUserPermissionsQueryHandler( ITenantProvider tenantProvider) : IRequestHandler { - private readonly IIdentityUserRepository _identityUserRepository = identityUserRepository; - private readonly IUserRoleRepository _userRoleRepository = userRoleRepository; - private readonly IRoleRepository _roleRepository = roleRepository; - private readonly IPermissionRepository _permissionRepository = permissionRepository; - private readonly IRolePermissionRepository _rolePermissionRepository = rolePermissionRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task Handle(GetUserPermissionsQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var user = await _identityUserRepository.FindByIdAsync(request.UserId, cancellationToken); + // 1. 获取租户并查询用户 + var tenantId = tenantProvider.GetCurrentTenantId(); + var user = await identityUserRepository.FindByIdAsync(request.UserId, cancellationToken); if (user == null || user.TenantId != tenantId) { return null; } + // 2. 解析角色与权限 var roleCodes = await ResolveUserRolesAsync(tenantId, user.Id, cancellationToken); var permissionCodes = await ResolveUserPermissionsAsync(tenantId, user.Id, cancellationToken); + // 3. 返回用户权限概览 return new UserPermissionDto { UserId = user.Id, @@ -55,34 +51,39 @@ public sealed class GetUserPermissionsQueryHandler( private async Task ResolveUserRolesAsync(long tenantId, long userId, CancellationToken cancellationToken) { - var relations = await _userRoleRepository.GetByUserIdAsync(tenantId, userId, cancellationToken); + // 1. 查询用户角色关系 + var relations = await userRoleRepository.GetByUserIdAsync(tenantId, userId, cancellationToken); var roleIds = relations.Select(x => x.RoleId).Distinct().ToArray(); if (roleIds.Length == 0) { return Array.Empty(); } - var roles = await _roleRepository.GetByIdsAsync(tenantId, roleIds, cancellationToken); + // 2. 查询角色编码 + var roles = await roleRepository.GetByIdsAsync(tenantId, roleIds, cancellationToken); return roles.Select(x => x.Code).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } private async Task ResolveUserPermissionsAsync(long tenantId, long userId, CancellationToken cancellationToken) { - var relations = await _userRoleRepository.GetByUserIdAsync(tenantId, userId, cancellationToken); + // 1. 查询用户角色关系 + var relations = await userRoleRepository.GetByUserIdAsync(tenantId, userId, cancellationToken); var roleIds = relations.Select(x => x.RoleId).Distinct().ToArray(); if (roleIds.Length == 0) { return Array.Empty(); } - var rolePermissions = await _rolePermissionRepository.GetByRoleIdsAsync(tenantId, roleIds, cancellationToken); + // 2. 查询角色-权限关系 + var rolePermissions = await rolePermissionRepository.GetByRoleIdsAsync(tenantId, roleIds, cancellationToken); var permissionIds = rolePermissions.Select(x => x.PermissionId).Distinct().ToArray(); if (permissionIds.Length == 0) { return Array.Empty(); } - var permissions = await _permissionRepository.GetByIdsAsync(tenantId, permissionIds, cancellationToken); + // 3. 查询权限编码 + var permissions = await permissionRepository.GetByIdsAsync(tenantId, permissionIds, cancellationToken); return permissions.Select(x => x.Code).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs index 2b533cd..571ebf5 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs @@ -16,9 +16,11 @@ public sealed class ListRoleTemplatesQueryHandler(IRoleTemplateRepository roleTe /// public async Task> Handle(ListRoleTemplatesQuery request, CancellationToken cancellationToken) { + // 1. 查询模板与权限映射 var templates = await roleTemplateRepository.GetAllAsync(request.IsActive, cancellationToken); var permissionsMap = await roleTemplateRepository.GetPermissionsAsync(templates.Select(t => t.Id), cancellationToken); + // 2. 排序并映射 DTO var dtos = templates .OrderBy(template => template.TemplateCode, StringComparer.OrdinalIgnoreCase) .Select(template => @@ -30,6 +32,7 @@ public sealed class ListRoleTemplatesQueryHandler(IRoleTemplateRepository roleTe }) .ToArray(); + // 3. 返回结果 return dtos; } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchPermissionsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchPermissionsQueryHandler.cs index 97bdd1b..62a4ce9 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchPermissionsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchPermissionsQueryHandler.cs @@ -19,9 +19,11 @@ public sealed class SearchPermissionsQueryHandler( { public async Task> Handle(SearchPermissionsQuery request, CancellationToken cancellationToken) { + // 1. 获取租户上下文并查询权限 var tenantId = tenantProvider.GetCurrentTenantId(); var permissions = await permissionRepository.SearchAsync(tenantId, request.Keyword, cancellationToken); + // 2. 排序 var sorted = request.SortBy?.ToLowerInvariant() switch { "name" => request.SortDescending @@ -35,11 +37,13 @@ public sealed class SearchPermissionsQueryHandler( : permissions.OrderBy(x => x.CreatedAt) }; + // 3. 分页 var paged = sorted .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); + // 4. 映射 DTO var items = paged.Select(permission => new PermissionDto { Id = permission.Id, @@ -49,6 +53,7 @@ public sealed class SearchPermissionsQueryHandler( Description = permission.Description }).ToList(); + // 5. 返回分页结果 return new PagedResult(items, request.Page, request.PageSize, permissions.Count); } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchRolesQueryHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchRolesQueryHandler.cs index bd11a5d..3e18b69 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchRolesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchRolesQueryHandler.cs @@ -19,9 +19,11 @@ public sealed class SearchRolesQueryHandler( { public async Task> Handle(SearchRolesQuery request, CancellationToken cancellationToken) { + // 1. 获取租户上下文并查询角色 var tenantId = tenantProvider.GetCurrentTenantId(); var roles = await roleRepository.SearchAsync(tenantId, request.Keyword, cancellationToken); + // 2. 排序 var sorted = request.SortBy?.ToLowerInvariant() switch { "name" => request.SortDescending @@ -32,11 +34,13 @@ public sealed class SearchRolesQueryHandler( : roles.OrderBy(x => x.CreatedAt) }; + // 3. 分页 var paged = sorted .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); + // 4. 映射 DTO var items = paged.Select(role => new RoleDto { Id = role.Id, @@ -46,6 +50,7 @@ public sealed class SearchRolesQueryHandler( Description = role.Description }).ToList(); + // 5. 返回分页结果 return new PagedResult(items, request.Page, request.PageSize, roles.Count); } } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchUserPermissionsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchUserPermissionsQueryHandler.cs index 07e3595..599d6fd 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchUserPermissionsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchUserPermissionsQueryHandler.cs @@ -22,25 +22,21 @@ public sealed class SearchUserPermissionsQueryHandler( ITenantProvider tenantProvider) : IRequestHandler> { - private readonly IIdentityUserRepository _identityUserRepository = identityUserRepository; - private readonly IUserRoleRepository _userRoleRepository = userRoleRepository; - private readonly IRoleRepository _roleRepository = roleRepository; - private readonly IPermissionRepository _permissionRepository = permissionRepository; - private readonly IRolePermissionRepository _rolePermissionRepository = rolePermissionRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; - /// public async Task> Handle(SearchUserPermissionsQuery request, CancellationToken cancellationToken) { - var tenantId = _tenantProvider.GetCurrentTenantId(); - var users = await _identityUserRepository.SearchAsync(tenantId, request.Keyword, cancellationToken); + // 1. 获取租户并查询用户 + var tenantId = tenantProvider.GetCurrentTenantId(); + var users = await identityUserRepository.SearchAsync(tenantId, request.Keyword, cancellationToken); + // 2. 排序与分页 var sorted = SortUsers(users, request.SortBy, request.SortDescending); var paged = sorted .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); + // 3. 解析角色与权限 var resolved = await ResolveRolesAndPermissionsAsync(tenantId, paged, cancellationToken); var items = paged.Select(user => new UserPermissionDto { @@ -81,23 +77,27 @@ public sealed class SearchUserPermissionsQueryHandler( IReadOnlyCollection users, CancellationToken cancellationToken) { + // 1. 查询用户角色关系 var userIds = users.Select(x => x.Id).ToArray(); - var userRoleRelations = await _userRoleRepository.GetByUserIdsAsync(tenantId, userIds, cancellationToken); + var userRoleRelations = await userRoleRepository.GetByUserIdsAsync(tenantId, userIds, cancellationToken); var roleIds = userRoleRelations.Select(x => x.RoleId).Distinct().ToArray(); + // 2. 查询角色信息 var roles = roleIds.Length == 0 ? Array.Empty() - : await _roleRepository.GetByIdsAsync(tenantId, roleIds, cancellationToken); + : await roleRepository.GetByIdsAsync(tenantId, roleIds, cancellationToken); var roleCodeMap = roles.ToDictionary(r => r.Id, r => r.Code, comparer: EqualityComparer.Default); + // 3. 查询角色-权限关系 var rolePermissions = roleIds.Length == 0 ? Array.Empty() - : await _rolePermissionRepository.GetByRoleIdsAsync(tenantId, roleIds, cancellationToken); + : await rolePermissionRepository.GetByRoleIdsAsync(tenantId, roleIds, cancellationToken); var permissionIds = rolePermissions.Select(x => x.PermissionId).Distinct().ToArray(); + // 4. 查询权限详情 var permissions = permissionIds.Length == 0 ? Array.Empty() - : await _permissionRepository.GetByIdsAsync(tenantId, permissionIds, cancellationToken); + : await permissionRepository.GetByIdsAsync(tenantId, permissionIds, cancellationToken); var permissionCodeMap = permissions.ToDictionary(p => p.Id, p => p.Code, comparer: EqualityComparer.Default); var rolePermissionsLookup = rolePermissions @@ -107,6 +107,7 @@ public sealed class SearchUserPermissionsQueryHandler( var result = new Dictionary(); foreach (var userId in userIds) { + // 5. 聚合用户角色与权限编码 var rolesForUser = userRoleRelations.Where(ur => ur.UserId == userId).Select(ur => ur.RoleId).Distinct().ToArray(); var roleCodes = rolesForUser .Select(rid => roleCodeMap.GetValueOrDefault(rid)) diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdatePermissionCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdatePermissionCommandHandler.cs index b123164..cde1cdd 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdatePermissionCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdatePermissionCommandHandler.cs @@ -16,6 +16,7 @@ public sealed class UpdatePermissionCommandHandler( { public async Task Handle(UpdatePermissionCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文并查询权限 var tenantId = tenantProvider.GetCurrentTenantId(); var permission = await permissionRepository.FindByIdAsync(request.PermissionId, tenantId, cancellationToken); if (permission == null) @@ -23,12 +24,15 @@ public sealed class UpdatePermissionCommandHandler( return null; } + // 2. 更新字段 permission.Name = request.Name; permission.Description = request.Description; + // 3. 持久化 await permissionRepository.UpdateAsync(permission, cancellationToken); await permissionRepository.SaveChangesAsync(cancellationToken); + // 4. 返回 DTO return new PermissionDto { Id = permission.Id, diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdateRoleCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdateRoleCommandHandler.cs index c9b6a2d..49898de 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdateRoleCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdateRoleCommandHandler.cs @@ -16,6 +16,7 @@ public sealed class UpdateRoleCommandHandler( { public async Task Handle(UpdateRoleCommand request, CancellationToken cancellationToken) { + // 1. 获取租户上下文并查询角色 var tenantId = tenantProvider.GetCurrentTenantId(); var role = await roleRepository.FindByIdAsync(request.RoleId, tenantId, cancellationToken); if (role == null) @@ -23,12 +24,15 @@ public sealed class UpdateRoleCommandHandler( return null; } + // 2. 更新字段 role.Name = request.Name; role.Description = request.Description; + // 3. 持久化 await roleRepository.UpdateAsync(role, cancellationToken); await roleRepository.SaveChangesAsync(cancellationToken); + // 4. 返回 DTO return new RoleDto { Id = role.Id, diff --git a/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs b/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs index 402385f..5a1bca6 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeRequest.cs @@ -10,8 +10,6 @@ namespace TakeoutSaaS.Application.Sms.Contracts; /// public sealed class SendVerificationCodeRequest(string phoneNumber, string scene, SmsProviderKind? provider = null) { - - /// /// 手机号(支持 +86 前缀或纯 11 位)。 /// diff --git a/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs b/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs index 034230c..9eb1262 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Contracts/VerifyVerificationCodeRequest.cs @@ -8,8 +8,6 @@ namespace TakeoutSaaS.Application.Sms.Contracts; /// public sealed class VerifyVerificationCodeRequest(string phoneNumber, string scene, string code) { - - /// /// 手机号。 /// diff --git a/src/Application/TakeoutSaaS.Application/Sms/Services/VerificationCodeService.cs b/src/Application/TakeoutSaaS.Application/Sms/Services/VerificationCodeService.cs index 88806c9..70048b1 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Services/VerificationCodeService.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Services/VerificationCodeService.cs @@ -30,6 +30,7 @@ public sealed class VerificationCodeService( /// public async Task SendAsync(SendVerificationCodeRequest request, CancellationToken cancellationToken = default) { + // 1. 参数校验 if (string.IsNullOrWhiteSpace(request.PhoneNumber)) { throw new BusinessException(ErrorCodes.BadRequest, "手机号不能为空"); @@ -40,6 +41,7 @@ public sealed class VerificationCodeService( throw new BusinessException(ErrorCodes.BadRequest, "场景不能为空"); } + // 2. 解析模板与缓存键 var smsOptions = smsOptionsMonitor.CurrentValue; var codeOptions = codeOptionsMonitor.CurrentValue; var templateCode = ResolveTemplate(request.Scene, smsOptions); @@ -48,8 +50,10 @@ public sealed class VerificationCodeService( var cacheKey = $"{codeOptions.CachePrefix}:{tenantKey}:{request.Scene}:{phone}"; var cooldownKey = $"{cacheKey}:cooldown"; + // 3. 检查冷却期 await EnsureCooldownAsync(cooldownKey, codeOptions.CooldownSeconds, cancellationToken).ConfigureAwait(false); + // 4. 生成验证码并发送短信 var code = GenerateCode(codeOptions.CodeLength); var variables = new Dictionary { { "code", code } }; var sender = senderResolver.Resolve(request.Provider); @@ -61,6 +65,7 @@ public sealed class VerificationCodeService( throw new BusinessException(ErrorCodes.InternalServerError, $"短信发送失败:{smsResult.Message}"); } + // 5. 写入验证码与冷却缓存 var expiresAt = DateTimeOffset.UtcNow.AddMinutes(codeOptions.ExpireMinutes); await cache.SetStringAsync(cacheKey, code, new DistributedCacheEntryOptions { @@ -83,11 +88,13 @@ public sealed class VerificationCodeService( /// public async Task VerifyAsync(VerifyVerificationCodeRequest request, CancellationToken cancellationToken = default) { + // 1. 基础校验 if (string.IsNullOrWhiteSpace(request.Code)) { return false; } + // 2. 读取验证码 var codeOptions = codeOptionsMonitor.CurrentValue; var phone = NormalizePhoneNumber(request.PhoneNumber); var tenantKey = tenantProvider.GetCurrentTenantId() == 0 ? "platform" : tenantProvider.GetCurrentTenantId().ToString(); @@ -99,6 +106,7 @@ public sealed class VerificationCodeService( return false; } + // 3. 比对成功后清除缓存 var success = string.Equals(cachedCode, request.Code, StringComparison.Ordinal); if (success) { diff --git a/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs b/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs index b757e36..48e05eb 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadRequest.cs @@ -10,8 +10,6 @@ namespace TakeoutSaaS.Application.Storage.Contracts; /// public sealed class DirectUploadRequest(UploadFileType fileType, string fileName, string contentType, long contentLength, string? requestOrigin) { - - /// /// 文件类型。 /// diff --git a/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs b/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs index ea19f91..011f39d 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs @@ -17,8 +17,6 @@ public sealed class UploadFileRequest( long contentLength, string? requestOrigin) { - - /// /// 文件分类。 /// diff --git a/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs b/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs index f2105c5..6ce4e2c 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs @@ -35,11 +35,13 @@ public sealed class FileStorageService( /// public async Task UploadAsync(UploadFileRequest request, CancellationToken cancellationToken = default) { + // 1. 校验请求 if (request is null) { throw new BusinessException(ErrorCodes.BadRequest, "上传请求不能为空"); } + // 2. 读取安全配置并校验来源/大小/类型 var options = optionsMonitor.CurrentValue; var security = options.Security; ValidateOrigin(request.RequestOrigin, security); @@ -50,15 +52,18 @@ public sealed class FileStorageService( var contentType = NormalizeContentType(request.ContentType, extension); ResetStream(request.Content); + // 3. 生成对象键与元数据 var objectKey = BuildObjectKey(request.FileType, extension); var metadata = BuildMetadata(request.FileType); var expires = TimeSpan.FromMinutes(Math.Max(1, security.DefaultUrlExpirationMinutes)); var provider = providerResolver.Resolve(); + // 4. 上传到对象存储 var uploadResult = await provider.UploadAsync( new StorageUploadRequest(objectKey, request.Content, contentType, request.ContentLength, true, expires, metadata), cancellationToken).ConfigureAwait(false); + // 5. 追加防盗链签名并返回 var finalUrl = AppendAntiLeechToken(uploadResult.SignedUrl ?? uploadResult.Url, objectKey, expires, security); logger.LogInformation("文件上传成功:{ObjectKey} ({Size} bytes)", objectKey, request.ContentLength); @@ -73,11 +78,13 @@ public sealed class FileStorageService( /// public async Task CreateDirectUploadAsync(DirectUploadRequest request, CancellationToken cancellationToken = default) { + // 1. 校验请求 if (request is null) { throw new BusinessException(ErrorCodes.BadRequest, "直传请求不能为空"); } + // 2. 校验来源/大小/类型 var options = optionsMonitor.CurrentValue; var security = options.Security; ValidateOrigin(request.RequestOrigin, security); @@ -87,14 +94,17 @@ public sealed class FileStorageService( ValidateExtension(request.FileType, extension, security); var contentType = NormalizeContentType(request.ContentType, extension); + // 3. 构建直传参数 var objectKey = BuildObjectKey(request.FileType, extension); var provider = providerResolver.Resolve(); var expires = TimeSpan.FromMinutes(Math.Max(1, security.DefaultUrlExpirationMinutes)); + // 4. 向存储获取直传凭证 var directResult = await provider.CreateDirectUploadAsync( new StorageDirectUploadRequest(objectKey, contentType, request.ContentLength, expires), cancellationToken).ConfigureAwait(false); + // 5. 构造直传结果并追加防盗链 var finalDownloadUrl = directResult.SignedDownloadUrl != null ? AppendAntiLeechToken(directResult.SignedDownloadUrl, objectKey, expires, security) : null; diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/DatabaseConstants.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/DatabaseConstants.cs index cf8a78e..2f4022e 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/DatabaseConstants.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/DatabaseConstants.cs @@ -6,7 +6,7 @@ public static class DatabaseConstants { /// - /// 默认业务库(AppDatabase)。 + /// 默认业务库(AppDatabase). /// public const string AppDataSource = "AppDatabase"; diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/ErrorCodes.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/ErrorCodes.cs index 2d7ed97..3214155 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/ErrorCodes.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Constants/ErrorCodes.cs @@ -5,15 +5,43 @@ namespace TakeoutSaaS.Shared.Abstractions.Constants; /// public static class ErrorCodes { + /// + /// 请求参数错误。 + /// public const int BadRequest = 400; + + /// + /// 未授权访问。 + /// public const int Unauthorized = 401; + + /// + /// 权限不足。 + /// public const int Forbidden = 403; + + /// + /// 资源未找到。 + /// public const int NotFound = 404; + + /// + /// 资源冲突。 + /// public const int Conflict = 409; + + /// + /// 校验失败。 + /// public const int ValidationFailed = 422; + + /// + /// 服务器内部错误。 + /// public const int InternalServerError = 500; - // 业务自定义区间(10000+) + /// + /// 业务自定义错误(10000+)。 + /// public const int BusinessError = 10001; } - diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.cs index b49a215..62365b3 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.cs @@ -6,7 +6,7 @@ namespace TakeoutSaaS.Shared.Abstractions.Results; /// /// 统一的 API 返回结果包装。 /// -/// 数据载荷类型 +/// 数据载荷类型。 public sealed record ApiResponse { /// @@ -92,6 +92,9 @@ public sealed record ApiResponse Timestamp = DateTime.UtcNow }; + /// + /// 解析当前 TraceId。 + /// private static string ResolveTraceId() { if (!string.IsNullOrWhiteSpace(TraceContext.TraceId)) @@ -113,9 +116,19 @@ public sealed record ApiResponse } } +/// +/// 作为 TraceId 缺失时的本地雪花 ID 备用生成器。 +/// internal sealed class IdFallbackGenerator { + /// + /// 延迟初始化的单例实例承载。 + /// private static readonly Lazy Lazy = new(() => new IdFallbackGenerator()); + + /// + /// 获取备用雪花生成器单例。 + /// public static IdFallbackGenerator Instance => Lazy.Value; private readonly object _sync = new(); @@ -126,6 +139,9 @@ internal sealed class IdFallbackGenerator { } + /// + /// 生成雪花风格的本地备用 ID。 + /// public long NextId() { lock (_sync) @@ -149,6 +165,9 @@ internal sealed class IdFallbackGenerator } } + /// + /// 等待到下一个毫秒以避免序列冲突。 + /// private static long WaitNextMillis(long lastTimestamp) { var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); diff --git a/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs b/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs index 7c5be5f..9ec3592 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs @@ -11,7 +11,6 @@ namespace TakeoutSaaS.Shared.Web.Middleware; /// public sealed class RequestLoggingMiddleware(RequestDelegate next, ILogger logger) { - public async Task InvokeAsync(HttpContext context) { var stopwatch = Stopwatch.StartNew(); diff --git a/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs b/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs index e7f5209..3d5c423 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs @@ -12,8 +12,6 @@ namespace TakeoutSaaS.Shared.Web.Security; /// public sealed class HttpContextCurrentUserAccessor(IHttpContextAccessor httpContextAccessor) : ICurrentUserAccessor { - - /// public long UserId { diff --git a/src/Domain/TakeoutSaaS.Domain/Deliveries/Entities/DeliveryOrder.cs b/src/Domain/TakeoutSaaS.Domain/Deliveries/Entities/DeliveryOrder.cs index c547d6b..f408473 100644 --- a/src/Domain/TakeoutSaaS.Domain/Deliveries/Entities/DeliveryOrder.cs +++ b/src/Domain/TakeoutSaaS.Domain/Deliveries/Entities/DeliveryOrder.cs @@ -8,6 +8,9 @@ namespace TakeoutSaaS.Domain.Deliveries.Entities; /// public sealed class DeliveryOrder : MultiTenantEntityBase { + /// + /// 获取或设置关联订单 ID。 + /// public long OrderId { get; set; } /// diff --git a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs index 7f78dde..b6e7833 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs @@ -10,13 +10,80 @@ namespace TakeoutSaaS.Domain.Identity.Repositories; /// public interface IPermissionRepository { + /// + /// 根据 ID 查询权限。 + /// + /// 权限 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 权限实体或 null。 Task FindByIdAsync(long permissionId, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 根据编码查询权限。 + /// + /// 权限编码。 + /// 租户 ID。 + /// 取消标记。 + /// 权限实体或 null。 Task FindByCodeAsync(string code, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 根据编码集合查询权限列表。 + /// + /// 租户 ID。 + /// 权限编码集合。 + /// 取消标记。 + /// 权限集合。 Task> GetByCodesAsync(long tenantId, IEnumerable codes, CancellationToken cancellationToken = default); + + /// + /// 根据 ID 集合查询权限列表。 + /// + /// 租户 ID。 + /// 权限 ID 集合。 + /// 取消标记。 + /// 权限集合。 Task> GetByIdsAsync(long tenantId, IEnumerable permissionIds, CancellationToken cancellationToken = default); + + /// + /// 按关键字搜索权限。 + /// + /// 租户 ID。 + /// 关键字。 + /// 取消标记。 + /// 权限集合。 Task> SearchAsync(long tenantId, string? keyword, CancellationToken cancellationToken = default); + + /// + /// 新增权限。 + /// + /// 权限实体。 + /// 取消标记。 + /// 异步操作任务。 Task AddAsync(Permission permission, CancellationToken cancellationToken = default); + + /// + /// 更新权限。 + /// + /// 权限实体。 + /// 取消标记。 + /// 异步操作任务。 Task UpdateAsync(Permission permission, CancellationToken cancellationToken = default); + + /// + /// 删除权限。 + /// + /// 权限 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步操作任务。 Task DeleteAsync(long permissionId, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 保存仓储变更。 + /// + /// 取消标记。 + /// 异步操作任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRolePermissionRepository.cs b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRolePermissionRepository.cs index 5ef9d8d..3f45d5c 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRolePermissionRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRolePermissionRepository.cs @@ -10,8 +10,37 @@ namespace TakeoutSaaS.Domain.Identity.Repositories; /// public interface IRolePermissionRepository { + /// + /// 根据角色 ID 集合获取角色权限关系。 + /// + /// 租户 ID。 + /// 角色 ID 集合。 + /// 取消标记。 + /// 角色权限关系列表。 Task> GetByRoleIdsAsync(long tenantId, IEnumerable roleIds, CancellationToken cancellationToken = default); + + /// + /// 批量新增角色权限关系。 + /// + /// 角色权限集合。 + /// 取消标记。 + /// 异步操作任务。 Task AddRangeAsync(IEnumerable rolePermissions, CancellationToken cancellationToken = default); + + /// + /// 替换角色的权限集合。 + /// + /// 租户 ID。 + /// 角色 ID。 + /// 权限 ID 集合。 + /// 取消标记。 + /// 异步操作任务。 Task ReplaceRolePermissionsAsync(long tenantId, long roleId, IEnumerable permissionIds, CancellationToken cancellationToken = default); + + /// + /// 提交持久化变更。 + /// + /// 取消标记。 + /// 异步操作任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleRepository.cs b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleRepository.cs index 822266e..f740fce 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleRepository.cs @@ -10,12 +10,71 @@ namespace TakeoutSaaS.Domain.Identity.Repositories; /// public interface IRoleRepository { + /// + /// 根据 ID 查询角色。 + /// + /// 角色 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 角色实体或 null。 Task FindByIdAsync(long roleId, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 根据编码查询角色。 + /// + /// 角色编码。 + /// 租户 ID。 + /// 取消标记。 + /// 角色实体或 null。 Task FindByCodeAsync(string code, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 批量获取角色列表。 + /// + /// 租户 ID。 + /// 角色 ID 集合。 + /// 取消标记。 + /// 角色集合。 Task> GetByIdsAsync(long tenantId, IEnumerable roleIds, CancellationToken cancellationToken = default); + + /// + /// 按关键字搜索角色。 + /// + /// 租户 ID。 + /// 关键字。 + /// 取消标记。 + /// 角色集合。 Task> SearchAsync(long tenantId, string? keyword, CancellationToken cancellationToken = default); + + /// + /// 新增角色。 + /// + /// 角色实体。 + /// 取消标记。 + /// 异步操作任务。 Task AddAsync(Role role, CancellationToken cancellationToken = default); + + /// + /// 更新角色。 + /// + /// 角色实体。 + /// 取消标记。 + /// 异步操作任务。 Task UpdateAsync(Role role, CancellationToken cancellationToken = default); + + /// + /// 删除角色。 + /// + /// 角色 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步操作任务。 Task DeleteAsync(long roleId, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 保存仓储变更。 + /// + /// 取消标记。 + /// 异步操作任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleTemplateRepository.cs b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleTemplateRepository.cs index 963754a..3a9ce61 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleTemplateRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleTemplateRepository.cs @@ -10,19 +10,68 @@ namespace TakeoutSaaS.Domain.Identity.Repositories; /// public interface IRoleTemplateRepository { + /// + /// 查询角色模板列表。 + /// + /// 启用状态过滤。 + /// 取消标记。 + /// 角色模板集合。 Task> GetAllAsync(bool? isActive, CancellationToken cancellationToken = default); + /// + /// 通过模板编码获取模板信息。 + /// + /// 模板编码。 + /// 取消标记。 + /// 模板实体或 null。 Task FindByCodeAsync(string templateCode, CancellationToken cancellationToken = default); + /// + /// 获取模板的权限列表。 + /// + /// 模板 ID。 + /// 取消标记。 + /// 权限集合。 Task> GetPermissionsAsync(long roleTemplateId, CancellationToken cancellationToken = default); + /// + /// 批量获取多个模板的权限映射。 + /// + /// 模板 ID 集合。 + /// 取消标记。 + /// 模板与权限列表的映射。 Task>> GetPermissionsAsync(IEnumerable roleTemplateIds, CancellationToken cancellationToken = default); + /// + /// 新增模板及其权限。 + /// + /// 模板实体。 + /// 权限编码集合。 + /// 取消标记。 + /// 异步操作任务。 Task AddAsync(RoleTemplate template, IEnumerable permissionCodes, CancellationToken cancellationToken = default); + /// + /// 更新模板及权限。 + /// + /// 模板实体。 + /// 权限编码集合。 + /// 取消标记。 + /// 异步操作任务。 Task UpdateAsync(RoleTemplate template, IEnumerable permissionCodes, CancellationToken cancellationToken = default); + /// + /// 删除模板。 + /// + /// 模板 ID。 + /// 取消标记。 + /// 异步操作任务。 Task DeleteAsync(long roleTemplateId, CancellationToken cancellationToken = default); + /// + /// 保存仓储变更。 + /// + /// 取消标记。 + /// 异步操作任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IUserRoleRepository.cs b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IUserRoleRepository.cs index aa9b9c8..68c0915 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IUserRoleRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IUserRoleRepository.cs @@ -10,8 +10,38 @@ namespace TakeoutSaaS.Domain.Identity.Repositories; /// public interface IUserRoleRepository { + /// + /// 批量获取指定用户的角色关系。 + /// + /// 租户 ID。 + /// 用户 ID 集合。 + /// 取消标记。 + /// 用户角色关系集合。 Task> GetByUserIdsAsync(long tenantId, IEnumerable userIds, CancellationToken cancellationToken = default); + + /// + /// 获取单个用户的角色关系。 + /// + /// 租户 ID。 + /// 用户 ID。 + /// 取消标记。 + /// 指定用户的角色关系列表。 Task> GetByUserIdAsync(long tenantId, long userId, CancellationToken cancellationToken = default); + + /// + /// 替换用户的角色列表。 + /// + /// 租户 ID。 + /// 用户 ID。 + /// 角色 ID 集合。 + /// 取消标记。 + /// 异步操作任务。 Task ReplaceUserRolesAsync(long tenantId, long userId, IEnumerable roleIds, CancellationToken cancellationToken = default); + + /// + /// 提交持久化变更。 + /// + /// 取消标记。 + /// 异步操作任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantRepository.cs b/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantRepository.cs index c467a2e..3a7df9b 100644 --- a/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantRepository.cs @@ -30,12 +30,30 @@ public interface IMerchantRepository /// 获取指定商户的合同列表。 /// Task> GetContractsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 根据合同 ID 获取合同详情。 + /// + /// 商户 ID。 + /// 租户 ID。 + /// 合同 ID。 + /// 取消标记。 + /// 合同实体或 null。 Task FindContractByIdAsync(long merchantId, long tenantId, long contractId, CancellationToken cancellationToken = default); /// /// 获取指定商户的资质文件列表。 /// Task> GetDocumentsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default); + + /// + /// 根据文件 ID 获取资质文件详情。 + /// + /// 商户 ID。 + /// 租户 ID。 + /// 文件 ID。 + /// 取消标记。 + /// 资质文件实体或 null。 Task FindDocumentByIdAsync(long merchantId, long tenantId, long documentId, CancellationToken cancellationToken = default); /// @@ -52,12 +70,26 @@ public interface IMerchantRepository /// 新增商户合同。 /// Task AddContractAsync(MerchantContract contract, CancellationToken cancellationToken = default); + + /// + /// 更新商户合同。 + /// + /// 合同实体。 + /// 取消标记。 + /// 异步操作任务。 Task UpdateContractAsync(MerchantContract contract, CancellationToken cancellationToken = default); /// /// 新增商户资质文件。 /// Task AddDocumentAsync(MerchantDocument document, CancellationToken cancellationToken = default); + + /// + /// 更新商户资质文件。 + /// + /// 资质文件实体。 + /// 取消标记。 + /// 异步操作任务。 Task UpdateDocumentAsync(MerchantDocument document, CancellationToken cancellationToken = default); /// diff --git a/src/Domain/TakeoutSaaS.Domain/Queues/Entities/QueueTicket.cs b/src/Domain/TakeoutSaaS.Domain/Queues/Entities/QueueTicket.cs index e235909..2c07860 100644 --- a/src/Domain/TakeoutSaaS.Domain/Queues/Entities/QueueTicket.cs +++ b/src/Domain/TakeoutSaaS.Domain/Queues/Entities/QueueTicket.cs @@ -8,6 +8,9 @@ namespace TakeoutSaaS.Domain.Queues.Entities; /// public sealed class QueueTicket : MultiTenantEntityBase { + /// + /// 获取或设置所属门店 ID。 + /// public long StoreId { get; set; } /// diff --git a/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs b/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs index 2e76835..c087384 100644 --- a/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs +++ b/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs @@ -14,8 +14,8 @@ using System.Threading.RateLimiting; const string CorsPolicyName = "GatewayCors"; +// 1. 创建构建器并配置 Serilog var builder = WebApplication.CreateBuilder(args); - builder.Host.UseSerilog((context, services, loggerConfiguration) => { loggerConfiguration @@ -24,9 +24,11 @@ builder.Host.UseSerilog((context, services, loggerConfiguration) => .Enrich.FromLogContext(); }); +// 2. 配置 YARP 反向代理 builder.Services.AddReverseProxy() .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); +// 3. 转发头部配置 builder.Services.Configure(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; @@ -34,6 +36,7 @@ builder.Services.Configure(options => options.KnownProxies.Clear(); }); +// 4. 配置 CORS builder.Services.AddCors(options => { options.AddPolicy(CorsPolicyName, policy => @@ -44,6 +47,7 @@ builder.Services.AddCors(options => }); }); +// 5. 配置网关限流 builder.Services.Configure(builder.Configuration.GetSection("Gateway:RateLimiting")); var rateLimitOptions = builder.Configuration.GetSection("Gateway:RateLimiting").Get() ?? new(); @@ -66,6 +70,7 @@ if (rateLimitOptions.Enabled) }); } +// 6. 配置 OpenTelemetry var otelOptions = builder.Configuration.GetSection("OpenTelemetry").Get() ?? new(); if (otelOptions.Enabled) { @@ -117,10 +122,13 @@ if (otelOptions.Enabled) }); } +// 7. 构建应用 var app = builder.Build(); +// 8. 转发头中间件 app.UseForwardedHeaders(); +// 9. 全局异常处理中间件 app.UseExceptionHandler(errorApp => { // 1. 捕获所有未处理异常并返回统一结构。 @@ -145,6 +153,7 @@ app.UseExceptionHandler(errorApp => }); }); +// 10. 请求日志 app.UseSerilogRequestLogging(options => { options.MessageTemplate = "网关请求 {RequestMethod} {RequestPath} => {StatusCode} 用时 {Elapsed:0.000} 秒"; @@ -156,6 +165,7 @@ app.UseSerilogRequestLogging(options => }; }); +// 11. CORS 与限流 app.UseCors(CorsPolicyName); if (rateLimitOptions.Enabled) @@ -163,6 +173,7 @@ if (rateLimitOptions.Enabled) app.UseRateLimiter(); } +// 12. 透传请求头并保证 Trace app.Use(async (context, next) => { // 1. 确保请求拥有可追踪的 ID。 @@ -187,6 +198,7 @@ app.Use(async (context, next) => await next(context); }); +// 13. 映射反向代理与健康接口 app.MapReverseProxy(); app.MapGet("/", () => Results.Json(new diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs index 34917c7..2a67038 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs @@ -15,8 +15,6 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// public sealed class EfDeliveryRepository(TakeoutAppDbContext context) : IDeliveryRepository { - - /// public Task FindByIdAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default) { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs index 35e00a8..cef1a73 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs @@ -15,8 +15,6 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// public sealed class EfMerchantRepository(TakeoutAppDbContext context) : IMerchantRepository { - - /// public Task FindByIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default) { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs index c73a185..b5a2c7f 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs @@ -16,8 +16,6 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// public sealed class EfOrderRepository(TakeoutAppDbContext context) : IOrderRepository { - - /// public Task FindByIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default) { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs index 90be2a6..4178313 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs @@ -15,8 +15,6 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// public sealed class EfPaymentRepository(TakeoutAppDbContext context) : IPaymentRepository { - - /// public Task FindByIdAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default) { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs index 65666bb..244578a 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs @@ -15,8 +15,6 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductRepository { - - /// public Task FindByIdAsync(long productId, long tenantId, CancellationToken cancellationToken = default) { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs index 3a0934a..53f6b4f 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs @@ -15,8 +15,6 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories; /// public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepository { - - /// public Task FindByIdAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs index 232ad67..64e8608 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs @@ -12,7 +12,6 @@ namespace TakeoutSaaS.Infrastructure.Dictionary.Repositories; /// public sealed class EfDictionaryRepository(DictionaryDbContext context) : IDictionaryRepository { - public Task FindGroupByIdAsync(long id, CancellationToken cancellationToken = default) => context.DictionaryGroups.FirstOrDefaultAsync(group => group.Id == id, cancellationToken); diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs index 1f2bc95..02701f8 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs @@ -13,7 +13,6 @@ namespace TakeoutSaaS.Infrastructure.Identity.Persistence; /// public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIdentityUserRepository { - public Task FindByAccountAsync(string account, CancellationToken cancellationToken = default) => dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Account == account, cancellationToken); diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs index 3276793..83c7b61 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs @@ -12,7 +12,6 @@ namespace TakeoutSaaS.Infrastructure.Identity.Persistence; /// public sealed class EfMiniUserRepository(IdentityDbContext dbContext) : IMiniUserRepository { - public Task FindByOpenIdAsync(string openId, CancellationToken cancellationToken = default) => dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken); diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs index 701a992..6506acb 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs @@ -22,17 +22,21 @@ public sealed class RabbitMqMessagePublisher(RabbitMqConnectionFactory connectio /// public Task PublishAsync(string routingKey, T message, CancellationToken cancellationToken = default) { + // 1. 确保通道可用 EnsureChannel(); var options = optionsMonitor.CurrentValue; var channel = _channel ?? throw new InvalidOperationException("RabbitMQ channel is not available."); + // 2. 声明交换机 channel.ExchangeDeclare(options.Exchange, options.ExchangeType, durable: true, autoDelete: false); + // 3. 序列化消息并设置属性 var body = serializer.Serialize(message); var props = channel.CreateBasicProperties(); props.ContentType = "application/json"; props.DeliveryMode = 2; props.MessageId = Guid.NewGuid().ToString("N"); + // 4. 发布消息 channel.BasicPublish(options.Exchange, routingKey, props, body); logger.LogDebug("发布消息到交换机 {Exchange} RoutingKey {RoutingKey}", options.Exchange, routingKey); return Task.CompletedTask; diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs index 1acba98..ef50289 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs @@ -21,16 +21,19 @@ public sealed class RabbitMqMessageSubscriber(RabbitMqConnectionFactory connecti /// public async Task SubscribeAsync(string queue, string routingKey, Func> handler, CancellationToken cancellationToken = default) { + // 1. 确保通道可用 EnsureChannel(); var options = optionsMonitor.CurrentValue; var channel = _channel ?? throw new InvalidOperationException("RabbitMQ channel is not available."); + // 2. 声明交换机、队列及绑定 channel.ExchangeDeclare(options.Exchange, options.ExchangeType, durable: true, autoDelete: false); channel.QueueDeclare(queue, durable: true, exclusive: false, autoDelete: false); channel.QueueBind(queue, options.Exchange, routingKey); channel.BasicQos(0, options.PrefetchCount, global: false); + // 3. 设置消费者回调 var consumer = new AsyncEventingBasicConsumer(channel); consumer.Received += async (_, ea) => { @@ -61,6 +64,7 @@ public sealed class RabbitMqMessageSubscriber(RabbitMqConnectionFactory connecti } }; + // 4. 开始消费 channel.BasicConsume(queue, autoAck: false, consumer); await Task.CompletedTask.ConfigureAwait(false); } diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs b/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs index c94a47c..166d66e 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs @@ -13,8 +13,6 @@ namespace TakeoutSaaS.Module.Sms.Services; public sealed class AliyunSmsSender(IHttpClientFactory httpClientFactory, IOptionsMonitor optionsMonitor, ILogger logger) : ISmsSender { - private readonly IHttpClientFactory _httpClientFactory = httpClientFactory; - /// public SmsProviderKind Provider => SmsProviderKind.Aliyun; diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs b/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs index f7b9737..72f8083 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs @@ -27,6 +27,7 @@ public sealed class TencentSmsSender(IHttpClientFactory httpClientFactory, IOpti /// public async Task SendAsync(SmsSendRequest request, CancellationToken cancellationToken = default) { + // 1. 读取配置并处理 Mock var options = optionsMonitor.CurrentValue; if (options.UseMock) { @@ -34,6 +35,7 @@ public sealed class TencentSmsSender(IHttpClientFactory httpClientFactory, IOpti return new SmsSendResult { Success = true, Message = "Mocked" }; } + // 2. 构建请求负载与签名所需字段 var tencent = options.Tencent; var payload = BuildPayload(request, tencent); var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); @@ -44,6 +46,7 @@ public sealed class TencentSmsSender(IHttpClientFactory httpClientFactory, IOpti var stringToSign = BuildStringToSign(canonicalRequest, timestamp, date); var signature = Sign(stringToSign, tencent.SecretKey, date); + // 3. 构建 HTTP 请求 using var httpClient = httpClientFactory.CreateClient(nameof(TencentSmsSender)); using var httpRequest = new HttpRequestMessage(HttpMethod.Post, tencent.Endpoint) { @@ -58,6 +61,7 @@ public sealed class TencentSmsSender(IHttpClientFactory httpClientFactory, IOpti httpRequest.Headers.Add("Authorization", $"TC3-HMAC-SHA256 Credential={tencent.SecretId}/{date}/{Service}/tc3_request, SignedHeaders=content-type;host, Signature={signature}"); + // 4. 发送请求并读取响应 var response = await httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); @@ -67,6 +71,7 @@ public sealed class TencentSmsSender(IHttpClientFactory httpClientFactory, IOpti return new SmsSendResult { Success = false, Message = content }; } + // 5. 解析响应 using var doc = JsonDocument.Parse(content); var root = doc.RootElement.GetProperty("Response"); var status = root.GetProperty("SendStatusSet")[0]; diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs index 80d6a81..201e394 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadRequest.cs @@ -12,8 +12,6 @@ namespace TakeoutSaaS.Module.Storage.Models; /// 签名有效期。 public sealed class StorageDirectUploadRequest(string objectKey, string contentType, long contentLength, TimeSpan expires) { - - /// /// 目标对象键。 /// diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs index 3d055ab..b718c18 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs @@ -25,7 +25,6 @@ public sealed class StorageUploadRequest( TimeSpan signedUrlExpires, IDictionary? metadata = null) { - /// /// 对象键。 /// diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Providers/AliyunOssStorageProvider.cs b/src/Modules/TakeoutSaaS.Module.Storage/Providers/AliyunOssStorageProvider.cs index abb74ed..b12fd4b 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Providers/AliyunOssStorageProvider.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Providers/AliyunOssStorageProvider.cs @@ -28,6 +28,7 @@ public sealed class AliyunOssStorageProvider(IOptionsMonitor opt /// public async Task UploadAsync(StorageUploadRequest request, CancellationToken cancellationToken = default) { + // 1. 准备元数据 var options = CurrentOptions; var metadata = new ObjectMetadata { @@ -41,13 +42,16 @@ public sealed class AliyunOssStorageProvider(IOptionsMonitor opt } // Aliyun OSS SDK 支持异步方法,如未支持将同步封装为任务。 + // 2. 上传对象 await PutObjectAsync(options.AliyunOss.Bucket, request.ObjectKey, request.Content, metadata, cancellationToken) .ConfigureAwait(false); + // 3. 生成签名或公有 URL var signedUrl = request.GenerateSignedUrl ? await GenerateDownloadUrlAsync(request.ObjectKey, request.SignedUrlExpires, cancellationToken).ConfigureAwait(false) : null; + // 4. 返回上传结果 return new StorageUploadResult { ObjectKey = request.ObjectKey, @@ -61,10 +65,12 @@ public sealed class AliyunOssStorageProvider(IOptionsMonitor opt /// public Task CreateDirectUploadAsync(StorageDirectUploadRequest request, CancellationToken cancellationToken = default) { + // 1. 计算过期时间并生成直传/下载链接 var expiresAt = DateTimeOffset.UtcNow.Add(request.Expires); var uploadUrl = GeneratePresignedUrl(request.ObjectKey, request.Expires, SignHttpMethod.Put, request.ContentType); var downloadUrl = GeneratePresignedUrl(request.ObjectKey, request.Expires, SignHttpMethod.Get, null); + // 2. 返回直传参数 var result = new StorageDirectUploadResult { UploadUrl = uploadUrl, @@ -80,6 +86,7 @@ public sealed class AliyunOssStorageProvider(IOptionsMonitor opt /// public Task GenerateDownloadUrlAsync(string objectKey, TimeSpan expires, CancellationToken cancellationToken = default) { + // 1. 生成预签名下载 URL var url = GeneratePresignedUrl(objectKey, expires, SignHttpMethod.Get, null); return Task.FromResult(url); } @@ -110,6 +117,7 @@ public sealed class AliyunOssStorageProvider(IOptionsMonitor opt private async Task PutObjectAsync(string bucket, string key, Stream content, ObjectMetadata metadata, CancellationToken cancellationToken) { var client = EnsureClient(); + // SDK 无异步则封装为 Task await Task.Run(() => client.PutObject(bucket, key, content, metadata), cancellationToken).ConfigureAwait(false); } diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs b/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs index afdfe96..f42e34c 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs @@ -65,6 +65,7 @@ public abstract class S3StorageProviderBase : IObjectStorageProvider, IDisposabl /// public virtual async Task UploadAsync(StorageUploadRequest request, CancellationToken cancellationToken = default) { + // 1. 构建上传请求 var putRequest = new PutObjectRequest { BucketName = Bucket, @@ -79,12 +80,15 @@ public abstract class S3StorageProviderBase : IObjectStorageProvider, IDisposabl putRequest.Metadata[kv.Key] = kv.Value; } + // 2. 执行上传 await Client.PutObjectAsync(putRequest, cancellationToken).ConfigureAwait(false); + // 3. 根据需要生成签名 URL var signedUrl = request.GenerateSignedUrl ? GenerateSignedUrl(request.ObjectKey, request.SignedUrlExpires) : null; + // 4. 返回上传结果 return new StorageUploadResult { ObjectKey = request.ObjectKey, @@ -98,10 +102,12 @@ public abstract class S3StorageProviderBase : IObjectStorageProvider, IDisposabl /// public virtual Task CreateDirectUploadAsync(StorageDirectUploadRequest request, CancellationToken cancellationToken = default) { + // 1. 计算过期时间并生成直传 URL var expiresAt = DateTimeOffset.UtcNow.Add(request.Expires); var uploadUrl = GenerateSignedUrl(request.ObjectKey, request.Expires, HttpVerb.PUT, request.ContentType); var signedDownload = GenerateSignedUrl(request.ObjectKey, request.Expires); + // 2. 返回直传参数 var result = new StorageDirectUploadResult { UploadUrl = uploadUrl, @@ -117,6 +123,7 @@ public abstract class S3StorageProviderBase : IObjectStorageProvider, IDisposabl /// public virtual Task GenerateDownloadUrlAsync(string objectKey, TimeSpan expires, CancellationToken cancellationToken = default) { + // 1. 生成下载签名 URL var url = GenerateSignedUrl(objectKey, expires); return Task.FromResult(url); } diff --git a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs index 069202d..9f6b1b9 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantProvider.cs @@ -11,8 +11,6 @@ namespace TakeoutSaaS.Module.Tenancy; /// 租户上下文访问器 public sealed class TenantProvider(ITenantContextAccessor tenantContextAccessor) : ITenantProvider { - - /// public long GetCurrentTenantId() => tenantContextAccessor.Current?.TenantId ?? 0; diff --git a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs index 2a117bf..a5f6530 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs @@ -20,8 +20,6 @@ public sealed class TenantResolutionMiddleware( ITenantContextAccessor tenantContextAccessor, IOptionsMonitor optionsMonitor) { - - /// /// 解析租户并将上下文注入请求。 ///