From d41f69045fc79dcc25f43e935e84e76316acf5a2 Mon Sep 17 00:00:00 2001
From: MSuMshk <2039814060@qq.com>
Date: Sat, 21 Feb 2026 11:46:55 +0800
Subject: [PATCH] feat(product): add product schedule management api
---
.../Product/ProductScheduleContracts.cs | 156 +
.../Controllers/ProductScheduleController.cs | 139 +
.../ChangeProductScheduleStatusCommand.cs | 25 +
.../Commands/DeleteProductScheduleCommand.cs | 19 +
.../Commands/SaveProductScheduleCommand.cs | 50 +
.../Products/Dto/ProductScheduleItemDto.cs | 52 +
...angeProductScheduleStatusCommandHandler.cs | 52 +
.../DeleteProductScheduleCommandHandler.cs | 34 +
.../GetProductScheduleListQueryHandler.cs | 71 +
.../SaveProductScheduleCommandHandler.cs | 146 +
.../App/Products/ProductScheduleDtoFactory.cs | 35 +
.../App/Products/ProductScheduleMapping.cs | 102 +
.../Queries/GetProductScheduleListQuery.cs | 25 +
.../Products/Entities/ProductSchedule.cs | 39 +
.../Entities/ProductScheduleProduct.cs | 24 +
.../Repositories/IProductRepository.cs | 45 +
.../App/Persistence/TakeoutAppDbContext.cs | 35 +
.../App/Repositories/EfProductRepository.cs | 105 +
...0221032856_AddProductSchedules.Designer.cs | 8371 +++++++++++++++++
.../20260221032856_AddProductSchedules.cs | 97 +
.../TakeoutAppDbContextModelSnapshot.cs | 138 +
21 files changed, 9760 insertions(+)
create mode 100644 src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductScheduleContracts.cs
create mode 100644 src/Api/TakeoutSaaS.TenantApi/Controllers/ProductScheduleController.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Commands/ChangeProductScheduleStatusCommand.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Commands/DeleteProductScheduleCommand.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Commands/SaveProductScheduleCommand.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Dto/ProductScheduleItemDto.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Handlers/ChangeProductScheduleStatusCommandHandler.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Handlers/DeleteProductScheduleCommandHandler.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Handlers/GetProductScheduleListQueryHandler.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Handlers/SaveProductScheduleCommandHandler.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/ProductScheduleDtoFactory.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/ProductScheduleMapping.cs
create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Queries/GetProductScheduleListQuery.cs
create mode 100644 src/Domain/TakeoutSaaS.Domain/Products/Entities/ProductSchedule.cs
create mode 100644 src/Domain/TakeoutSaaS.Domain/Products/Entities/ProductScheduleProduct.cs
create mode 100644 src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221032856_AddProductSchedules.Designer.cs
create mode 100644 src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221032856_AddProductSchedules.cs
diff --git a/src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductScheduleContracts.cs b/src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductScheduleContracts.cs
new file mode 100644
index 0000000..19cb589
--- /dev/null
+++ b/src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductScheduleContracts.cs
@@ -0,0 +1,156 @@
+namespace TakeoutSaaS.TenantApi.Contracts.Product;
+
+///
+/// 商品时段规则列表查询请求。
+///
+public sealed class ProductScheduleListRequest
+{
+ ///
+ /// 门店 ID。
+ ///
+ public string StoreId { get; set; } = string.Empty;
+
+ ///
+ /// 关键字。
+ ///
+ public string? Keyword { get; set; }
+
+ ///
+ /// 状态(enabled/disabled)。
+ ///
+ public string? Status { get; set; }
+}
+
+///
+/// 保存商品时段规则请求。
+///
+public sealed class SaveProductScheduleRequest
+{
+ ///
+ /// 门店 ID。
+ ///
+ public string StoreId { get; set; } = string.Empty;
+
+ ///
+ /// 规则 ID(编辑时传)。
+ ///
+ public string? Id { get; set; }
+
+ ///
+ /// 规则名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 开始时间(HH:mm)。
+ ///
+ public string StartTime { get; set; } = "00:00";
+
+ ///
+ /// 结束时间(HH:mm)。
+ ///
+ public string EndTime { get; set; } = "00:00";
+
+ ///
+ /// 适用星期(1-7)。
+ ///
+ public List WeekDays { get; set; } = [];
+
+ ///
+ /// 关联商品 ID 列表。
+ ///
+ public List ProductIds { get; set; } = [];
+
+ ///
+ /// 状态(enabled/disabled)。
+ ///
+ public string Status { get; set; } = "enabled";
+}
+
+///
+/// 删除商品时段规则请求。
+///
+public sealed class DeleteProductScheduleRequest
+{
+ ///
+ /// 门店 ID。
+ ///
+ public string StoreId { get; set; } = string.Empty;
+
+ ///
+ /// 规则 ID。
+ ///
+ public string ScheduleId { get; set; } = string.Empty;
+}
+
+///
+/// 修改商品时段规则状态请求。
+///
+public sealed class ChangeProductScheduleStatusRequest
+{
+ ///
+ /// 门店 ID。
+ ///
+ public string StoreId { get; set; } = string.Empty;
+
+ ///
+ /// 规则 ID。
+ ///
+ public string ScheduleId { get; set; } = string.Empty;
+
+ ///
+ /// 状态(enabled/disabled)。
+ ///
+ public string Status { get; set; } = "enabled";
+}
+
+///
+/// 商品时段规则列表项响应。
+///
+public sealed class ProductScheduleItemResponse
+{
+ ///
+ /// 规则 ID。
+ ///
+ public string Id { get; set; } = string.Empty;
+
+ ///
+ /// 规则名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 开始时间(HH:mm)。
+ ///
+ public string StartTime { get; set; } = "00:00";
+
+ ///
+ /// 结束时间(HH:mm)。
+ ///
+ public string EndTime { get; set; } = "00:00";
+
+ ///
+ /// 适用星期(1-7)。
+ ///
+ public List WeekDays { get; set; } = [];
+
+ ///
+ /// 状态(enabled/disabled)。
+ ///
+ public string Status { get; set; } = "enabled";
+
+ ///
+ /// 关联商品数量。
+ ///
+ public int ProductCount { get; set; }
+
+ ///
+ /// 关联商品 ID 列表。
+ ///
+ public List ProductIds { get; set; } = [];
+
+ ///
+ /// 更新时间。
+ ///
+ public string UpdatedAt { get; set; } = string.Empty;
+}
diff --git a/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductScheduleController.cs b/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductScheduleController.cs
new file mode 100644
index 0000000..c8a29b1
--- /dev/null
+++ b/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductScheduleController.cs
@@ -0,0 +1,139 @@
+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.Application.App.Stores.Services;
+using TakeoutSaaS.Infrastructure.App.Persistence;
+using TakeoutSaaS.Shared.Abstractions.Results;
+using TakeoutSaaS.Shared.Web.Api;
+using TakeoutSaaS.TenantApi.Contracts.Product;
+
+namespace TakeoutSaaS.TenantApi.Controllers;
+
+///
+/// 租户端商品时段规则管理。
+///
+[ApiVersion("1.0")]
+[Authorize]
+[Route("api/tenant/v{version:apiVersion}/product")]
+public sealed class ProductScheduleController(
+ IMediator mediator,
+ TakeoutAppDbContext dbContext,
+ StoreContextService storeContextService) : BaseApiController
+{
+ ///
+ /// 商品时段规则列表。
+ ///
+ [HttpGet("schedule/list")]
+ [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)]
+ public async Task>> GetScheduleList(
+ [FromQuery] ProductScheduleListRequest request,
+ CancellationToken cancellationToken)
+ {
+ var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
+ await EnsureStoreAccessibleAsync(storeId, cancellationToken);
+
+ var result = await mediator.Send(new GetProductScheduleListQuery
+ {
+ StoreId = storeId,
+ Keyword = request.Keyword,
+ Status = request.Status
+ }, cancellationToken);
+
+ return ApiResponse>.Ok(result.Select(MapScheduleItem).ToList());
+ }
+
+ ///
+ /// 保存商品时段规则。
+ ///
+ [HttpPost("schedule/save")]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ public async Task> SaveSchedule(
+ [FromBody] SaveProductScheduleRequest request,
+ CancellationToken cancellationToken)
+ {
+ var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
+ await EnsureStoreAccessibleAsync(storeId, cancellationToken);
+
+ var result = await mediator.Send(new SaveProductScheduleCommand
+ {
+ StoreId = storeId,
+ ScheduleId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id),
+ Name = request.Name,
+ StartTime = request.StartTime,
+ EndTime = request.EndTime,
+ WeekDays = request.WeekDays ?? [],
+ ProductIds = StoreApiHelpers.ParseSnowflakeList(request.ProductIds),
+ Status = request.Status
+ }, cancellationToken);
+
+ return ApiResponse.Ok(MapScheduleItem(result));
+ }
+
+ ///
+ /// 删除商品时段规则。
+ ///
+ [HttpPost("schedule/delete")]
+ [ProducesResponseType(typeof(ApiResponse