using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Products.Commands; using TakeoutSaaS.Application.App.Products.Dto; using TakeoutSaaS.Application.App.Products.Queries; using TakeoutSaaS.Domain.Products.Enums; using TakeoutSaaS.Module.Authorization.Attributes; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Results; using TakeoutSaaS.Shared.Web.Api; 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)] public async Task>> List( [FromQuery] long? storeId, [FromQuery] long? categoryId, [FromQuery] ProductStatus? status, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? sortBy = null, [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { // 1. 组装查询参数并执行查询 var result = await mediator.Send(new SearchProductsQuery { StoreId = storeId, CategoryId = categoryId, Status = status, Page = page, PageSize = pageSize, SortBy = sortBy, 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); } /// /// 更新商品。 /// /// 更新后的商品信息。 [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); } /// /// 删除商品。 /// /// 删除结果。 [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, "商品不存在"); } /// /// 获取商品全量详情。 /// [HttpGet("{productId:long}/detail")] [PermissionAuthorize("product:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> FullDetail(long productId, CancellationToken cancellationToken) { var result = await mediator.Send(new GetProductDetailQuery { ProductId = productId }, cancellationToken); return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商品不存在") : ApiResponse.Ok(result); } /// /// 上架商品。 /// [HttpPost("{productId:long}/publish")] [PermissionAuthorize("product:publish")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Publish(long productId, [FromBody] PublishProductCommand command, CancellationToken cancellationToken) { if (command.ProductId == 0) { command = command with { ProductId = productId }; } var result = await mediator.Send(command, cancellationToken); return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商品不存在") : ApiResponse.Ok(result); } /// /// 下架商品。 /// [HttpPost("{productId:long}/unpublish")] [PermissionAuthorize("product:publish")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Unpublish(long productId, [FromBody] UnpublishProductCommand command, CancellationToken cancellationToken) { if (command.ProductId == 0) { command = command with { ProductId = productId }; } var result = await mediator.Send(command, cancellationToken); return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商品不存在") : ApiResponse.Ok(result); } /// /// 替换商品 SKU。 /// [HttpPut("{productId:long}/skus")] [PermissionAuthorize("product-sku:update")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> ReplaceSkus(long productId, [FromBody] ReplaceProductSkusCommand command, CancellationToken cancellationToken) { if (command.ProductId == 0) { command = command with { ProductId = productId }; } var result = await mediator.Send(command, cancellationToken); return ApiResponse>.Ok(result); } /// /// 替换商品规格。 /// [HttpPut("{productId:long}/attributes")] [PermissionAuthorize("product-attr:update")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> ReplaceAttributes(long productId, [FromBody] ReplaceProductAttributesCommand command, CancellationToken cancellationToken) { if (command.ProductId == 0) { command = command with { ProductId = productId }; } var result = await mediator.Send(command, cancellationToken); return ApiResponse>.Ok(result); } /// /// 替换商品加料。 /// [HttpPut("{productId:long}/addons")] [PermissionAuthorize("product-addon:update")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> ReplaceAddons(long productId, [FromBody] ReplaceProductAddonsCommand command, CancellationToken cancellationToken) { if (command.ProductId == 0) { command = command with { ProductId = productId }; } var result = await mediator.Send(command, cancellationToken); return ApiResponse>.Ok(result); } /// /// 替换商品媒资。 /// [HttpPut("{productId:long}/media")] [PermissionAuthorize("product-media:update")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> ReplaceMedia(long productId, [FromBody] ReplaceProductMediaCommand command, CancellationToken cancellationToken) { if (command.ProductId == 0) { command = command with { ProductId = productId }; } var result = await mediator.Send(command, cancellationToken); return ApiResponse>.Ok(result); } /// /// 替换商品价格策略。 /// [HttpPut("{productId:long}/pricing-rules")] [PermissionAuthorize("product-pricing:update")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> ReplacePricingRules(long productId, [FromBody] ReplaceProductPricingRulesCommand command, CancellationToken cancellationToken) { if (command.ProductId == 0) { command = command with { ProductId = productId }; } var result = await mediator.Send(command, cancellationToken); return ApiResponse>.Ok(result); } }