diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6c8b717 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig +root = true + +[*.cs] +dotnet_diagnostic.SA1600.severity = error +dotnet_diagnostic.SA1601.severity = error +dotnet_diagnostic.SA1615.severity = error +dotnet_diagnostic.SA1629.severity = none +dotnet_diagnostic.SA1202.severity = none +dotnet_diagnostic.SA1200.severity = none +dotnet_diagnostic.SA1623.severity = none +dotnet_diagnostic.SA1111.severity = none +dotnet_diagnostic.SA1101.severity = none diff --git a/.gitignore b/.gitignore index 3857e65..16baddd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bin/ obj/ **/bin/ **/obj/ +.claude/ diff --git a/Directory.Build.props b/Directory.Build.props index 59c4f40..e975914 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,5 +6,7 @@ latest false + + + - diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs index 72f23f4..32b4e5a 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/AuthController.cs @@ -1,8 +1,4 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Contracts; @@ -13,24 +9,22 @@ 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 /// + /// 登录请求。 + /// 取消标记。 + /// 包含访问令牌与刷新令牌的响应。 [HttpPost("login")] [AllowAnonymous] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -43,6 +37,9 @@ public sealed class AuthController(IAdminAuthService authService) : BaseApiContr /// /// 刷新 Token /// + /// 刷新令牌请求。 + /// 取消标记。 + /// 新的访问令牌与刷新令牌。 [HttpPost("refresh")] [AllowAnonymous] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -78,18 +75,22 @@ public sealed class AuthController(IAdminAuthService authService) : BaseApiContr /// } /// /// + /// 取消标记。 + /// 当前用户档案信息。 [HttpGet("profile")] [PermissionAuthorize("identity:profile:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); } @@ -119,6 +120,9 @@ public sealed class AuthController(IAdminAuthService authService) : BaseApiContr /// } /// /// + /// 目标用户 ID。 + /// 取消标记。 + /// 用户权限概览,未找到则返回 404。 [HttpGet("permissions/{userId:long}")] [PermissionAuthorize("identity:permission:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs index fe5d2cf..6f0f953 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/DeliveriesController.cs @@ -1,6 +1,5 @@ using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Deliveries.Commands; using TakeoutSaaS.Application.App.Deliveries.Dto; @@ -16,31 +15,40 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 配送单管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/deliveries")] public sealed class DeliveriesController(IMediator mediator) : BaseApiController { - - /// /// 创建配送单。 /// + /// 创建命令。 + /// 取消标记。 + /// 创建后的配送单。 [HttpPost] [PermissionAuthorize("delivery:create")] [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); } /// /// 查询配送单列表。 /// + /// 订单 ID。 + /// 配送状态。 + /// 页码。 + /// 每页大小。 + /// 排序字段。 + /// 是否倒序。 + /// 取消标记。 + /// 配送单分页列表。 [HttpGet] [PermissionAuthorize("delivery:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -53,6 +61,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,19 +72,26 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } /// /// 获取配送单详情。 /// + /// 配送单 ID。 + /// 取消标记。 + /// 配送单详情或未找到。 [HttpGet("{deliveryOrderId:long}")] [PermissionAuthorize("delivery:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -84,17 +100,26 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController /// /// 更新配送单。 /// + /// 配送单 ID。 + /// 更新命令。 + /// 取消标记。 + /// 更新后的配送单或未找到。 [HttpPut("{deliveryOrderId:long}")] [PermissionAuthorize("delivery:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -103,13 +128,19 @@ public sealed class DeliveriesController(IMediator mediator) : BaseApiController /// /// 删除配送单。 /// + /// 配送单 ID。 + /// 取消标记。 + /// 删除结果,未找到则返回错误。 [HttpDelete("{deliveryOrderId:long}")] [PermissionAuthorize("delivery:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..f2cc347 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/DictionaryController.cs @@ -12,74 +12,101 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 参数字典管理。 /// -/// -/// 初始化字典控制器。 -/// /// 字典服务 [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/dictionaries")] public sealed class DictionaryController(IDictionaryAppService dictionaryAppService) : BaseApiController { - - /// /// 查询字典分组。 /// + /// 分组查询条件。 + /// 取消标记。 + /// 分组列表。 [HttpGet] [PermissionAuthorize("dictionary:group:read")] [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); } /// /// 创建字典分组。 /// + /// 创建分组请求。 + /// 取消标记。 + /// 创建后的分组。 [HttpPost] [PermissionAuthorize("dictionary:group:create")] [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); } /// /// 更新字典分组。 /// + /// 分组 ID。 + /// 更新请求。 + /// 取消标记。 + /// 更新后的分组。 [HttpPut("{groupId:long}")] [PermissionAuthorize("dictionary:group:update")] [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); } /// /// 删除字典分组。 /// + /// 分组 ID。 + /// 取消标记。 + /// 操作结果。 [HttpDelete("{groupId:long}")] [PermissionAuthorize("dictionary:group:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> DeleteGroup(long groupId, CancellationToken cancellationToken) { + // 1. 删除字典分组 await dictionaryAppService.DeleteGroupAsync(groupId, cancellationToken); + + // 2. 返回成功响应 return ApiResponse.Success(); } /// /// 创建字典项。 /// + /// 分组 ID。 + /// 创建请求。 + /// 取消标记。 + /// 创建的字典项。 [HttpPost("{groupId:long}/items")] [PermissionAuthorize("dictionary:item:create")] [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); } @@ -87,35 +114,54 @@ public sealed class DictionaryController(IDictionaryAppService dictionaryAppServ /// /// 更新字典项。 /// + /// 字典项 ID。 + /// 更新请求。 + /// 取消标记。 + /// 更新后的字典项。 [HttpPut("items/{itemId:long}")] [PermissionAuthorize("dictionary:item:update")] [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); } /// /// 删除字典项。 /// + /// 字典项 ID。 + /// 取消标记。 + /// 操作结果。 [HttpDelete("items/{itemId:long}")] [PermissionAuthorize("dictionary:item:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> DeleteItem(long itemId, CancellationToken cancellationToken) { + // 1. 删除字典项 await dictionaryAppService.DeleteItemAsync(itemId, cancellationToken); + + // 2. 返回成功响应 return ApiResponse.Success(); } /// /// 批量获取字典项(命中缓存)。 /// + /// 批量查询请求。 + /// 取消标记。 + /// 分组编码到字典项列表的映射。 [HttpPost("batch")] [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..cd92566 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/FilesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/FilesController.cs @@ -1,6 +1,4 @@ -using System.Linq; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.Storage.Abstractions; using TakeoutSaaS.Application.Storage.Contracts; @@ -19,34 +17,38 @@ namespace TakeoutSaaS.AdminApi.Controllers; [Route("api/admin/v{version:apiVersion}/files")] public sealed class FilesController(IFileStorageService fileStorageService) : BaseApiController { - private readonly IFileStorageService _fileStorageService = fileStorageService; - /// /// 上传图片或文件。 /// + /// 文件上传响应信息。 [HttpPost("upload")] [RequestFormLimits(MultipartBodyLengthLimit = 30 * 1024 * 1024)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..90dc606 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/HealthController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/HealthController.cs @@ -1,6 +1,4 @@ -using System; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Shared.Abstractions.Results; using TakeoutSaaS.Shared.Web.Api; @@ -23,7 +21,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..497cc87 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantCategoriesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantCategoriesController.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Merchants.Commands; using TakeoutSaaS.Application.App.Merchants.Dto; @@ -24,37 +22,54 @@ public sealed class MerchantCategoriesController(IMediator mediator) : BaseApiCo /// /// 列出所有类目。 /// + /// 取消标记。 + /// 类目列表。 [HttpGet] [PermissionAuthorize("merchant_category:read")] [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); } /// /// 新增类目。 /// + /// 创建命令。 + /// 取消标记。 + /// 创建的类目。 [HttpPost] [PermissionAuthorize("merchant_category:create")] [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); } /// /// 删除类目。 /// + /// 类目 ID。 + /// 取消标记。 + /// 删除结果,未找到则返回错误。 [HttpDelete("{categoryId:long}")] [PermissionAuthorize("merchant_category:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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, "类目不存在"); @@ -63,12 +78,18 @@ public sealed class MerchantCategoriesController(IMediator mediator) : BaseApiCo /// /// 批量调整类目排序。 /// + /// 排序命令。 + /// 取消标记。 + /// 执行结果。 [HttpPost("reorder")] [PermissionAuthorize("merchant_category:update")] [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..c5b52d0 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantsController.cs @@ -1,6 +1,5 @@ using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Merchants.Commands; using TakeoutSaaS.Application.App.Merchants.Dto; @@ -16,31 +15,39 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 商户管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/merchants")] public sealed class MerchantsController(IMediator mediator) : BaseApiController { - - /// /// 创建商户。 /// + /// 创建命令。 + /// 取消标记。 + /// 创建后的商户。 [HttpPost] [PermissionAuthorize("merchant:create")] [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); } /// /// 查询商户列表。 /// + /// 状态筛选。 + /// 页码。 + /// 每页大小。 + /// 排序字段。 + /// 是否倒序。 + /// 取消标记。 + /// 商户分页结果。 [HttpGet] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -52,6 +59,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,24 +68,34 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController SortBy = sortBy, SortDescending = sortDesc }, cancellationToken); + + // 2. 返回分页结果 return ApiResponse>.Ok(result); } /// /// 更新商户。 /// + /// 商户 ID。 + /// 更新命令。 + /// 取消标记。 + /// 更新后的商户或未找到。 [HttpPut("{merchantId:long}")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -86,13 +104,19 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 删除商户。 /// + /// 商户 ID。 + /// 取消标记。 + /// 删除结果。 [HttpDelete("{merchantId:long}")] [PermissionAuthorize("merchant:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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, "商户不存在"); @@ -101,13 +125,19 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 获取商户概览。 /// + /// 商户 ID。 + /// 取消标记。 + /// 商户概览或未找到。 [HttpGet("{merchantId:long}")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -116,18 +146,23 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 获取商户详细资料(含证照、合同)。 /// + /// 创建的证照信息。 [HttpGet("{merchantId:long}/detail")] [PermissionAuthorize("merchant:read")] [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); } /// /// 上传商户证照信息(先通过文件上传接口获取 COS 地址)。 /// + /// 创建的证照信息。 [HttpPost("{merchantId:long}/documents")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -136,7 +171,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); } @@ -144,18 +182,23 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 商户证照列表。 /// + /// 商户证照列表。 [HttpGet("{merchantId:long}/documents")] [PermissionAuthorize("merchant:read")] [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); } /// /// 审核指定证照。 /// + /// 审核后的证照信息。 [HttpPost("{merchantId:long}/documents/{documentId:long}/review")] [PermissionAuthorize("merchant:review")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -165,7 +208,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); } @@ -173,6 +219,7 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 新增商户合同。 /// + /// 创建的合同信息。 [HttpPost("{merchantId:long}/contracts")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -181,7 +228,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); } @@ -189,18 +239,23 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 合同列表。 /// + /// 商户合同列表。 [HttpGet("{merchantId:long}/contracts")] [PermissionAuthorize("merchant:read")] [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); } /// /// 更新合同状态(生效/终止等)。 /// + /// 更新后的合同信息。 [HttpPut("{merchantId:long}/contracts/{contractId:long}/status")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -210,7 +265,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); } @@ -218,12 +276,16 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 审核商户(通过/驳回)。 /// + /// 审核后的商户信息。 [HttpPost("{merchantId:long}/review")] [PermissionAuthorize("merchant:review")] [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); } @@ -231,6 +293,7 @@ public sealed class MerchantsController(IMediator mediator) : BaseApiController /// /// 审核日志。 /// + /// 商户审核日志分页结果。 [HttpGet("{merchantId:long}/audits")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -240,19 +303,26 @@ 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); } /// /// 可选商户类目列表。 /// + /// 可选的商户类目列表。 [HttpGet("categories")] [PermissionAuthorize("merchant:read")] [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..d190e96 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/OrdersController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/OrdersController.cs @@ -1,6 +1,5 @@ using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Orders.Commands; using TakeoutSaaS.Application.App.Orders.Dto; @@ -17,31 +16,31 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 订单管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/orders")] public sealed class OrdersController(IMediator mediator) : BaseApiController { - - /// /// 创建订单。 /// + /// 创建的订单信息。 [HttpPost] [PermissionAuthorize("order:create")] [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); } /// /// 查询订单列表。 /// + /// 订单分页列表。 [HttpGet] [PermissionAuthorize("order:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -56,6 +55,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,19 +68,24 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } /// /// 获取订单详情。 /// + /// 订单详情。 [HttpGet("{orderId:long}")] [PermissionAuthorize("order:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -89,17 +94,23 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController /// /// 更新订单。 /// + /// 更新后的订单信息。 [HttpPut("{orderId:long}")] [PermissionAuthorize("order:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -108,13 +119,17 @@ public sealed class OrdersController(IMediator mediator) : BaseApiController /// /// 删除订单。 /// + /// 删除结果。 [HttpDelete("{orderId:long}")] [PermissionAuthorize("order:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..287f893 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/PaymentsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/PaymentsController.cs @@ -1,6 +1,5 @@ using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Payments.Commands; using TakeoutSaaS.Application.App.Payments.Dto; @@ -16,31 +15,31 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 支付记录管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/payments")] public sealed class PaymentsController(IMediator mediator) : BaseApiController { - - /// /// 创建支付记录。 /// + /// 创建的支付记录信息。 [HttpPost] [PermissionAuthorize("payment:create")] [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); } /// /// 查询支付记录列表。 /// + /// 支付记录分页列表。 [HttpGet] [PermissionAuthorize("payment:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -53,6 +52,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,19 +63,24 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } /// /// 获取支付记录详情。 /// + /// 支付记录详情。 [HttpGet("{paymentId:long}")] [PermissionAuthorize("payment:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -84,17 +89,23 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController /// /// 更新支付记录。 /// + /// 更新后的支付记录信息。 [HttpPut("{paymentId:long}")] [PermissionAuthorize("payment:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -103,13 +114,17 @@ public sealed class PaymentsController(IMediator mediator) : BaseApiController /// /// 删除支付记录。 /// + /// 删除结果。 [HttpDelete("{paymentId:long}")] [PermissionAuthorize("payment:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..5eda408 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/PermissionsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/PermissionsController.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.Identity.Commands; using TakeoutSaaS.Application.Identity.Contracts; @@ -26,6 +25,9 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle /// /// 示例:GET /api/admin/v1/permissions?keyword=order&page=1&pageSize=20 /// + /// 查询条件。 + /// 取消标记。 + /// 权限的分页结果。 [HttpGet] [PermissionAuthorize("identity:permission:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -38,6 +40,9 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle /// /// 创建权限。 /// + /// 创建命令。 + /// 取消标记。 + /// 创建的权限。 [HttpPost] [PermissionAuthorize("identity:permission:create")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -50,6 +55,10 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle /// /// 更新权限。 /// + /// 权限 ID。 + /// 更新命令。 + /// 取消标记。 + /// 更新后的权限,未找到时返回 404。 [HttpPut("{permissionId:long}")] [PermissionAuthorize("identity:permission:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -66,6 +75,9 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle /// /// 删除权限。 /// + /// 权限 ID。 + /// 取消标记。 + /// 删除结果。 [HttpDelete("{permissionId:long}")] [PermissionAuthorize("identity:permission:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs index 3eb9fe1..0dfc911 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/ProductsController.cs @@ -1,6 +1,5 @@ using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Products.Commands; using TakeoutSaaS.Application.App.Products.Dto; @@ -16,31 +15,31 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 商品管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/products")] public sealed class ProductsController(IMediator mediator) : BaseApiController { - - /// /// 创建商品。 /// + /// 创建的商品信息。 [HttpPost] [PermissionAuthorize("product:create")] [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); } /// /// 查询商品列表。 /// + /// 商品分页列表。 [HttpGet] [PermissionAuthorize("product:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -54,6 +53,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,19 +65,24 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } /// /// 获取商品详情。 /// + /// 商品详情。 [HttpGet("{productId:long}")] [PermissionAuthorize("product:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -86,17 +91,23 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController /// /// 更新商品。 /// + /// 更新后的商品信息。 [HttpPut("{productId:long}")] [PermissionAuthorize("product:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -105,13 +116,17 @@ public sealed class ProductsController(IMediator mediator) : BaseApiController /// /// 删除商品。 /// + /// 删除结果。 [HttpDelete("{productId:long}")] [PermissionAuthorize("product:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..7c97263 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/RolesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/RolesController.cs @@ -1,9 +1,7 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.Identity.Commands; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Application.Identity.Queries; @@ -27,12 +25,16 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// /// 示例:GET /api/admin/v1/roles/templates /// + /// 角色模板列表。 [HttpGet("templates")] [PermissionAuthorize("identity:role:read")] [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); } @@ -42,13 +44,17 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// /// 示例:GET /api/admin/v1/roles/templates/tenant-admin /// + /// 角色模板详情。 [HttpGet("templates/{templateCode}")] [PermissionAuthorize("identity:role:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -57,18 +63,23 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// /// 创建角色模板。 /// + /// 创建的角色模板信息。 [HttpPost("templates")] [PermissionAuthorize("role-template:create")] [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); } /// /// 更新角色模板。 /// + /// 更新后的角色模板信息。 [HttpPut("templates/{templateCode}")] [PermissionAuthorize("role-template:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -78,8 +89,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); @@ -88,12 +104,16 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// /// 删除角色模板。 /// + /// 删除结果。 [HttpDelete("templates/{templateCode}")] [PermissionAuthorize("role-template:delete")] [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); } @@ -104,6 +124,7 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// 示例:POST /api/admin/v1/roles/templates/store-manager/copy /// Body: { "roleName": "新区店长" } /// + /// 创建的角色信息。 [HttpPost("templates/{templateCode}/copy")] [PermissionAuthorize("identity:role:create")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -112,7 +133,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); } @@ -124,6 +148,7 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// 示例:POST /api/admin/v1/roles/templates/init /// Body: { "templateCodes": ["tenant-admin","store-manager","store-staff"] } /// + /// 创建的角色列表。 [HttpPost("templates/init")] [PermissionAuthorize("identity:role:create")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -131,7 +156,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); } @@ -144,38 +172,52 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// GET /api/admin/v1/roles?keyword=ops&page=1&pageSize=20 /// Header: Authorization: Bearer <JWT> + X-Tenant-Id /// + /// 角色分页结果。 [HttpGet] [PermissionAuthorize("identity:role:read")] [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); } /// /// 创建角色。 /// + /// 创建的角色信息。 [HttpPost] [PermissionAuthorize("identity:role:create")] [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); } /// /// 更新角色。 /// + /// 更新后的角色信息。 [HttpPut("{roleId:long}")] [PermissionAuthorize("identity:role:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -184,12 +226,16 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// /// 删除角色。 /// + /// 删除结果。 [HttpDelete("{roleId:long}")] [PermissionAuthorize("identity:role:delete")] [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); } @@ -197,12 +243,16 @@ public sealed class RolesController(IMediator mediator) : BaseApiController /// /// 绑定角色权限(覆盖式)。 /// + /// 是否绑定成功。 [HttpPut("{roleId:long}/permissions")] [PermissionAuthorize("identity:role:bind-permission")] [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 3acccb7..7d51d20 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/StoresController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/StoresController.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Dto; @@ -17,31 +16,31 @@ namespace TakeoutSaaS.AdminApi.Controllers; /// /// 门店管理。 /// -/// -/// 初始化控制器。 -/// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/stores")] public sealed class StoresController(IMediator mediator) : BaseApiController { - - /// /// 创建门店。 /// + /// 创建的门店信息。 [HttpPost] [PermissionAuthorize("store:create")] [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); } /// /// 查询门店列表。 /// + /// 门店分页列表。 [HttpGet] [PermissionAuthorize("store:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -54,6 +53,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, @@ -64,19 +64,24 @@ public sealed class StoresController(IMediator mediator) : BaseApiController SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } /// /// 获取门店详情。 /// + /// 门店详情。 [HttpGet("{storeId:long}")] [PermissionAuthorize("store:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -85,17 +90,23 @@ public sealed class StoresController(IMediator mediator) : BaseApiController /// /// 更新门店。 /// + /// 更新后的门店信息。 [HttpPut("{storeId:long}")] [PermissionAuthorize("store:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -104,13 +115,17 @@ public sealed class StoresController(IMediator mediator) : BaseApiController /// /// 删除门店。 /// + /// 删除结果。 [HttpDelete("{storeId:long}")] [PermissionAuthorize("store:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..7a2fcb2 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/SystemParametersController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/SystemParametersController.cs @@ -1,6 +1,5 @@ using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.SystemParameters.Commands; using TakeoutSaaS.Application.App.SystemParameters.Dto; @@ -26,18 +25,23 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont /// /// 创建系统参数。 /// + /// 创建的系统参数信息。 [HttpPost] [PermissionAuthorize("system-parameter:create")] [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); } /// /// 查询系统参数列表。 /// + /// 分页的系统参数列表。 [HttpGet] [PermissionAuthorize("system-parameter:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -50,6 +54,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,19 +65,24 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont SortDescending = sortDesc }, cancellationToken); + // 2. 返回分页结果 return ApiResponse>.Ok(result); } /// /// 获取系统参数详情。 /// + /// 系统参数详情。 [HttpGet("{parameterId:long}")] [PermissionAuthorize("system-parameter:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -81,18 +91,23 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont /// /// 更新系统参数。 /// + /// 更新后的系统参数信息。 [HttpPut("{parameterId:long}")] [PermissionAuthorize("system-parameter:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -101,13 +116,17 @@ public sealed class SystemParametersController(IMediator mediator) : BaseApiCont /// /// 删除系统参数。 /// + /// 删除结果。 [HttpDelete("{parameterId:long}")] [PermissionAuthorize("system-parameter:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..b99b30f 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantAnnouncementsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantAnnouncementsController.cs @@ -1,8 +1,7 @@ -using System.ComponentModel.DataAnnotations; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -23,26 +22,36 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC /// /// 分页查询公告。 /// + /// 租户公告分页结果。 [HttpGet] [PermissionAuthorize("tenant-announcement:read")] [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); } /// /// 公告详情。 /// + /// 租户公告详情。 [HttpGet("{announcementId:long}")] [PermissionAuthorize("tenant-announcement:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -51,12 +60,16 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC /// /// 创建公告。 /// + /// 创建的公告信息。 [HttpPost] [PermissionAuthorize("tenant-announcement:create")] [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); } @@ -64,14 +77,20 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC /// /// 更新公告。 /// + /// 更新后的公告信息。 [HttpPut("{announcementId:long}")] [PermissionAuthorize("tenant-announcement:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -80,25 +99,33 @@ public sealed class TenantAnnouncementsController(IMediator mediator) : BaseApiC /// /// 删除公告。 /// + /// 删除结果。 [HttpDelete("{announcementId:long}")] [PermissionAuthorize("tenant-announcement:delete")] [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); } /// /// 标记公告已读。 /// + /// 标记已读后的公告信息。 [HttpPost("{announcementId:long}/read")] [PermissionAuthorize("tenant-announcement:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..9de398f 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantBillingsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantBillingsController.cs @@ -1,8 +1,7 @@ -using System.ComponentModel.DataAnnotations; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -23,26 +22,36 @@ public sealed class TenantBillingsController(IMediator mediator) : BaseApiContro /// /// 分页查询账单。 /// + /// 租户账单分页结果。 [HttpGet] [PermissionAuthorize("tenant-bill:read")] [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); } /// /// 账单详情。 /// + /// 租户账单详情。 [HttpGet("{billingId:long}")] [PermissionAuthorize("tenant-bill:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -51,12 +60,16 @@ public sealed class TenantBillingsController(IMediator mediator) : BaseApiContro /// /// 创建账单。 /// + /// 创建的账单信息。 [HttpPost] [PermissionAuthorize("tenant-bill:create")] [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); } @@ -64,14 +77,20 @@ public sealed class TenantBillingsController(IMediator mediator) : BaseApiContro /// /// 标记账单已支付。 /// + /// 标记支付后的账单信息。 [HttpPost("{billingId:long}/pay")] [PermissionAuthorize("tenant-bill:pay")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..18c78ce 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantNotificationsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantNotificationsController.cs @@ -1,6 +1,5 @@ using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; @@ -22,26 +21,36 @@ public sealed class TenantNotificationsController(IMediator mediator) : BaseApiC /// /// 分页查询通知。 /// + /// 租户通知分页结果。 [HttpGet] [PermissionAuthorize("tenant-notification:read")] [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); } /// /// 标记通知已读。 /// + /// 标记已读后的通知信息。 [HttpPost("{notificationId:long}/read")] [PermissionAuthorize("tenant-notification:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..455c095 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantPackagesController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantPackagesController.cs @@ -1,8 +1,7 @@ -using System.ComponentModel.DataAnnotations; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -23,25 +22,37 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro /// /// 分页查询租户套餐。 /// + /// 查询条件。 + /// 取消标记。 + /// 租户套餐分页结果。 [HttpGet] [PermissionAuthorize("tenant-package:read")] [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); } /// /// 查看套餐详情。 /// + /// 套餐 ID。 + /// 取消标记。 + /// 套餐详情或未找到。 [HttpGet("{tenantPackageId:long}")] [PermissionAuthorize("tenant-package:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -50,26 +61,41 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro /// /// 创建套餐。 /// + /// 创建命令。 + /// 取消标记。 + /// 创建后的套餐。 [HttpPost] [PermissionAuthorize("tenant-package:create")] [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); } /// /// 更新套餐。 /// + /// 套餐 ID。 + /// 更新命令。 + /// 取消标记。 + /// 更新后的套餐或未找到。 [HttpPut("{tenantPackageId:long}")] [PermissionAuthorize("tenant-package:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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); @@ -78,12 +104,18 @@ public sealed class TenantPackagesController(IMediator mediator) : BaseApiContro /// /// 删除套餐。 /// + /// 套餐 ID。 + /// 取消标记。 + /// 删除结果。 [HttpDelete("{tenantPackageId:long}")] [PermissionAuthorize("tenant-package:delete")] [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..f3bc0a0 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantsController.cs @@ -1,13 +1,11 @@ -using System.ComponentModel.DataAnnotations; using MediatR; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; using TakeoutSaaS.Module.Authorization.Attributes; -using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Results; using TakeoutSaaS.Shared.Web.Api; @@ -24,42 +22,55 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController /// /// 注册租户并初始化套餐。 /// + /// 注册的租户信息。 [HttpPost] [PermissionAuthorize("tenant:create")] [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); } /// /// 分页查询租户。 /// + /// 租户分页结果。 [HttpGet] [PermissionAuthorize("tenant:read")] [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); } /// /// 查看租户详情。 /// + /// 租户详情。 [HttpGet("{tenantId:long}")] [PermissionAuthorize("tenant:read")] [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); } /// /// 提交或更新实名认证资料。 /// + /// 提交的实名认证信息。 [HttpPost("{tenantId:long}/verification")] [PermissionAuthorize("tenant:review")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -68,27 +79,39 @@ 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); } /// /// 审核租户。 /// + /// 审核后的租户信息。 [HttpPost("{tenantId:long}/review")] [PermissionAuthorize("tenant:review")] [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); } /// /// 创建或续费租户订阅。 /// + /// 创建或续费的订阅信息。 [HttpPost("{tenantId:long}/subscriptions")] [PermissionAuthorize("tenant:subscription")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -97,7 +120,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); } @@ -105,6 +131,7 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController /// /// 套餐升降配。 /// + /// 更新后的订阅信息。 [HttpPut("{tenantId:long}/subscriptions/{subscriptionId:long}/plan")] [PermissionAuthorize("tenant:subscription")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -114,14 +141,20 @@ 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); } /// /// 查询审核日志。 /// + /// 租户审核日志分页结果。 [HttpGet("{tenantId:long}/audits")] [PermissionAuthorize("tenant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -131,7 +164,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); } @@ -140,6 +176,7 @@ public sealed class TenantsController(IMediator mediator) : BaseApiController /// 配额校验并占用额度(门店/账号/短信/配送)。 /// /// 需在请求头携带 X-Tenant-Id 对应的租户。 + /// 配额校验结果。 [HttpPost("{tenantId:long}/quotas/check")] [PermissionAuthorize("tenant:quota:check")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] @@ -148,7 +185,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..c6a4a04 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Controllers/UserPermissionsController.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/UserPermissionsController.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Contracts; @@ -51,6 +50,9 @@ public sealed class UserPermissionsController(IAdminAuthService authService) : B /// } /// /// + /// 搜索条件。 + /// 取消标记。 + /// 分页的用户权限概览。 [HttpGet] [PermissionAuthorize("identity:permission:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] @@ -58,6 +60,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 +69,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..3b0c4b6 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Program.cs +++ b/src/Api/TakeoutSaaS.AdminApi/Program.cs @@ -1,10 +1,4 @@ -using System; -using System.Linq; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -26,13 +20,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 +44,7 @@ builder.Host.UseSerilog((context, _, configuration) => outputTemplate: logTemplate); }); +// 4. 注册通用 Web 能力与 Swagger builder.Services.AddSharedWebCore(); builder.Services.AddSharedSwagger(options => { @@ -54,6 +52,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 +71,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 +88,6 @@ builder.Services.AddOpenTelemetry() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddEntityFrameworkCoreInstrumentation(); - if (!string.IsNullOrWhiteSpace(otelEndpoint)) { tracing.AddOtlpExporter(exporter => @@ -107,7 +108,6 @@ builder.Services.AddOpenTelemetry() .AddHttpClientInstrumentation() .AddRuntimeInstrumentation() .AddPrometheusExporter(); - if (!string.IsNullOrWhiteSpace(otelEndpoint)) { metrics.AddOtlpExporter(exporter => @@ -122,6 +122,7 @@ builder.Services.AddOpenTelemetry() } }); +// 7. 解析并配置 CORS var adminOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:Admin"); builder.Services.AddCors(options => { @@ -131,8 +132,8 @@ builder.Services.AddCors(options => }); }); +// 8. 构建应用并配置中间件管道 var app = builder.Build(); - app.UseCors("AdminApiCors"); app.UseTenantResolution(); app.UseSharedWebCore(); @@ -140,12 +141,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 +156,7 @@ static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionK .ToArray() ?? []; } +// 10. 构建 CORS 策略 static void ConfigureCorsPolicy(CorsPolicyBuilder policy, string[] origins) { if (origins.Length == 0) diff --git a/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs b/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs index 332dec2..11e7d10 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/AuthController.cs @@ -10,38 +10,46 @@ namespace TakeoutSaaS.MiniApi.Controllers; /// /// 小程序登录认证 /// -/// -/// 小程序登录认证 -/// -/// +/// 提供小程序端的微信登录与 Token 刷新能力。 +/// 小程序认证服务 [ApiVersion("1.0")] [Authorize] [Route("api/mini/v{version:apiVersion}/auth")] public sealed class AuthController(IMiniAuthService authService) : BaseApiController { - - /// /// 微信登录 /// + /// 微信登录请求。 + /// 取消标记。 + /// 包含访问令牌与刷新令牌的响应。 [HttpPost("wechat/login")] [AllowAnonymous] [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); } /// /// 刷新 Token /// + /// 刷新令牌请求。 + /// 取消标记。 + /// 新的访问令牌与刷新令牌。 [HttpPost("refresh")] [AllowAnonymous] [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..0d651fb 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/FilesController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/FilesController.cs @@ -1,6 +1,4 @@ -using System.Linq; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.Storage.Abstractions; using TakeoutSaaS.Application.Storage.Contracts; @@ -19,34 +17,41 @@ namespace TakeoutSaaS.MiniApi.Controllers; [Route("api/mini/v{version:apiVersion}/files")] public sealed class FilesController(IFileStorageService fileStorageService) : BaseApiController { - private readonly IFileStorageService _fileStorageService = fileStorageService; - /// /// 上传图片或文件。 /// + /// 上传文件。 + /// 上传类型。 + /// 取消标记。 + /// 上传结果,包含访问链接等信息。 [HttpPost("upload")] [RequestFormLimits(MultipartBodyLengthLimit = 30 * 1024 * 1024)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..e19775c 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/HealthController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/HealthController.cs @@ -1,6 +1,4 @@ -using System; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Shared.Abstractions.Results; using TakeoutSaaS.Shared.Web.Api; @@ -23,7 +21,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..7411644 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Controllers/MeController.cs @@ -1,8 +1,4 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Contracts; @@ -16,31 +12,31 @@ namespace TakeoutSaaS.MiniApi.Controllers; /// /// 当前用户信息 /// -/// -/// -/// -/// +/// 提供小程序端当前用户档案查询。 +/// 小程序认证服务 [ApiVersion("1.0")] [Authorize] [Route("api/mini/v{version:apiVersion}/me")] public sealed class MeController(IMiniAuthService authService) : BaseApiController { - - /// /// 获取用户档案 /// + /// 取消标记。 + /// 当前用户档案信息。 [HttpGet] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [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..8369004 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Program.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Program.cs @@ -1,7 +1,4 @@ -using System; -using System.Linq; using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.Extensions.Configuration; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -18,9 +15,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 +35,7 @@ builder.Host.UseSerilog((_, _, configuration) => outputTemplate: logTemplate); }); +// 3. 注册通用 Web 能力与 Swagger builder.Services.AddSharedWebCore(); builder.Services.AddSharedSwagger(options => { @@ -43,6 +43,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 +53,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 +106,7 @@ builder.Services.AddOpenTelemetry() } }); +// 6. 配置 CORS var miniOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:Mini"); builder.Services.AddCors(options => { @@ -111,6 +116,7 @@ builder.Services.AddCors(options => }); }); +// 7. 构建应用并配置中间件管道 var app = builder.Build(); app.UseCors("MiniApiCors"); @@ -123,6 +129,7 @@ app.MapPrometheusScrapingEndpoint(); app.MapControllers(); app.Run(); +// 8. 解析配置中的 CORS 域名 static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionKey) { var origins = configuration.GetSection(sectionKey).Get(); @@ -132,6 +139,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..08fe92b 100644 --- a/src/Api/TakeoutSaaS.UserApi/Controllers/HealthController.cs +++ b/src/Api/TakeoutSaaS.UserApi/Controllers/HealthController.cs @@ -1,6 +1,4 @@ -using System; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Shared.Abstractions.Results; using TakeoutSaaS.Shared.Web.Api; @@ -23,7 +21,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..d59e501 100644 --- a/src/Api/TakeoutSaaS.UserApi/Program.cs +++ b/src/Api/TakeoutSaaS.UserApi/Program.cs @@ -1,7 +1,4 @@ -using System; -using System.Linq; using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.Extensions.Configuration; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -12,9 +9,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 +29,7 @@ builder.Host.UseSerilog((_, _, configuration) => outputTemplate: logTemplate); }); +// 3. 注册通用 Web 能力与 Swagger builder.Services.AddSharedWebCore(); builder.Services.AddSharedSwagger(options => { @@ -37,8 +37,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 +94,7 @@ builder.Services.AddOpenTelemetry() } }); +// 6. 配置 CORS var userOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:User"); builder.Services.AddCors(options => { @@ -99,6 +104,7 @@ builder.Services.AddCors(options => }); }); +// 7. 构建应用并配置中间件管道 var app = builder.Build(); app.UseCors("UserApiCors"); @@ -111,6 +117,7 @@ app.MapPrometheusScrapingEndpoint(); app.MapControllers(); app.Run(); +// 8. 解析配置中的 CORS 域名 static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionKey) { var origins = configuration.GetSection(sectionKey).Get(); @@ -120,6 +127,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/Extensions/AppApplicationServiceCollectionExtensions.cs b/src/Application/TakeoutSaaS.Application/App/Extensions/AppApplicationServiceCollectionExtensions.cs index 62284b1..c4af1ce 100644 --- a/src/Application/TakeoutSaaS.Application/App/Extensions/AppApplicationServiceCollectionExtensions.cs +++ b/src/Application/TakeoutSaaS.Application/App/Extensions/AppApplicationServiceCollectionExtensions.cs @@ -1,7 +1,7 @@ -using System.Reflection; using FluentValidation; using MediatR; using Microsoft.Extensions.DependencyInjection; +using System.Reflection; using TakeoutSaaS.Application.App.Common.Behaviors; namespace TakeoutSaaS.Application.App.Extensions; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/AddMerchantDocumentCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/AddMerchantDocumentCommand.cs index cf0d4a3..bc40f63 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/AddMerchantDocumentCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/AddMerchantDocumentCommand.cs @@ -1,6 +1,5 @@ -using System; -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Domain.Merchants.Enums; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantCategoryCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantCategoryCommand.cs index 115d4ba..fe01142 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantCategoryCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantCategoryCommand.cs @@ -1,5 +1,5 @@ -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Merchants.Dto; namespace TakeoutSaaS.Application.App.Merchants.Commands; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantContractCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantContractCommand.cs index 176cf98..957ba01 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantContractCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/CreateMerchantContractCommand.cs @@ -1,6 +1,5 @@ -using System; -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Merchants.Dto; namespace TakeoutSaaS.Application.App.Merchants.Commands; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/DeleteMerchantCategoryCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/DeleteMerchantCategoryCommand.cs index ffa0326..98cfd21 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/DeleteMerchantCategoryCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/DeleteMerchantCategoryCommand.cs @@ -1,5 +1,5 @@ -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; namespace TakeoutSaaS.Application.App.Merchants.Commands; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReorderMerchantCategoriesCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReorderMerchantCategoriesCommand.cs index 79bd657..542e6aa 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReorderMerchantCategoriesCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReorderMerchantCategoriesCommand.cs @@ -1,6 +1,5 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; namespace TakeoutSaaS.Application.App.Merchants.Commands; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantCommand.cs index 792cddc..362e20b 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantCommand.cs @@ -1,5 +1,5 @@ -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Merchants.Dto; namespace TakeoutSaaS.Application.App.Merchants.Commands; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantDocumentCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantDocumentCommand.cs index 23d6e2a..0e01c26 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantDocumentCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/ReviewMerchantDocumentCommand.cs @@ -1,5 +1,5 @@ -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Merchants.Dto; namespace TakeoutSaaS.Application.App.Merchants.Commands; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/UpdateMerchantContractStatusCommand.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/UpdateMerchantContractStatusCommand.cs index 3514b58..f89f9bf 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/UpdateMerchantContractStatusCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Commands/UpdateMerchantContractStatusCommand.cs @@ -1,6 +1,5 @@ -using System; -using System.ComponentModel.DataAnnotations; using MediatR; +using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Domain.Merchants.Enums; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantCategoryDto.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantCategoryDto.cs index 0d8636b..c8741ca 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantCategoryDto.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantCategoryDto.cs @@ -1,5 +1,3 @@ -using System; - namespace TakeoutSaaS.Application.App.Merchants.Dto; /// diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantDetailDto.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantDetailDto.cs index 4c8dedb..2fa8b95 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantDetailDto.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Dto/MerchantDetailDto.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace TakeoutSaaS.Application.App.Merchants.Dto; /// 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..645189c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCategoryCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/CreateMerchantCategoryCommandHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Merchants.Commands; using TakeoutSaaS.Application.App.Merchants.Dto; @@ -18,22 +17,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 +41,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..2fd67e5 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantAuditLogsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantAuditLogsQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Application.App.Merchants.Queries; @@ -16,20 +15,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..4ffc992 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantCategoriesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantCategoriesQueryHandler.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Merchants.Queries; using TakeoutSaaS.Domain.Merchants.Repositories; @@ -15,14 +13,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..8ce236b 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantContractsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantContractsQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Application.App.Merchants.Queries; @@ -17,16 +16,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..c93f19c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDocumentsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/GetMerchantDocumentsQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Application.App.Merchants.Queries; @@ -17,16 +16,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..c0ba13b 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ListMerchantCategoriesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ListMerchantCategoriesQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Application.App.Merchants.Queries; @@ -15,13 +14,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..cb71205 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReorderMerchantCategoriesCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReorderMerchantCategoriesCommandHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Merchants.Commands; using TakeoutSaaS.Domain.Merchants.Repositories; @@ -16,15 +15,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 +33,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..7260afb 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantDocumentCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Handlers/ReviewMerchantDocumentCommandHandler.cs @@ -1,8 +1,8 @@ using MediatR; using TakeoutSaaS.Application.App.Merchants.Commands; using TakeoutSaaS.Application.App.Merchants.Dto; -using TakeoutSaaS.Domain.Merchants.Enums; using TakeoutSaaS.Domain.Merchants.Entities; +using TakeoutSaaS.Domain.Merchants.Enums; using TakeoutSaaS.Domain.Merchants.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; @@ -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/Merchants/MerchantMapping.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/MerchantMapping.cs index 8c61873..4b52d54 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/MerchantMapping.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/MerchantMapping.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Domain.Merchants.Entities; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantCategoriesQuery.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantCategoriesQuery.cs index c55fd86..558bcc6 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantCategoriesQuery.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantCategoriesQuery.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; namespace TakeoutSaaS.Application.App.Merchants.Queries; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantContractsQuery.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantContractsQuery.cs index 8940465..bdf17d6 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantContractsQuery.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantContractsQuery.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.App.Merchants.Dto; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantDocumentsQuery.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantDocumentsQuery.cs index 997f02a..f3dd1bf 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantDocumentsQuery.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/GetMerchantDocumentsQuery.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.App.Merchants.Dto; diff --git a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/ListMerchantCategoriesQuery.cs b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/ListMerchantCategoriesQuery.cs index 4e4ef4a..5fa29e9 100644 --- a/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/ListMerchantCategoriesQuery.cs +++ b/src/Application/TakeoutSaaS.Application/App/Merchants/Queries/ListMerchantCategoriesQuery.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.App.Merchants.Dto; diff --git a/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/CreateOrderCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/CreateOrderCommandHandler.cs index a7b1289..9b3cc0f 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,18 @@ 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); - // 5. 返回 DTO + await orderRepository.SaveChangesAsync(cancellationToken); + + // 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/Payments/Handlers/SearchPaymentsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Payments/Handlers/SearchPaymentsQueryHandler.cs index 9d6c4ae..a265022 100644 --- a/src/Application/TakeoutSaaS.Application/App/Payments/Handlers/SearchPaymentsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Payments/Handlers/SearchPaymentsQueryHandler.cs @@ -1,7 +1,6 @@ using MediatR; using TakeoutSaaS.Application.App.Payments.Dto; using TakeoutSaaS.Application.App.Payments.Queries; -using TakeoutSaaS.Domain.Payments.Entities; using TakeoutSaaS.Domain.Payments.Repositories; using TakeoutSaaS.Shared.Abstractions.Results; using TakeoutSaaS.Shared.Abstractions.Tenancy; 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..169ddbb 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CheckTenantQuotaCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CheckTenantQuotaCommandHandler.cs @@ -1,4 +1,3 @@ -using System; using MediatR; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; @@ -24,18 +23,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 +51,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 +70,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..76aafb6 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAuditLogsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantAuditLogsQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -13,20 +12,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..1600ecf 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantAnnouncementReadCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/MarkTenantAnnouncementReadCommandHandler.cs @@ -3,8 +3,6 @@ using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Repositories; -using TakeoutSaaS.Shared.Abstractions.Constants; -using TakeoutSaaS.Shared.Abstractions.Exceptions; using TakeoutSaaS.Shared.Abstractions.Security; namespace TakeoutSaaS.Application.App.Tenants.Handlers; @@ -20,15 +18,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 +45,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..2c15546 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantAnnouncementsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantAnnouncementsQueryHandler.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -20,22 +18,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 +68,7 @@ public sealed class SearchTenantAnnouncementsQueryHandler( } } + // 6. 映射 DTO 并带上已读状态 var items = pageItems .Select(a => { @@ -73,6 +77,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..66821fc 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantBillsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantBillsQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -15,13 +14,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..294a639 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantNotificationsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantNotificationsQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -15,6 +14,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 +23,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..21c3ba7 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantPackagesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantPackagesQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -16,8 +15,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 +29,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..755b911 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/SearchTenantsQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Application.App.Tenants.Queries; @@ -13,27 +12,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/Abstractions/IDictionaryCache.cs b/src/Application/TakeoutSaaS.Application/Dictionary/Abstractions/IDictionaryCache.cs index ebdc59f..f9ea54b 100644 --- a/src/Application/TakeoutSaaS.Application/Dictionary/Abstractions/IDictionaryCache.cs +++ b/src/Application/TakeoutSaaS.Application/Dictionary/Abstractions/IDictionaryCache.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Dictionary.Models; namespace TakeoutSaaS.Application.Dictionary.Abstractions; diff --git a/src/Application/TakeoutSaaS.Application/Dictionary/Contracts/CreateDictionaryItemRequest.cs b/src/Application/TakeoutSaaS.Application/Dictionary/Contracts/CreateDictionaryItemRequest.cs index 668d369..b19ccce 100644 --- a/src/Application/TakeoutSaaS.Application/Dictionary/Contracts/CreateDictionaryItemRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Dictionary/Contracts/CreateDictionaryItemRequest.cs @@ -1,6 +1,6 @@ +using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; using TakeoutSaaS.Shared.Abstractions.Serialization; -using System.ComponentModel.DataAnnotations; namespace TakeoutSaaS.Application.Dictionary.Contracts; diff --git a/src/Application/TakeoutSaaS.Application/Dictionary/Models/DictionaryGroupDto.cs b/src/Application/TakeoutSaaS.Application/Dictionary/Models/DictionaryGroupDto.cs index 95a81f0..cdee6bf 100644 --- a/src/Application/TakeoutSaaS.Application/Dictionary/Models/DictionaryGroupDto.cs +++ b/src/Application/TakeoutSaaS.Application/Dictionary/Models/DictionaryGroupDto.cs @@ -1,6 +1,5 @@ -using TakeoutSaaS.Domain.Dictionary.Enums; - using System.Text.Json.Serialization; +using TakeoutSaaS.Domain.Dictionary.Enums; using TakeoutSaaS.Shared.Abstractions.Serialization; namespace TakeoutSaaS.Application.Dictionary.Models; diff --git a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs index 5ff8e9a..65616a8 100644 --- a/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs +++ b/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryAppService.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.Extensions.Logging; using TakeoutSaaS.Application.Dictionary.Abstractions; using TakeoutSaaS.Application.Dictionary.Contracts; @@ -21,18 +20,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 +45,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 +54,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 +72,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 +85,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 +100,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 +113,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 +131,7 @@ public sealed class DictionaryAppService( IsEnabled = request.IsEnabled }; + // 3. 持久化并失效缓存 await repository.AddItemAsync(item, cancellationToken); await repository.SaveChangesAsync(cancellationToken); await InvalidateCacheAsync(group, cancellationToken); @@ -128,16 +141,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 +162,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 +176,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 +188,7 @@ public sealed class DictionaryAppService( return new Dictionary>(StringComparer.OrdinalIgnoreCase); } + // 2. 按租户合并系统与业务字典 var tenantId = tenantProvider.GetCurrentTenantId(); var result = new Dictionary>(StringComparer.OrdinalIgnoreCase); @@ -190,6 +210,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 +222,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 +291,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/Abstractions/IAdminAuthService.cs b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IAdminAuthService.cs index d1cd0a7..8961c72 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IAdminAuthService.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IAdminAuthService.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Shared.Abstractions.Results; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IJwtTokenService.cs b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IJwtTokenService.cs index 4235181..5bf401e 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IJwtTokenService.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IJwtTokenService.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Identity.Contracts; namespace TakeoutSaaS.Application.Identity.Abstractions; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/ILoginRateLimiter.cs b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/ILoginRateLimiter.cs index f4a7c5c..66f91a9 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/ILoginRateLimiter.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/ILoginRateLimiter.cs @@ -1,6 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; - namespace TakeoutSaaS.Application.Identity.Abstractions; /// diff --git a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IMiniAuthService.cs b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IMiniAuthService.cs index c7a509e..8c5250a 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IMiniAuthService.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IMiniAuthService.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Identity.Contracts; namespace TakeoutSaaS.Application.Identity.Abstractions; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IRefreshTokenStore.cs b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IRefreshTokenStore.cs index 29a1bf6..938b4cf 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IRefreshTokenStore.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IRefreshTokenStore.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Identity.Models; namespace TakeoutSaaS.Application.Identity.Abstractions; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IWeChatAuthService.cs b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IWeChatAuthService.cs index 417c8b9..c53b72c 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IWeChatAuthService.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Abstractions/IWeChatAuthService.cs @@ -1,6 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; - namespace TakeoutSaaS.Application.Identity.Abstractions; /// diff --git a/src/Application/TakeoutSaaS.Application/Identity/Commands/CreateRoleTemplateCommand.cs b/src/Application/TakeoutSaaS.Application/Identity/Commands/CreateRoleTemplateCommand.cs index 7ca69d1..de2bd4c 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Commands/CreateRoleTemplateCommand.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Commands/CreateRoleTemplateCommand.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Commands/InitializeRoleTemplatesCommand.cs b/src/Application/TakeoutSaaS.Application/Identity/Commands/InitializeRoleTemplatesCommand.cs index dc1a583..7eb39f9 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Commands/InitializeRoleTemplatesCommand.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Commands/InitializeRoleTemplatesCommand.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Commands/UpdateRoleTemplateCommand.cs b/src/Application/TakeoutSaaS.Application/Identity/Commands/UpdateRoleTemplateCommand.cs index d88b3ba..ee6d5bf 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Commands/UpdateRoleTemplateCommand.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Commands/UpdateRoleTemplateCommand.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Contracts/RoleTemplateDto.cs b/src/Application/TakeoutSaaS.Application/Identity/Contracts/RoleTemplateDto.cs index 4690fa2..d3d9415 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Contracts/RoleTemplateDto.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Contracts/RoleTemplateDto.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace TakeoutSaaS.Application.Identity.Contracts; /// diff --git a/src/Application/TakeoutSaaS.Application/Identity/Contracts/UserPermissionDto.cs b/src/Application/TakeoutSaaS.Application/Identity/Contracts/UserPermissionDto.cs index f3393ad..3506491 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Contracts/UserPermissionDto.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Contracts/UserPermissionDto.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json.Serialization; using TakeoutSaaS.Shared.Abstractions.Serialization; 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..807371a 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CopyRoleTemplateCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CopyRoleTemplateCommandHandler.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Commands; using TakeoutSaaS.Application.Identity.Contracts; @@ -26,6 +23,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 +34,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 +68,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 +93,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..2bec7ea 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleTemplateCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/CreateRoleTemplateCommandHandler.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Commands; using TakeoutSaaS.Application.Identity.Contracts; @@ -19,17 +17,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 +39,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..be16a6e 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetRoleTemplateQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetRoleTemplateQueryHandler.cs @@ -1,4 +1,3 @@ -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Application.Identity.Queries; @@ -15,14 +14,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..915f386 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetUserPermissionsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/GetUserPermissionsQueryHandler.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Application.Identity.Queries; @@ -20,26 +18,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 +49,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/InitializeRoleTemplatesCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/InitializeRoleTemplatesCommandHandler.cs index 483b336..7ec7a0e 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/InitializeRoleTemplatesCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/InitializeRoleTemplatesCommandHandler.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Commands; using TakeoutSaaS.Application.Identity.Contracts; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs index 2b533cd..e99254b 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/ListRoleTemplatesQueryHandler.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Application.Identity.Queries; @@ -16,9 +14,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 +30,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..df6245b 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchPermissionsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchPermissionsQueryHandler.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Application.Identity.Queries; @@ -19,9 +17,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 +35,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 +51,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..7309de3 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchRolesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchRolesQueryHandler.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Application.Identity.Queries; @@ -19,9 +17,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 +32,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 +48,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..4184be8 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchUserPermissionsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/SearchUserPermissionsQueryHandler.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Application.Identity.Queries; @@ -22,25 +19,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 +74,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 +104,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/TemplateMapper.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/TemplateMapper.cs index 73e0509..aaf6d71 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/TemplateMapper.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/TemplateMapper.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Domain.Identity.Entities; 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/Identity/Handlers/UpdateRoleTemplateCommandHandler.cs b/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdateRoleTemplateCommandHandler.cs index 1f07395..a98bf6e 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdateRoleTemplateCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Handlers/UpdateRoleTemplateCommandHandler.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using MediatR; using TakeoutSaaS.Application.Identity.Commands; using TakeoutSaaS.Application.Identity.Contracts; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Queries/ListRoleTemplatesQuery.cs b/src/Application/TakeoutSaaS.Application/Identity/Queries/ListRoleTemplatesQuery.cs index 7da3def..1b76a6d 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Queries/ListRoleTemplatesQuery.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Queries/ListRoleTemplatesQuery.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MediatR; using TakeoutSaaS.Application.Identity.Contracts; diff --git a/src/Application/TakeoutSaaS.Application/Identity/Services/AdminAuthService.cs b/src/Application/TakeoutSaaS.Application/Identity/Services/AdminAuthService.cs index 4836eb8..72e8f2d 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Services/AdminAuthService.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Services/AdminAuthService.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.AspNetCore.Identity; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Contracts; @@ -80,7 +77,7 @@ public sealed class AdminAuthService( // 3. 撤销旧刷新令牌(防止重复使用) await refreshTokenStore.RevokeAsync(descriptor.Token, cancellationToken); - + // 4. 生成新的令牌对 var profile = await BuildProfileAsync(user, cancellationToken); return await jwtTokenService.CreateTokensAsync(profile, false, cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/Identity/Services/MiniAuthService.cs b/src/Application/TakeoutSaaS.Application/Identity/Services/MiniAuthService.cs index 5d83289..8234d7e 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Services/MiniAuthService.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Services/MiniAuthService.cs @@ -1,5 +1,5 @@ -using System.Net; using Microsoft.AspNetCore.Http; +using System.Net; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Domain.Identity.Entities; @@ -54,7 +54,7 @@ public sealed class MiniAuthService( // 5. 登录成功后重置限流计数 await rateLimiter.ResetAsync(throttleKey, cancellationToken); - + // 6. 构建用户档案并生成令牌 var profile = BuildProfile(user); return await jwtTokenService.CreateTokensAsync(profile, isNew, cancellationToken); @@ -82,7 +82,7 @@ public sealed class MiniAuthService( // 3. 撤销旧刷新令牌(防止重复使用) await refreshTokenStore.RevokeAsync(descriptor.Token, cancellationToken); - + // 4. 生成新的令牌对 var profile = BuildProfile(user); return await jwtTokenService.CreateTokensAsync(profile, false, cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/Messaging/Abstractions/IEventPublisher.cs b/src/Application/TakeoutSaaS.Application/Messaging/Abstractions/IEventPublisher.cs index b471f0a..c17a6f7 100644 --- a/src/Application/TakeoutSaaS.Application/Messaging/Abstractions/IEventPublisher.cs +++ b/src/Application/TakeoutSaaS.Application/Messaging/Abstractions/IEventPublisher.cs @@ -1,6 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; - namespace TakeoutSaaS.Application.Messaging.Abstractions; /// diff --git a/src/Application/TakeoutSaaS.Application/Messaging/Services/EventPublisher.cs b/src/Application/TakeoutSaaS.Application/Messaging/Services/EventPublisher.cs index 60b04d3..d87dc80 100644 --- a/src/Application/TakeoutSaaS.Application/Messaging/Services/EventPublisher.cs +++ b/src/Application/TakeoutSaaS.Application/Messaging/Services/EventPublisher.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Messaging.Abstractions; using TakeoutSaaS.Module.Messaging.Abstractions; diff --git a/src/Application/TakeoutSaaS.Application/Sms/Abstractions/IVerificationCodeService.cs b/src/Application/TakeoutSaaS.Application/Sms/Abstractions/IVerificationCodeService.cs index 514b843..4eab7cc 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Abstractions/IVerificationCodeService.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Abstractions/IVerificationCodeService.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Sms.Contracts; namespace TakeoutSaaS.Application.Sms.Abstractions; 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/SendVerificationCodeResponse.cs b/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeResponse.cs index 5b6cf77..e9f84f1 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeResponse.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Contracts/SendVerificationCodeResponse.cs @@ -1,5 +1,3 @@ -using System; - namespace TakeoutSaaS.Application.Sms.Contracts; /// 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..1fbac0f 100644 --- a/src/Application/TakeoutSaaS.Application/Sms/Services/VerificationCodeService.cs +++ b/src/Application/TakeoutSaaS.Application/Sms/Services/VerificationCodeService.cs @@ -1,15 +1,14 @@ -using System.Security.Cryptography; -using System.Text; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using System.Security.Cryptography; +using System.Text; using TakeoutSaaS.Application.Sms.Abstractions; using TakeoutSaaS.Application.Sms.Contracts; using TakeoutSaaS.Application.Sms.Options; using TakeoutSaaS.Module.Sms.Abstractions; using TakeoutSaaS.Module.Sms.Models; using TakeoutSaaS.Module.Sms.Options; -using TakeoutSaaS.Module.Sms; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; using TakeoutSaaS.Shared.Abstractions.Tenancy; @@ -30,6 +29,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 +40,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 +49,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 +64,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 +87,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 +105,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/Abstractions/IFileStorageService.cs b/src/Application/TakeoutSaaS.Application/Storage/Abstractions/IFileStorageService.cs index f164c5c..b92f66d 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Abstractions/IFileStorageService.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Abstractions/IFileStorageService.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Application.Storage.Contracts; namespace TakeoutSaaS.Application.Storage.Abstractions; 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/DirectUploadResponse.cs b/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadResponse.cs index 4989657..de1ab0b 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadResponse.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Contracts/DirectUploadResponse.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace TakeoutSaaS.Application.Storage.Contracts; /// diff --git a/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs b/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs index ea19f91..43e12f2 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Contracts/UploadFileRequest.cs @@ -1,4 +1,3 @@ -using System.IO; using TakeoutSaaS.Application.Storage.Enums; namespace TakeoutSaaS.Application.Storage.Contracts; @@ -17,8 +16,6 @@ public sealed class UploadFileRequest( long contentLength, string? requestOrigin) { - - /// /// 文件分类。 /// diff --git a/src/Application/TakeoutSaaS.Application/Storage/Extensions/UploadFileTypeParser.cs b/src/Application/TakeoutSaaS.Application/Storage/Extensions/UploadFileTypeParser.cs index dd712ba..89efe3d 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Extensions/UploadFileTypeParser.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Extensions/UploadFileTypeParser.cs @@ -1,4 +1,3 @@ -using System; using TakeoutSaaS.Application.Storage.Enums; namespace TakeoutSaaS.Application.Storage.Extensions; diff --git a/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs b/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs index f2105c5..a887bf9 100644 --- a/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs +++ b/src/Application/TakeoutSaaS.Application/Storage/Services/FileStorageService.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using System.Security.Cryptography; +using System.Text; using TakeoutSaaS.Application.Storage.Abstractions; using TakeoutSaaS.Application.Storage.Contracts; using TakeoutSaaS.Application.Storage.Enums; @@ -35,11 +29,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 +46,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 +72,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 +88,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/Data/IDapperExecutor.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Data/IDapperExecutor.cs index 3783a93..0423468 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Data/IDapperExecutor.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Data/IDapperExecutor.cs @@ -29,6 +29,7 @@ public interface IDapperExecutor /// 连接角色(读/写)。 /// 命令委托,提供已打开的连接和取消标记。 /// 取消标记。 + /// 异步执行任务。 Task ExecuteAsync( string dataSourceName, DatabaseConnectionRole role, diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Diagnostics/TraceContext.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Diagnostics/TraceContext.cs index ad8aa43..ae8284b 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Diagnostics/TraceContext.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Diagnostics/TraceContext.cs @@ -1,5 +1,3 @@ -using System.Threading; - namespace TakeoutSaaS.Shared.Abstractions.Diagnostics; /// diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs index a87def6..f95bf0b 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Exceptions/ValidationException.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace TakeoutSaaS.Shared.Abstractions.Exceptions; /// diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.NonGeneric.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.NonGeneric.cs index d3e8d6d..04f400c 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.NonGeneric.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.NonGeneric.cs @@ -8,24 +8,36 @@ public static class ApiResponse /// /// 仅返回成功消息(无数据)。 /// + /// 提示信息。 + /// 封装后的成功响应。 public static ApiResponse Success(string? message = "操作成功") => ApiResponse.Ok(message: message); /// /// 成功且携带数据。 /// + /// 业务数据。 + /// 提示信息。 + /// 封装后的成功响应。 public static ApiResponse Ok(object? data, string? message = "操作成功") => data is null ? ApiResponse.Ok(message: message) : ApiResponse.Ok(data, message); /// /// 错误返回。 /// + /// 错误码。 + /// 错误提示。 + /// 封装后的失败响应。 public static ApiResponse Failure(int code, string message) => ApiResponse.Error(code, message); /// /// 错误返回(附带详情)。 /// + /// 错误码。 + /// 错误提示。 + /// 错误详情。 + /// 封装后的失败响应。 public static ApiResponse Error(int code, string message, object? errors = null) => ApiResponse.Error(code, message, errors); } diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/ApiResponse.cs index b49a215..f89a1b7 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 { /// @@ -47,36 +47,53 @@ public sealed record ApiResponse /// /// 成功返回。 /// + /// 业务数据。 + /// 提示信息。 + /// 封装后的成功响应。 public static ApiResponse Ok(T data, string? message = "操作成功") => Create(true, 200, message, data); /// /// 无数据的成功返回。 /// + /// 提示信息。 + /// 封装后的成功响应。 public static ApiResponse Ok(string? message = "操作成功") => Create(true, 200, message, default); /// /// 兼容旧名称:成功结果。 /// + /// 业务数据。 + /// 提示信息。 + /// 封装后的成功响应。 public static ApiResponse SuccessResult(T data, string? message = "操作成功") => Ok(data, message); /// /// 错误返回。 /// + /// 错误码。 + /// 错误提示。 + /// 错误详情。 + /// 封装后的失败响应。 public static ApiResponse Error(int code, string message, object? errors = null) => Create(false, code, message, default, errors); /// /// 兼容旧名称:失败结果。 /// + /// 错误码。 + /// 错误提示。 + /// 封装后的失败响应。 public static ApiResponse Failure(int code, string message) => Error(code, message); /// /// 附加错误详情。 /// + /// 错误详情。 + /// 包含错误详情的新响应。 public ApiResponse WithErrors(object? errors) => this with { Errors = errors }; @@ -92,6 +109,10 @@ public sealed record ApiResponse Timestamp = DateTime.UtcNow }; + /// + /// 解析当前 TraceId。 + /// + /// 当前有效的 TraceId。 private static string ResolveTraceId() { if (!string.IsNullOrWhiteSpace(TraceContext.TraceId)) @@ -113,9 +134,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 +157,10 @@ internal sealed class IdFallbackGenerator { } + /// + /// 生成雪花风格的本地备用 ID。 + /// + /// 本地生成的雪花 ID。 public long NextId() { lock (_sync) @@ -149,6 +184,11 @@ internal sealed class IdFallbackGenerator } } + /// + /// 等待到下一个毫秒以避免序列冲突。 + /// + /// 上一毫秒的时间戳。 + /// 下一个时间戳(毫秒)。 private static long WaitNextMillis(long lastTimestamp) { var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs index 69c5ba4..4bc4c7e 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Results/PagedResult.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace TakeoutSaaS.Shared.Abstractions.Results; /// diff --git a/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/ITenantProvider.cs b/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/ITenantProvider.cs index 02c818a..76358db 100644 --- a/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/ITenantProvider.cs +++ b/src/Core/TakeoutSaaS.Shared.Abstractions/Tenancy/ITenantProvider.cs @@ -6,7 +6,8 @@ namespace TakeoutSaaS.Shared.Abstractions.Tenancy; public interface ITenantProvider { /// - /// 获取当前租户 ID,未解析时返回 Guid.Empty。 + /// 获取当前租户 ID,未解析时返回 0。 /// + /// 当前请求绑定的租户 ID,未解析时为 0。 long GetCurrentTenantId(); } diff --git a/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs b/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs index 06d22fb..862edb9 100644 --- a/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs +++ b/src/Core/TakeoutSaaS.Shared.Kernel/Ids/SnowflakeIdGenerator.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.Security.Cryptography; -using System.Threading; using TakeoutSaaS.Shared.Abstractions.Ids; namespace TakeoutSaaS.Shared.Kernel.Ids; diff --git a/src/Core/TakeoutSaaS.Shared.Web/Extensions/ServiceCollectionExtensions.cs b/src/Core/TakeoutSaaS.Shared.Web/Extensions/ServiceCollectionExtensions.cs index dd5f1e3..7ac65ce 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using TakeoutSaaS.Shared.Abstractions.Security; using TakeoutSaaS.Shared.Web.Filters; diff --git a/src/Core/TakeoutSaaS.Shared.Web/Filters/ValidateModelAttribute.cs b/src/Core/TakeoutSaaS.Shared.Web/Filters/ValidateModelAttribute.cs index 4c802e0..c6ad12f 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Filters/ValidateModelAttribute.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Filters/ValidateModelAttribute.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using TakeoutSaaS.Shared.Abstractions.Constants; diff --git a/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs b/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs index e1dadd9..eb7d0a9 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Middleware/CorrelationIdMiddleware.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using System.Diagnostics; using TakeoutSaaS.Shared.Abstractions.Diagnostics; using TakeoutSaaS.Shared.Abstractions.Ids; @@ -49,10 +46,10 @@ public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger - { - ["TraceId"] = traceId, - ["SpanId"] = spanId - })) + { + ["TraceId"] = traceId, + ["SpanId"] = spanId + })) { try { diff --git a/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs b/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs index 2babea2..c52d364 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Middleware/ExceptionHandlingMiddleware.cs @@ -1,10 +1,8 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using System.Text.Json; +using System.Text.Json.Serialization; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; using TakeoutSaaS.Shared.Abstractions.Results; diff --git a/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs b/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs index 7c5be5f..7162688 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Middleware/RequestLoggingMiddleware.cs @@ -1,7 +1,6 @@ -using System.Diagnostics; -using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using System.Diagnostics; using TakeoutSaaS.Shared.Abstractions.Diagnostics; namespace TakeoutSaaS.Shared.Web.Middleware; @@ -11,7 +10,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/ClaimsPrincipalExtensions.cs b/src/Core/TakeoutSaaS.Shared.Web/Security/ClaimsPrincipalExtensions.cs index 05a90b2..630789b 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Security/ClaimsPrincipalExtensions.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Security/ClaimsPrincipalExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Security.Claims; namespace TakeoutSaaS.Shared.Web.Security; diff --git a/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs b/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs index e7f5209..bc43ea8 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Security/HttpContextCurrentUserAccessor.cs @@ -1,5 +1,5 @@ -using System.Security.Claims; using Microsoft.AspNetCore.Http; +using System.Security.Claims; using TakeoutSaaS.Shared.Abstractions.Security; namespace TakeoutSaaS.Shared.Web.Security; @@ -12,8 +12,6 @@ namespace TakeoutSaaS.Shared.Web.Security; /// public sealed class HttpContextCurrentUserAccessor(IHttpContextAccessor httpContextAccessor) : ICurrentUserAccessor { - - /// public long UserId { diff --git a/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs b/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs index e167f00..68590fc 100644 --- a/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs +++ b/src/Core/TakeoutSaaS.Shared.Web/Swagger/ConfigureSwaggerOptions.cs @@ -1,6 +1,4 @@ -using System; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.OpenApi; 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/Deliveries/Repositories/IDeliveryRepository.cs b/src/Domain/TakeoutSaaS.Domain/Deliveries/Repositories/IDeliveryRepository.cs index 27d5c95..f97f67e 100644 --- a/src/Domain/TakeoutSaaS.Domain/Deliveries/Repositories/IDeliveryRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Deliveries/Repositories/IDeliveryRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Deliveries.Entities; using TakeoutSaaS.Domain.Deliveries.Enums; @@ -14,45 +11,77 @@ public interface IDeliveryRepository /// /// 依据标识获取配送单。 /// + /// 配送单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 配送单实体或 null。 Task FindByIdAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default); /// /// 依据订单标识获取配送单。 /// + /// 订单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 配送单实体或 null。 Task FindByOrderIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default); /// /// 获取配送事件轨迹。 /// + /// 配送单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 配送事件列表。 Task> GetEventsAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default); /// /// 新增配送单。 /// + /// 配送单实体。 + /// 取消标记。 + /// 异步任务。 Task AddDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default); /// /// 新增配送事件。 /// + /// 配送事件。 + /// 取消标记。 + /// 异步任务。 Task AddEventAsync(DeliveryEvent deliveryEvent, CancellationToken cancellationToken = default); /// /// 持久化变更。 /// + /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); /// /// 按状态查询配送单。 /// + /// 租户 ID。 + /// 配送状态。 + /// 订单 ID。 + /// 取消标记。 + /// 配送单列表。 Task> SearchAsync(long tenantId, DeliveryStatus? status, long? orderId, CancellationToken cancellationToken = default); /// /// 更新配送单。 /// + /// 配送单实体。 + /// 取消标记。 + /// 异步任务。 Task UpdateDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default); /// /// 删除配送单及事件。 /// + /// 配送单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task DeleteDeliveryOrderAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Dictionary/Entities/DictionaryGroup.cs b/src/Domain/TakeoutSaaS.Domain/Dictionary/Entities/DictionaryGroup.cs index 68694b0..bf48fca 100644 --- a/src/Domain/TakeoutSaaS.Domain/Dictionary/Entities/DictionaryGroup.cs +++ b/src/Domain/TakeoutSaaS.Domain/Dictionary/Entities/DictionaryGroup.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using TakeoutSaaS.Domain.Dictionary.Enums; using TakeoutSaaS.Shared.Abstractions.Entities; diff --git a/src/Domain/TakeoutSaaS.Domain/Dictionary/Repositories/IDictionaryRepository.cs b/src/Domain/TakeoutSaaS.Domain/Dictionary/Repositories/IDictionaryRepository.cs index 9a9b427..d0d4294 100644 --- a/src/Domain/TakeoutSaaS.Domain/Dictionary/Repositories/IDictionaryRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Dictionary/Repositories/IDictionaryRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Dictionary.Entities; using TakeoutSaaS.Domain.Dictionary.Enums; @@ -14,55 +11,89 @@ public interface IDictionaryRepository /// /// 依据 ID 获取分组。 /// + /// 分组 ID。 + /// 取消标记。 + /// 分组实体或 null。 Task FindGroupByIdAsync(long id, CancellationToken cancellationToken = default); /// /// 依据编码获取分组。 /// + /// 分组编码。 + /// 取消标记。 + /// 分组实体或 null。 Task FindGroupByCodeAsync(string code, CancellationToken cancellationToken = default); /// /// 搜索分组,可按作用域过滤。 /// + /// 作用域。 + /// 取消标记。 + /// 分组集合。 Task> SearchGroupsAsync(DictionaryScope? scope, CancellationToken cancellationToken = default); /// /// 新增分组。 /// + /// 分组实体。 + /// 取消标记。 + /// 异步任务。 Task AddGroupAsync(DictionaryGroup group, CancellationToken cancellationToken = default); /// /// 删除分组。 /// + /// 分组实体。 + /// 取消标记。 + /// 异步任务。 Task RemoveGroupAsync(DictionaryGroup group, CancellationToken cancellationToken = default); /// /// 依据 ID 获取字典项。 /// + /// 字典项 ID。 + /// 取消标记。 + /// 字典项或 null。 Task FindItemByIdAsync(long id, CancellationToken cancellationToken = default); /// /// 获取某分组下的所有字典项。 /// + /// 分组 ID。 + /// 取消标记。 + /// 字典项集合。 Task> GetItemsByGroupIdAsync(long groupId, CancellationToken cancellationToken = default); /// /// 按分组编码集合获取字典项(可包含系统参数)。 /// + /// 分组编码集合。 + /// 租户 ID。 + /// 是否包含系统分组。 + /// 取消标记。 + /// 字典项集合。 Task> GetItemsByCodesAsync(IEnumerable codes, long tenantId, bool includeSystem, CancellationToken cancellationToken = default); /// /// 新增字典项。 /// + /// 字典项实体。 + /// 取消标记。 + /// 异步任务。 Task AddItemAsync(DictionaryItem item, CancellationToken cancellationToken = default); /// /// 删除字典项。 /// + /// 字典项实体。 + /// 取消标记。 + /// 异步任务。 Task RemoveItemAsync(DictionaryItem item, CancellationToken cancellationToken = default); /// /// 持久化更改。 /// + /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IIdentityUserRepository.cs b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IIdentityUserRepository.cs index 5b809cf..cc3a497 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IIdentityUserRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IIdentityUserRepository.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Identity.Entities; namespace TakeoutSaaS.Domain.Identity.Repositories; @@ -14,11 +10,17 @@ public interface IIdentityUserRepository /// /// 根据账号获取后台用户。 /// + /// 账号。 + /// 取消标记。 + /// 后台用户或 null。 Task FindByAccountAsync(string account, CancellationToken cancellationToken = default); /// /// 根据 ID 获取后台用户。 /// + /// 用户 ID。 + /// 取消标记。 + /// 后台用户或 null。 Task FindByIdAsync(long userId, CancellationToken cancellationToken = default); /// @@ -27,10 +29,15 @@ public interface IIdentityUserRepository /// 租户 ID。 /// 可选关键字(账号/名称)。 /// 取消标记。 + /// 后台用户列表。 Task> SearchAsync(long tenantId, string? keyword, CancellationToken cancellationToken = default); /// /// 获取指定租户、用户集合对应的用户(只读)。 /// + /// 租户 ID。 + /// 用户 ID 集合。 + /// 取消标记。 + /// 后台用户列表。 Task> GetByIdsAsync(long tenantId, IEnumerable userIds, CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs index 7f78dde..d643394 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IPermissionRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Identity.Entities; namespace TakeoutSaaS.Domain.Identity.Repositories; @@ -10,13 +7,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..4502d1e 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRolePermissionRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRolePermissionRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Identity.Entities; namespace TakeoutSaaS.Domain.Identity.Repositories; @@ -10,8 +7,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..726a513 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Identity.Entities; namespace TakeoutSaaS.Domain.Identity.Repositories; @@ -10,12 +7,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..f175722 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleTemplateRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IRoleTemplateRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Identity.Entities; namespace TakeoutSaaS.Domain.Identity.Repositories; @@ -10,19 +7,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..6759d08 100644 --- a/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IUserRoleRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Identity/Repositories/IUserRoleRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Identity.Entities; namespace TakeoutSaaS.Domain.Identity.Repositories; @@ -10,8 +7,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/IMerchantCategoryRepository.cs b/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantCategoryRepository.cs index f8669d6..cabdd80 100644 --- a/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantCategoryRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantCategoryRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Merchants.Entities; namespace TakeoutSaaS.Domain.Merchants.Repositories; @@ -13,35 +10,57 @@ public interface IMerchantCategoryRepository /// /// 列出当前租户的类目。 /// + /// 租户 ID。 + /// 取消标记。 + /// 类目列表。 Task> ListAsync(long tenantId, CancellationToken cancellationToken = default); /// /// 是否存在同名类目。 /// + /// 类目名称。 + /// 租户 ID。 + /// 取消标记。 + /// 存在返回 true。 Task ExistsAsync(string name, long tenantId, CancellationToken cancellationToken = default); /// /// 查找类目。 /// + /// 类目 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 类目实体或 null。 Task FindByIdAsync(long id, long tenantId, CancellationToken cancellationToken = default); /// /// 新增类目。 /// + /// 类目实体。 + /// 取消标记。 + /// 异步任务。 Task AddAsync(MerchantCategory category, CancellationToken cancellationToken = default); /// /// 删除类目。 /// + /// 类目实体。 + /// 取消标记。 + /// 异步任务。 Task RemoveAsync(MerchantCategory category, CancellationToken cancellationToken = default); /// /// 批量更新类目信息。 /// + /// 类目集合。 + /// 取消标记。 + /// 异步任务。 Task UpdateRangeAsync(IEnumerable categories, 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 0a746c6..1983bed 100644 --- a/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Merchants/Repositories/IMerchantRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Merchants.Entities; using TakeoutSaaS.Domain.Merchants.Enums; @@ -14,16 +11,28 @@ public interface IMerchantRepository /// /// 依据标识获取商户。 /// + /// 商户 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 商户实体或 null。 Task FindByIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default); /// /// 按状态筛选商户列表。 /// + /// 租户 ID。 + /// 状态过滤。 + /// 取消标记。 + /// 商户集合。 Task> SearchAsync(long tenantId, MerchantStatus? status, CancellationToken cancellationToken = default); /// /// 获取指定商户的员工列表。 /// + /// 商户 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 员工集合。 Task> GetStaffAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default); /// @@ -39,50 +48,111 @@ public interface IMerchantRepository /// /// 获取指定商户的合同列表。 /// + /// 商户 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 合同集合。 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); /// /// 获取指定商户的资质文件列表。 /// + /// 商户 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 资质文件列表。 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); /// /// 新增商户主体。 /// + /// 商户实体。 + /// 取消标记。 + /// 异步任务。 Task AddMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default); /// /// 新增商户员工。 /// + /// 员工实体。 + /// 取消标记。 + /// 异步任务。 Task AddStaffAsync(MerchantStaff staff, CancellationToken cancellationToken = default); /// /// 新增商户合同。 /// + /// 合同实体。 + /// 取消标记。 + /// 异步任务。 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); /// /// 持久化变更。 /// + /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); /// /// 更新商户信息。 /// + /// 商户实体。 + /// 取消标记。 + /// 异步任务。 Task UpdateMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default); /// /// 删除商户。 /// + /// 商户 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task DeleteMerchantAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default); /// @@ -93,10 +163,17 @@ public interface IMerchantRepository /// /// 记录审核日志。 /// + /// 审核日志实体。 + /// 取消标记。 + /// 异步任务。 Task AddAuditLogAsync(MerchantAuditLog log, CancellationToken cancellationToken = default); /// /// 获取审核日志。 /// + /// 商户 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 审核日志列表。 Task> GetAuditLogsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Orders/Repositories/IOrderRepository.cs b/src/Domain/TakeoutSaaS.Domain/Orders/Repositories/IOrderRepository.cs index d48ecf3..96a3bd6 100644 --- a/src/Domain/TakeoutSaaS.Domain/Orders/Repositories/IOrderRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Orders/Repositories/IOrderRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Orders.Entities; using TakeoutSaaS.Domain.Orders.Enums; using TakeoutSaaS.Domain.Payments.Enums; @@ -15,65 +12,111 @@ public interface IOrderRepository /// /// 依据标识获取订单。 /// + /// 订单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 订单实体或 null。 Task FindByIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default); /// /// 依据订单号获取订单。 /// + /// 订单号。 + /// 租户 ID。 + /// 取消标记。 + /// 订单实体或 null。 Task FindByOrderNoAsync(string orderNo, long tenantId, CancellationToken cancellationToken = default); /// /// 按状态筛选订单列表。 /// + /// 租户 ID。 + /// 订单状态。 + /// 支付状态。 + /// 取消标记。 + /// 订单集合。 Task> SearchAsync(long tenantId, OrderStatus? status, PaymentStatus? paymentStatus, CancellationToken cancellationToken = default); /// /// 获取订单明细行。 /// + /// 订单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 订单明细集合。 Task> GetItemsAsync(long orderId, long tenantId, CancellationToken cancellationToken = default); /// /// 获取订单状态流转记录。 /// + /// 订单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 状态变更记录列表。 Task> GetStatusHistoryAsync(long orderId, long tenantId, CancellationToken cancellationToken = default); /// /// 获取订单退款申请。 /// + /// 订单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 退款申请列表。 Task> GetRefundsAsync(long orderId, long tenantId, CancellationToken cancellationToken = default); /// /// 新增订单。 /// + /// 订单实体。 + /// 取消标记。 + /// 异步任务。 Task AddOrderAsync(Order order, CancellationToken cancellationToken = default); /// /// 新增订单明细。 /// + /// 明细集合。 + /// 取消标记。 + /// 异步任务。 Task AddItemsAsync(IEnumerable items, CancellationToken cancellationToken = default); /// /// 新增订单状态记录。 /// + /// 状态记录。 + /// 取消标记。 + /// 异步任务。 Task AddStatusHistoryAsync(OrderStatusHistory history, CancellationToken cancellationToken = default); /// /// 新增退款申请。 /// + /// 退款申请实体。 + /// 取消标记。 + /// 异步任务。 Task AddRefundAsync(RefundRequest refund, CancellationToken cancellationToken = default); /// /// 持久化变更。 /// + /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); /// /// 更新订单。 /// + /// 订单实体。 + /// 取消标记。 + /// 异步任务。 Task UpdateOrderAsync(Order order, CancellationToken cancellationToken = default); /// /// 删除订单。 /// + /// 订单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task DeleteOrderAsync(long orderId, long tenantId, CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Payments/Repositories/IPaymentRepository.cs b/src/Domain/TakeoutSaaS.Domain/Payments/Repositories/IPaymentRepository.cs index f12c937..7983a5a 100644 --- a/src/Domain/TakeoutSaaS.Domain/Payments/Repositories/IPaymentRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Payments/Repositories/IPaymentRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Payments.Entities; using TakeoutSaaS.Domain.Payments.Enums; @@ -14,45 +11,76 @@ public interface IPaymentRepository /// /// 依据标识获取支付记录。 /// + /// 支付记录 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 支付记录或 null。 Task FindByIdAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default); /// /// 依据订单标识获取支付记录。 /// + /// 订单 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 支付记录或 null。 Task FindByOrderIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default); /// /// 获取支付对应的退款记录。 /// + /// 支付记录 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 退款记录列表。 Task> GetRefundsAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default); /// /// 新增支付记录。 /// + /// 支付实体。 + /// 取消标记。 + /// 异步任务。 Task AddPaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default); /// /// 新增退款记录。 /// + /// 退款实体。 + /// 取消标记。 + /// 异步任务。 Task AddRefundAsync(PaymentRefundRecord refund, CancellationToken cancellationToken = default); /// /// 持久化变更。 /// + /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); /// /// 按状态筛选支付记录。 /// + /// 租户 ID。 + /// 支付状态。 + /// 取消标记。 + /// 支付记录列表。 Task> SearchAsync(long tenantId, PaymentStatus? status, CancellationToken cancellationToken = default); /// /// 更新支付记录。 /// + /// 支付实体。 + /// 取消标记。 + /// 异步任务。 Task UpdatePaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default); /// /// 删除支付记录及关联退款。 /// + /// 支付记录 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task DeletePaymentAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Products/Repositories/IProductRepository.cs b/src/Domain/TakeoutSaaS.Domain/Products/Repositories/IProductRepository.cs index 451c535..5b4563a 100644 --- a/src/Domain/TakeoutSaaS.Domain/Products/Repositories/IProductRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Products/Repositories/IProductRepository.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Enums; @@ -45,6 +41,10 @@ public interface IProductRepository /// /// 获取商品加料组与选项。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 加料组集合。 Task> GetAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// @@ -55,6 +55,10 @@ public interface IProductRepository /// /// 获取商品加料选项。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 加料选项集合。 Task> GetAddonOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// @@ -65,6 +69,10 @@ public interface IProductRepository /// /// 获取商品规格组与选项。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 规格组集合。 Task> GetAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// @@ -75,6 +83,10 @@ public interface IProductRepository /// /// 获取商品规格选项。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 规格选项集合。 Task> GetAttributeOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// @@ -85,6 +97,10 @@ public interface IProductRepository /// /// 获取商品媒资。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 媒资列表。 Task> GetMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// @@ -95,6 +111,10 @@ public interface IProductRepository /// /// 获取商品定价规则。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 定价规则集合。 Task> GetPricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// @@ -105,85 +125,144 @@ public interface IProductRepository /// /// 新增分类。 /// + /// 分类实体。 + /// 取消标记。 + /// 异步任务。 Task AddCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default); /// /// 新增商品。 /// + /// 商品实体。 + /// 取消标记。 + /// 异步任务。 Task AddProductAsync(Product product, CancellationToken cancellationToken = default); /// /// 新增 SKU。 /// + /// SKU 集合。 + /// 取消标记。 + /// 异步任务。 Task AddSkusAsync(IEnumerable skus, CancellationToken cancellationToken = default); /// /// 新增加料组与选项。 /// + /// 加料组集合。 + /// 加料选项集合。 + /// 取消标记。 + /// 异步任务。 Task AddAddonGroupsAsync(IEnumerable groups, IEnumerable options, CancellationToken cancellationToken = default); /// /// 新增规格组与选项。 /// + /// 规格组集合。 + /// 规格选项集合。 + /// 取消标记。 + /// 异步任务。 Task AddAttributeGroupsAsync(IEnumerable groups, IEnumerable options, CancellationToken cancellationToken = default); /// /// 新增媒资。 /// + /// 媒资集合。 + /// 取消标记。 + /// 异步任务。 Task AddMediaAssetsAsync(IEnumerable assets, CancellationToken cancellationToken = default); /// /// 新增定价规则。 /// + /// 定价规则集合。 + /// 取消标记。 + /// 异步任务。 Task AddPricingRulesAsync(IEnumerable rules, CancellationToken cancellationToken = default); /// /// 持久化变更。 /// + /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); /// /// 更新商品。 /// + /// 商品实体。 + /// 取消标记。 + /// 异步任务。 Task UpdateProductAsync(Product product, CancellationToken cancellationToken = default); /// /// 删除商品。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task DeleteProductAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// /// 更新分类。 /// + /// 分类实体。 + /// 取消标记。 + /// 异步任务。 Task UpdateCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default); /// /// 删除分类。 /// + /// 分类 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task DeleteCategoryAsync(long categoryId, long tenantId, CancellationToken cancellationToken = default); /// /// 删除商品下的 SKU。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task RemoveSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// /// 删除商品下的加料组及选项。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task RemoveAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// /// 删除商品下的规格组及选项。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task RemoveAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// /// 删除商品媒资。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task RemoveMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default); /// /// 删除商品定价规则。 /// + /// 商品 ID。 + /// 租户 ID。 + /// 取消标记。 + /// 异步任务。 Task RemovePricingRulesAsync(long productId, long tenantId, 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/Domain/TakeoutSaaS.Domain/Stores/Repositories/IStoreRepository.cs b/src/Domain/TakeoutSaaS.Domain/Stores/Repositories/IStoreRepository.cs index b7a4766..fd941bc 100644 --- a/src/Domain/TakeoutSaaS.Domain/Stores/Repositories/IStoreRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Stores/Repositories/IStoreRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Stores.Entities; using TakeoutSaaS.Domain.Stores.Enums; diff --git a/src/Domain/TakeoutSaaS.Domain/SystemParameters/Repositories/ISystemParameterRepository.cs b/src/Domain/TakeoutSaaS.Domain/SystemParameters/Repositories/ISystemParameterRepository.cs index c222342..ced9e53 100644 --- a/src/Domain/TakeoutSaaS.Domain/SystemParameters/Repositories/ISystemParameterRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/SystemParameters/Repositories/ISystemParameterRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.SystemParameters.Entities; namespace TakeoutSaaS.Domain.SystemParameters.Repositories; @@ -13,35 +10,56 @@ public interface ISystemParameterRepository /// /// 根据标识获取系统参数。 /// + /// 参数 ID。 + /// 取消标记。 + /// 系统参数或 null。 Task FindByIdAsync(long id, CancellationToken cancellationToken = default); /// /// 根据键获取系统参数(当前租户)。 /// + /// 参数键。 + /// 取消标记。 + /// 系统参数或 null。 Task FindByKeyAsync(string key, CancellationToken cancellationToken = default); /// /// 查询系统参数列表。 /// + /// 关键字。 + /// 启用状态。 + /// 取消标记。 + /// 参数列表。 Task> SearchAsync(string? keyword, bool? isEnabled, CancellationToken cancellationToken = default); /// /// 新增系统参数。 /// + /// 参数实体。 + /// 取消标记。 + /// 异步任务。 Task AddAsync(SystemParameter parameter, CancellationToken cancellationToken = default); /// /// 删除系统参数。 /// + /// 参数实体。 + /// 取消标记。 + /// 异步任务。 Task RemoveAsync(SystemParameter parameter, CancellationToken cancellationToken = default); /// /// 更新系统参数。 /// + /// 参数实体。 + /// 取消标记。 + /// 异步任务。 Task UpdateAsync(SystemParameter parameter, CancellationToken cancellationToken = default); /// /// 持久化更改。 /// + /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementReadRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementReadRepository.cs index ac76bc8..53af993 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementReadRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementReadRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Tenants.Entities; namespace TakeoutSaaS.Domain.Tenants.Repositories; @@ -44,11 +41,13 @@ public interface ITenantAnnouncementReadRepository /// /// 已读实体。 /// 取消标记。 + /// 异步任务。 Task AddAsync(TenantAnnouncementRead record, CancellationToken cancellationToken = default); /// /// 保存变更。 /// /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementRepository.cs index c3e34b6..2314d10 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; @@ -41,6 +38,7 @@ public interface ITenantAnnouncementRepository /// /// 公告实体。 /// 取消标记。 + /// 异步任务。 Task AddAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default); /// @@ -48,6 +46,7 @@ public interface ITenantAnnouncementRepository /// /// 公告实体。 /// 取消标记。 + /// 异步任务。 Task UpdateAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default); /// @@ -56,11 +55,13 @@ public interface ITenantAnnouncementRepository /// 租户 ID。 /// 公告 ID。 /// 取消标记。 + /// 异步任务。 Task DeleteAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default); /// /// 保存变更。 /// /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantBillingRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantBillingRepository.cs index d4ddfa8..9edf1a4 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantBillingRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantBillingRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; @@ -50,6 +47,7 @@ public interface ITenantBillingRepository /// /// 账单实体。 /// 取消标记。 + /// 异步任务。 Task AddAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default); /// @@ -57,11 +55,13 @@ public interface ITenantBillingRepository /// /// 账单实体。 /// 取消标记。 + /// 异步任务。 Task UpdateAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default); /// /// 保存变更。 /// /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantNotificationRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantNotificationRepository.cs index b66093d..c81f613 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantNotificationRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantNotificationRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; @@ -43,6 +40,7 @@ public interface ITenantNotificationRepository /// /// 通知实体。 /// 取消标记。 + /// 异步任务。 Task AddAsync(TenantNotification notification, CancellationToken cancellationToken = default); /// @@ -50,11 +48,13 @@ public interface ITenantNotificationRepository /// /// 通知实体。 /// 取消标记。 + /// 异步任务。 Task UpdateAsync(TenantNotification notification, CancellationToken cancellationToken = default); /// /// 保存变更。 /// /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPackageRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPackageRepository.cs index 05e9f9f..4fe61d5 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPackageRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPackageRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Tenants.Entities; namespace TakeoutSaaS.Domain.Tenants.Repositories; @@ -32,6 +29,7 @@ public interface ITenantPackageRepository /// /// 套餐实体。 /// 取消标记。 + /// 异步任务。 Task AddAsync(TenantPackage package, CancellationToken cancellationToken = default); /// @@ -39,6 +37,7 @@ public interface ITenantPackageRepository /// /// 套餐实体。 /// 取消标记。 + /// 异步任务。 Task UpdateAsync(TenantPackage package, CancellationToken cancellationToken = default); /// @@ -46,11 +45,13 @@ public interface ITenantPackageRepository /// /// 套餐 ID(雪花算法)。 /// 取消标记。 + /// 异步任务。 Task DeleteAsync(long id, CancellationToken cancellationToken = default); /// /// 持久化。 /// /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageRepository.cs index 07eae39..ff1b791 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageRepository.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; @@ -33,6 +30,7 @@ public interface ITenantQuotaUsageRepository /// /// 配额使用实体。 /// 取消标记。 + /// 异步任务。 Task AddAsync(TenantQuotaUsage usage, CancellationToken cancellationToken = default); /// @@ -40,11 +38,13 @@ public interface ITenantQuotaUsageRepository /// /// 配额使用实体。 /// 取消标记。 + /// 异步任务。 Task UpdateAsync(TenantQuotaUsage usage, CancellationToken cancellationToken = default); /// /// 持久化。 /// /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs index 33dedf9..c396852 100644 --- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs +++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; @@ -35,6 +33,7 @@ public interface ITenantRepository /// /// 租户实体。 /// 取消标记。 + /// 异步任务。 Task AddTenantAsync(Tenant tenant, CancellationToken cancellationToken = default); /// @@ -42,6 +41,7 @@ public interface ITenantRepository /// /// 租户实体。 /// 取消标记。 + /// 异步任务。 Task UpdateTenantAsync(Tenant tenant, CancellationToken cancellationToken = default); /// @@ -65,6 +65,7 @@ public interface ITenantRepository /// /// 实名资料实体。 /// 取消标记。 + /// 异步任务。 Task UpsertVerificationProfileAsync(TenantVerificationProfile profile, CancellationToken cancellationToken = default); /// @@ -89,6 +90,7 @@ public interface ITenantRepository /// /// 订阅实体。 /// 取消标记。 + /// 异步任务。 Task AddSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default); /// @@ -96,6 +98,7 @@ public interface ITenantRepository /// /// 订阅实体。 /// 取消标记。 + /// 异步任务。 Task UpdateSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default); /// @@ -103,6 +106,7 @@ public interface ITenantRepository /// /// 订阅历史实体。 /// 取消标记。 + /// 异步任务。 Task AddSubscriptionHistoryAsync(TenantSubscriptionHistory history, CancellationToken cancellationToken = default); /// @@ -118,6 +122,7 @@ public interface ITenantRepository /// /// 审核日志实体。 /// 取消标记。 + /// 异步任务。 Task AddAuditLogAsync(TenantAuditLog log, CancellationToken cancellationToken = default); /// @@ -132,5 +137,6 @@ public interface ITenantRepository /// 持久化。 /// /// 取消标记。 + /// 异步任务。 Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs b/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs index 2e76835..6011bd9 100644 --- a/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs +++ b/src/Gateway/TakeoutSaaS.ApiGateway/Program.cs @@ -1,21 +1,18 @@ -using System.Diagnostics; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.AspNetCore.RateLimiting; using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using Serilog; -using TakeoutSaaS.ApiGateway.Configuration; +using System.Diagnostics; using System.Threading.RateLimiting; +using TakeoutSaaS.ApiGateway.Configuration; const string CorsPolicyName = "GatewayCors"; +// 1. 创建构建器并配置 Serilog var builder = WebApplication.CreateBuilder(args); - builder.Host.UseSerilog((context, services, loggerConfiguration) => { loggerConfiguration @@ -24,9 +21,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 +33,7 @@ builder.Services.Configure(options => options.KnownProxies.Clear(); }); +// 4. 配置 CORS builder.Services.AddCors(options => { options.AddPolicy(CorsPolicyName, policy => @@ -44,6 +44,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 +67,7 @@ if (rateLimitOptions.Enabled) }); } +// 6. 配置 OpenTelemetry var otelOptions = builder.Configuration.GetSection("OpenTelemetry").Get() ?? new(); if (otelOptions.Enabled) { @@ -117,10 +119,13 @@ if (otelOptions.Enabled) }); } +// 7. 构建应用 var app = builder.Build(); +// 8. 转发头中间件 app.UseForwardedHeaders(); +// 9. 全局异常处理中间件 app.UseExceptionHandler(errorApp => { // 1. 捕获所有未处理异常并返回统一结构。 @@ -145,6 +150,7 @@ app.UseExceptionHandler(errorApp => }); }); +// 10. 请求日志 app.UseSerilogRequestLogging(options => { options.MessageTemplate = "网关请求 {RequestMethod} {RequestPath} => {StatusCode} 用时 {Elapsed:0.000} 秒"; @@ -156,6 +162,7 @@ app.UseSerilogRequestLogging(options => }; }); +// 11. CORS 与限流 app.UseCors(CorsPolicyName); if (rateLimitOptions.Enabled) @@ -163,6 +170,7 @@ if (rateLimitOptions.Enabled) app.UseRateLimiter(); } +// 12. 透传请求头并保证 Trace app.Use(async (context, next) => { // 1. 确保请求拥有可追踪的 ID。 @@ -187,6 +195,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/Options/AppSeedOptions.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Options/AppSeedOptions.cs index b3ec66d..4099e12 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Options/AppSeedOptions.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Options/AppSeedOptions.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace TakeoutSaaS.Infrastructure.App.Options; /// diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Options/DictionarySeedGroupOptions.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Options/DictionarySeedGroupOptions.cs index 2cec142..c59ebb4 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Options/DictionarySeedGroupOptions.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Options/DictionarySeedGroupOptions.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using TakeoutSaaS.Domain.Dictionary.Enums; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs index 4072f0c..bd3fca8 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/AppDataSeeder.cs @@ -1,11 +1,9 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using TakeoutSaaS.Domain.Dictionary.Entities; -using TakeoutSaaS.Domain.Dictionary.Enums; using TakeoutSaaS.Domain.SystemParameters.Entities; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs index 34917c7..3bc734d 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfDeliveryRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Deliveries.Entities; using TakeoutSaaS.Domain.Deliveries.Enums; @@ -15,8 +14,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/EfMerchantCategoryRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantCategoryRepository.cs index 8b83a3e..f832908 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantCategoryRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantCategoryRepository.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Merchants.Entities; using TakeoutSaaS.Domain.Merchants.Repositories; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs index fa7f85c..34eeb92 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfMerchantRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Merchants.Entities; using TakeoutSaaS.Domain.Merchants.Enums; @@ -15,8 +14,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..ba663d4 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfOrderRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Orders.Entities; using TakeoutSaaS.Domain.Orders.Enums; @@ -16,8 +15,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..1b6ea8d 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfPaymentRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Payments.Entities; using TakeoutSaaS.Domain.Payments.Enums; @@ -15,8 +14,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 8d0ae9c..5b49b68 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfProductRepository.cs @@ -16,8 +16,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 1e1886c..23c67ce 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs @@ -16,8 +16,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/App/Repositories/EfTenantAnnouncementReadRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementReadRepository.cs index 94b26a2..23236ae 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementReadRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementReadRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Repositories; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementRepository.cs index 404152f..5ed5afd 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantBillingRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantBillingRepository.cs index 3083de5..7b939d5 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantBillingRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantBillingRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantNotificationRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantNotificationRepository.cs index 56cea39..27417cd 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantNotificationRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantNotificationRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantPackageRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantPackageRepository.cs index 0a2cd22..d23b429 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantPackageRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantPackageRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Repositories; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantQuotaUsageRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantQuotaUsageRepository.cs index 778bada..dd9c564 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantQuotaUsageRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantQuotaUsageRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs index 9c4ab49..850046d 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Extensions/DatabaseServiceCollectionExtensions.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Extensions/DatabaseServiceCollectionExtensions.cs index 4f50569..e9940a0 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Extensions/DatabaseServiceCollectionExtensions.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Extensions/DatabaseServiceCollectionExtensions.cs @@ -3,8 +3,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using TakeoutSaaS.Infrastructure.Common.Options; using TakeoutSaaS.Infrastructure.Common.Persistence; -using TakeoutSaaS.Shared.Abstractions.Ids; using TakeoutSaaS.Shared.Abstractions.Data; +using TakeoutSaaS.Shared.Abstractions.Ids; using TakeoutSaaS.Shared.Kernel.Ids; namespace TakeoutSaaS.Infrastructure.Common.Extensions; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/AppDbContext.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/AppDbContext.cs index 4733274..1f06200 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/AppDbContext.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/AppDbContext.cs @@ -1,9 +1,9 @@ -using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System.Reflection; using TakeoutSaaS.Shared.Abstractions.Entities; -using TakeoutSaaS.Shared.Abstractions.Security; using TakeoutSaaS.Shared.Abstractions.Ids; +using TakeoutSaaS.Shared.Abstractions.Security; namespace TakeoutSaaS.Infrastructure.Common.Persistence; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DapperExecutor.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DapperExecutor.cs index e0761aa..d9defe1 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DapperExecutor.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DapperExecutor.cs @@ -1,6 +1,6 @@ -using System.Data; using Microsoft.Extensions.Logging; using Npgsql; +using System.Data; using TakeoutSaaS.Shared.Abstractions.Data; namespace TakeoutSaaS.Infrastructure.Common.Persistence; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DatabaseConnectionFactory.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DatabaseConnectionFactory.cs index 7852aaa..54fee08 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DatabaseConnectionFactory.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DatabaseConnectionFactory.cs @@ -1,9 +1,8 @@ -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Security.Cryptography; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using System.ComponentModel.DataAnnotations; +using System.Security.Cryptography; using TakeoutSaaS.Infrastructure.Common.Options; using TakeoutSaaS.Shared.Abstractions.Data; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DesignTime/DesignTimeDbContextFactoryBase.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DesignTime/DesignTimeDbContextFactoryBase.cs index 5a2cf5f..a2dcba9 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DesignTime/DesignTimeDbContextFactoryBase.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/DesignTime/DesignTimeDbContextFactoryBase.cs @@ -1,6 +1,3 @@ -using System; -using System.IO; -using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/ModelBuilderCommentExtensions.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/ModelBuilderCommentExtensions.cs index e143c8f..193409c 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/ModelBuilderCommentExtensions.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/ModelBuilderCommentExtensions.cs @@ -1,8 +1,8 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; using System.Collections.Concurrent; using System.Reflection; using System.Xml.Linq; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; namespace TakeoutSaaS.Infrastructure.Common.Persistence; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/TenantAwareDbContext.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/TenantAwareDbContext.cs index f743d24..66e0f41 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/TenantAwareDbContext.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/TenantAwareDbContext.cs @@ -1,9 +1,9 @@ -using System.Reflection; using Microsoft.EntityFrameworkCore; +using System.Reflection; using TakeoutSaaS.Shared.Abstractions.Entities; +using TakeoutSaaS.Shared.Abstractions.Ids; using TakeoutSaaS.Shared.Abstractions.Security; using TakeoutSaaS.Shared.Abstractions.Tenancy; -using TakeoutSaaS.Shared.Abstractions.Ids; namespace TakeoutSaaS.Infrastructure.Common.Persistence; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Extensions/DictionaryServiceCollectionExtensions.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Extensions/DictionaryServiceCollectionExtensions.cs index 41cc530..0055d45 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Extensions/DictionaryServiceCollectionExtensions.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Extensions/DictionaryServiceCollectionExtensions.cs @@ -1,16 +1,14 @@ -using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using TakeoutSaaS.Application.Dictionary.Abstractions; using TakeoutSaaS.Domain.Dictionary.Repositories; +using TakeoutSaaS.Domain.SystemParameters.Repositories; using TakeoutSaaS.Infrastructure.Common.Extensions; -using TakeoutSaaS.Infrastructure.Common.Options; using TakeoutSaaS.Infrastructure.Dictionary.Options; using TakeoutSaaS.Infrastructure.Dictionary.Persistence; using TakeoutSaaS.Infrastructure.Dictionary.Repositories; using TakeoutSaaS.Infrastructure.Dictionary.Services; using TakeoutSaaS.Shared.Abstractions.Constants; -using TakeoutSaaS.Domain.SystemParameters.Repositories; namespace TakeoutSaaS.Infrastructure.Dictionary.Extensions; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs index 232ad67..4ce7005 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfDictionaryRepository.cs @@ -1,9 +1,8 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; -using TakeoutSaaS.Infrastructure.Dictionary.Persistence; using TakeoutSaaS.Domain.Dictionary.Entities; using TakeoutSaaS.Domain.Dictionary.Enums; using TakeoutSaaS.Domain.Dictionary.Repositories; +using TakeoutSaaS.Infrastructure.Dictionary.Persistence; namespace TakeoutSaaS.Infrastructure.Dictionary.Repositories; @@ -12,7 +11,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/Dictionary/Repositories/EfSystemParameterRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfSystemParameterRepository.cs index 1ec7868..d7e515c 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfSystemParameterRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Repositories/EfSystemParameterRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.SystemParameters.Entities; using TakeoutSaaS.Domain.SystemParameters.Repositories; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs index 372c467..4897f82 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Dictionary/Services/DistributedDictionaryCache.cs @@ -1,6 +1,6 @@ -using System.Text.Json; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Options; +using System.Text.Json; using TakeoutSaaS.Application.Dictionary.Abstractions; using TakeoutSaaS.Application.Dictionary.Models; using TakeoutSaaS.Infrastructure.Dictionary.Options; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/JwtAuthenticationExtensions.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/JwtAuthenticationExtensions.cs index 79c475c..b346321 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/JwtAuthenticationExtensions.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/JwtAuthenticationExtensions.cs @@ -1,11 +1,10 @@ -using System; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; using TakeoutSaaS.Infrastructure.Identity.Options; namespace TakeoutSaaS.Infrastructure.Identity.Extensions; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/ServiceCollectionExtensions.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/ServiceCollectionExtensions.cs index d98587f..ef2fd47 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Extensions/ServiceCollectionExtensions.cs @@ -1,12 +1,9 @@ -using System; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using TakeoutSaaS.Application.Identity.Abstractions; -using TakeoutSaaS.Domain.Identity.Entities; using TakeoutSaaS.Domain.Identity.Repositories; using TakeoutSaaS.Infrastructure.Common.Extensions; -using TakeoutSaaS.Infrastructure.Common.Options; using TakeoutSaaS.Infrastructure.Identity.Options; using TakeoutSaaS.Infrastructure.Identity.Persistence; using TakeoutSaaS.Infrastructure.Identity.Services; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs index 1f2bc95..487428f 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfIdentityUserRepository.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Identity.Entities; using TakeoutSaaS.Domain.Identity.Repositories; @@ -13,7 +9,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..78c3d52 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfMiniUserRepository.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Identity.Entities; using TakeoutSaaS.Domain.Identity.Repositories; @@ -12,7 +9,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/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfPermissionRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfPermissionRepository.cs index 37b7a0d..b8dbafc 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfPermissionRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfPermissionRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Identity.Entities; using TakeoutSaaS.Domain.Identity.Repositories; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRolePermissionRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRolePermissionRepository.cs index 30376da..0ba4322 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRolePermissionRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRolePermissionRepository.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Identity.Entities; using TakeoutSaaS.Domain.Identity.Repositories; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRoleTemplateRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRoleTemplateRepository.cs index e8c071b..2f77700 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRoleTemplateRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/EfRoleTemplateRepository.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Identity.Entities; using TakeoutSaaS.Domain.Identity.Repositories; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs index f4ec0a0..01c8da2 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDbContext.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDbContext.cs index 854b265..268a7c0 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDbContext.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDbContext.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using TakeoutSaaS.Domain.Identity.Entities; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/JwtTokenService.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/JwtTokenService.cs index 78d3f0e..71c3080 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/JwtTokenService.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/JwtTokenService.cs @@ -1,8 +1,8 @@ +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; -using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Contracts; using TakeoutSaaS.Infrastructure.Identity.Options; @@ -32,7 +32,7 @@ public sealed class JwtTokenService(IRefreshTokenStore refreshTokenStore, IOptio // 1. 构建 JWT Claims(包含用户 ID、账号、租户 ID、商户 ID、角色、权限等) var claims = BuildClaims(profile); - + // 2. 创建签名凭据(使用 HMAC SHA256 算法) var signingCredentials = new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Secret)), @@ -49,7 +49,7 @@ public sealed class JwtTokenService(IRefreshTokenStore refreshTokenStore, IOptio // 4. 序列化 JWT 为字符串 var accessToken = _tokenHandler.WriteToken(jwt); - + // 5. 生成刷新令牌并存储到 Redis var refreshDescriptor = await refreshTokenStore.IssueAsync(profile.UserId, refreshExpires, cancellationToken); diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs index e997c2e..3516290 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisLoginRateLimiter.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Options; using TakeoutSaaS.Application.Identity.Abstractions; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs index 36105e9..6c5651e 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs @@ -1,10 +1,7 @@ -using System; -using System.Security.Cryptography; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Options; +using System.Security.Cryptography; +using System.Text.Json; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Application.Identity.Models; using TakeoutSaaS.Infrastructure.Identity.Options; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs index 1bd8b1b..bbc6328 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/WeChatAuthService.cs @@ -1,10 +1,6 @@ -using System; -using System.Net.Http; +using Microsoft.Extensions.Options; using System.Net.Http.Json; using System.Text.Json.Serialization; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Options; using TakeoutSaaS.Application.Identity.Abstractions; using TakeoutSaaS.Infrastructure.Identity.Options; using TakeoutSaaS.Shared.Abstractions.Constants; diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20251202005208_InitSnowflake_App.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20251202005208_InitSnowflake_App.cs index 4b31527..6e26a56 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20251202005208_InitSnowflake_App.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20251202005208_InitSnowflake_App.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202005247_InitSnowflake_Dictionary.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202005247_InitSnowflake_Dictionary.cs index 374be49..7a1aa27 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202005247_InitSnowflake_Dictionary.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202005247_InitSnowflake_Dictionary.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202043204_AddSystemParametersTable.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202043204_AddSystemParametersTable.cs index 816fe03..27d688a 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202043204_AddSystemParametersTable.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/DictionaryDb/20251202043204_AddSystemParametersTable.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202005226_InitSnowflake_Identity.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202005226_InitSnowflake_Identity.cs index 772ad91..4893332 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202005226_InitSnowflake_Identity.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202005226_InitSnowflake_Identity.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202084523_AddRbacModel.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202084523_AddRbacModel.cs index aa64905..42a404e 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202084523_AddRbacModel.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/IdentityDb/20251202084523_AddRbacModel.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable diff --git a/src/Modules/TakeoutSaaS.Module.Authorization/Policies/PermissionAuthorizationHandler.cs b/src/Modules/TakeoutSaaS.Module.Authorization/Policies/PermissionAuthorizationHandler.cs index b8df4fe..780e58d 100644 --- a/src/Modules/TakeoutSaaS.Module.Authorization/Policies/PermissionAuthorizationHandler.cs +++ b/src/Modules/TakeoutSaaS.Module.Authorization/Policies/PermissionAuthorizationHandler.cs @@ -1,6 +1,3 @@ -using System; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; namespace TakeoutSaaS.Module.Authorization.Policies; diff --git a/src/Modules/TakeoutSaaS.Module.Delivery/TakeoutSaaS.Module.Delivery.csproj b/src/Modules/TakeoutSaaS.Module.Delivery/TakeoutSaaS.Module.Delivery.csproj index b407eac..ff77596 100644 --- a/src/Modules/TakeoutSaaS.Module.Delivery/TakeoutSaaS.Module.Delivery.csproj +++ b/src/Modules/TakeoutSaaS.Module.Delivery/TakeoutSaaS.Module.Delivery.csproj @@ -4,6 +4,9 @@ enable enable + + + diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessagePublisher.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessagePublisher.cs index 666c554..456be39 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessagePublisher.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessagePublisher.cs @@ -1,6 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; - namespace TakeoutSaaS.Module.Messaging.Abstractions; /// diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessageSubscriber.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessageSubscriber.cs index 1e7e0bd..685c523 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessageSubscriber.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Abstractions/IMessageSubscriber.cs @@ -1,7 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - namespace TakeoutSaaS.Module.Messaging.Abstractions; /// diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs index 701a992..3f2178c 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs @@ -1,5 +1,3 @@ -using System; -using System.Text; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using RabbitMQ.Client; @@ -22,17 +20,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.Scheduler/Abstractions/IRecurringJobRegistrar.cs b/src/Modules/TakeoutSaaS.Module.Scheduler/Abstractions/IRecurringJobRegistrar.cs index 79f5a29..198da74 100644 --- a/src/Modules/TakeoutSaaS.Module.Scheduler/Abstractions/IRecurringJobRegistrar.cs +++ b/src/Modules/TakeoutSaaS.Module.Scheduler/Abstractions/IRecurringJobRegistrar.cs @@ -1,6 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; - namespace TakeoutSaaS.Module.Scheduler.Abstractions; /// diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Abstractions/ISmsSender.cs b/src/Modules/TakeoutSaaS.Module.Sms/Abstractions/ISmsSender.cs index 5cf7dd5..01380db 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Abstractions/ISmsSender.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Abstractions/ISmsSender.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Module.Sms.Models; namespace TakeoutSaaS.Module.Sms.Abstractions; diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Models/SmsSendRequest.cs b/src/Modules/TakeoutSaaS.Module.Sms/Models/SmsSendRequest.cs index 643f299..fe3b333 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Models/SmsSendRequest.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Models/SmsSendRequest.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace TakeoutSaaS.Module.Sms.Models; /// diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Options/SmsOptions.cs b/src/Modules/TakeoutSaaS.Module.Sms/Options/SmsOptions.cs index 5520760..32c3ada 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Options/SmsOptions.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Options/SmsOptions.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using TakeoutSaaS.Module.Sms; namespace TakeoutSaaS.Module.Sms.Options; diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs b/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs index c94a47c..213cfa0 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Services/AliyunSmsSender.cs @@ -1,4 +1,3 @@ -using System.Net.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using TakeoutSaaS.Module.Sms.Abstractions; @@ -13,8 +12,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; @@ -27,6 +24,8 @@ public sealed class AliyunSmsSender(IHttpClientFactory httpClientFactory, IOptio logger.LogInformation("Mock 发送阿里云短信到 {Phone}, Template:{Template}", request.PhoneNumber, request.TemplateCode); return Task.FromResult(new SmsSendResult { Success = true, Message = "Mocked" }); } + // 预留 HttpClient,便于后续接入阿里云正式签名请求 + using var httpClient = httpClientFactory.CreateClient(nameof(AliyunSmsSender)); // 占位:保留待接入阿里云正式签名流程,当前返回未实现。 logger.LogWarning("阿里云短信尚未启用,请配置腾讯云或开启 UseMock。"); diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Services/SmsSenderResolver.cs b/src/Modules/TakeoutSaaS.Module.Sms/Services/SmsSenderResolver.cs index e47f6be..2d5906b 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Services/SmsSenderResolver.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Services/SmsSenderResolver.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.Extensions.Options; using TakeoutSaaS.Module.Sms.Abstractions; using TakeoutSaaS.Module.Sms.Options; diff --git a/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs b/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs index f7b9737..ee14a8f 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs +++ b/src/Modules/TakeoutSaaS.Module.Sms/Services/TencentSmsSender.cs @@ -1,10 +1,9 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System.Globalization; -using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Text.Json; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using TakeoutSaaS.Module.Sms.Abstractions; using TakeoutSaaS.Module.Sms.Models; using TakeoutSaaS.Module.Sms.Options; @@ -27,6 +26,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 +34,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 +45,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 +60,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 +70,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/Abstractions/IObjectStorageProvider.cs b/src/Modules/TakeoutSaaS.Module.Storage/Abstractions/IObjectStorageProvider.cs index c53009c..41b6e31 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Abstractions/IObjectStorageProvider.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Abstractions/IObjectStorageProvider.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using TakeoutSaaS.Module.Storage.Models; namespace TakeoutSaaS.Module.Storage.Abstractions; 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/StorageDirectUploadResult.cs b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadResult.cs index 8bfade5..15d11dd 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadResult.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageDirectUploadResult.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace TakeoutSaaS.Module.Storage.Models; /// diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs index 3d055ab..795a614 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Models/StorageUploadRequest.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.IO; - namespace TakeoutSaaS.Module.Storage.Models; /// @@ -25,7 +22,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..0244720 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Providers/AliyunOssStorageProvider.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Providers/AliyunOssStorageProvider.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using Aliyun.OSS; -using Aliyun.OSS.Util; using Microsoft.Extensions.Options; using TakeoutSaaS.Module.Storage.Abstractions; using TakeoutSaaS.Module.Storage.Models; @@ -28,6 +22,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 +36,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 +59,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 +80,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 +111,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/QiniuKodoStorageProvider.cs b/src/Modules/TakeoutSaaS.Module.Storage/Providers/QiniuKodoStorageProvider.cs index df6b05d..bccb657 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Providers/QiniuKodoStorageProvider.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Providers/QiniuKodoStorageProvider.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.Extensions.Options; using TakeoutSaaS.Module.Storage.Options; diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs b/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs index afdfe96..0745830 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Amazon; using Amazon.Runtime; using Amazon.S3; using Amazon.S3.Model; @@ -65,6 +60,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 +75,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 +97,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 +118,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.Storage/Providers/TencentCosStorageProvider.cs b/src/Modules/TakeoutSaaS.Module.Storage/Providers/TencentCosStorageProvider.cs index fdd7795..259d6e4 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Providers/TencentCosStorageProvider.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Providers/TencentCosStorageProvider.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.Extensions.Options; using TakeoutSaaS.Module.Storage.Options; diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Services/StorageProviderResolver.cs b/src/Modules/TakeoutSaaS.Module.Storage/Services/StorageProviderResolver.cs index 598d1ee..b1920c0 100644 --- a/src/Modules/TakeoutSaaS.Module.Storage/Services/StorageProviderResolver.cs +++ b/src/Modules/TakeoutSaaS.Module.Storage/Services/StorageProviderResolver.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.Extensions.Options; using TakeoutSaaS.Module.Storage.Abstractions; using TakeoutSaaS.Module.Storage.Options; diff --git a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantContextAccessor.cs b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantContextAccessor.cs index 2a7f7ab..b3b0589 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantContextAccessor.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantContextAccessor.cs @@ -1,4 +1,3 @@ -using System.Threading; using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Module.Tenancy; 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..46c9770 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionMiddleware.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -20,8 +19,6 @@ public sealed class TenantResolutionMiddleware( ITenantContextAccessor tenantContextAccessor, IOptionsMonitor optionsMonitor) { - - /// /// 解析租户并将上下文注入请求。 /// diff --git a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionOptions.cs b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionOptions.cs index 7cd928d..e43afa3 100644 --- a/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionOptions.cs +++ b/src/Modules/TakeoutSaaS.Module.Tenancy/TenantResolutionOptions.cs @@ -33,7 +33,7 @@ public sealed class TenantResolutionOptions /// 根域(不含子域),用于形如 {tenant}.rootDomain 的场景,例如 admin.takeoutsaas.com。 /// public string? RootDomain { get; set; } - + /// /// 需要跳过租户解析的路径集合(如健康检查),默认仅包含 /health。 /// diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 0000000..c5f59ab --- /dev/null +++ b/stylecop.json @@ -0,0 +1,7 @@ +{ + "settings": { + "documentationRules": { + "documentationCulture": "zh-CN" + } + } +}