feat: 新增加料管理接口与模板能力
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 43s
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 43s
This commit is contained in:
@@ -1185,12 +1185,15 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.StoreId).IsRequired();
|
||||
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
|
||||
builder.Property(x => x.Description).HasMaxLength(256).IsRequired();
|
||||
builder.Property(x => x.TemplateType).HasConversion<int>();
|
||||
builder.Property(x => x.SelectionType).HasConversion<int>();
|
||||
builder.Property(x => x.MinSelect).IsRequired();
|
||||
builder.Property(x => x.MaxSelect).IsRequired();
|
||||
builder.Property(x => x.SortOrder).IsRequired();
|
||||
builder.Property(x => x.IsEnabled).IsRequired();
|
||||
builder.Property(x => x.IsRequired).IsRequired();
|
||||
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name }).IsUnique();
|
||||
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TemplateType, x.Name }).IsUnique();
|
||||
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TemplateType, x.IsEnabled });
|
||||
}
|
||||
|
||||
@@ -1201,6 +1204,8 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.Property(x => x.TemplateId).IsRequired();
|
||||
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
|
||||
builder.Property(x => x.ExtraPrice).HasPrecision(18, 2);
|
||||
builder.Property(x => x.Stock).IsRequired();
|
||||
builder.Property(x => x.IsEnabled).IsRequired();
|
||||
builder.Property(x => x.SortOrder).IsRequired();
|
||||
builder.HasIndex(x => new { x.TenantId, x.TemplateId, x.Name }).IsUnique();
|
||||
}
|
||||
|
||||
@@ -138,7 +138,24 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
|
||||
{
|
||||
return await context.ProductSpecTemplates
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId)
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
x.StoreId == storeId &&
|
||||
x.TemplateType != ProductSpecTemplateType.Addon)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ThenBy(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductSpecTemplate>> GetAddonTemplatesByStoreAsync(long tenantId, long storeId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.ProductSpecTemplates
|
||||
.AsNoTracking()
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
x.StoreId == storeId &&
|
||||
x.TemplateType == ProductSpecTemplateType.Addon)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ThenBy(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
@@ -148,7 +165,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
|
||||
public Task<ProductSpecTemplate?> FindSpecTemplateByIdAsync(long templateId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.ProductSpecTemplates
|
||||
.Where(x => x.TenantId == tenantId && x.Id == templateId)
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
x.Id == templateId &&
|
||||
x.TemplateType != ProductSpecTemplateType.Addon)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ProductSpecTemplate?> FindAddonTemplateByIdAsync(long templateId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.ProductSpecTemplates
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
x.Id == templateId &&
|
||||
x.TemplateType == ProductSpecTemplateType.Addon)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -162,6 +193,28 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
x.StoreId == storeId &&
|
||||
x.TemplateType != ProductSpecTemplateType.Addon &&
|
||||
x.Name.ToLower() == normalizedLower);
|
||||
|
||||
if (excludeTemplateId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Id != excludeTemplateId.Value);
|
||||
}
|
||||
|
||||
return query.AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> ExistsAddonTemplateNameAsync(long tenantId, long storeId, string name, long? excludeTemplateId = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var normalizedName = (name ?? string.Empty).Trim();
|
||||
var normalizedLower = normalizedName.ToLowerInvariant();
|
||||
var query = context.ProductSpecTemplates
|
||||
.AsNoTracking()
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
x.StoreId == storeId &&
|
||||
x.TemplateType == ProductSpecTemplateType.Addon &&
|
||||
x.Name.ToLower() == normalizedLower);
|
||||
|
||||
if (excludeTemplateId.HasValue)
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ExtendProductSpecTemplateForAddonGroups : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_product_spec_templates_TenantId_StoreId_Name",
|
||||
table: "product_spec_templates");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Description",
|
||||
table: "product_spec_templates",
|
||||
type: "character varying(256)",
|
||||
maxLength: 256,
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
comment: "模板描述。");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "MaxSelect",
|
||||
table: "product_spec_templates",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 1,
|
||||
comment: "最大可选数。");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "MinSelect",
|
||||
table: "product_spec_templates",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
comment: "最小可选数。");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsEnabled",
|
||||
table: "product_spec_template_options",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: true,
|
||||
comment: "是否启用。");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Stock",
|
||||
table: "product_spec_template_options",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 999,
|
||||
comment: "库存数量。");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_product_spec_templates_TenantId_StoreId_TemplateType_Name",
|
||||
table: "product_spec_templates",
|
||||
columns: new[] { "TenantId", "StoreId", "TemplateType", "Name" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_product_spec_templates_TenantId_StoreId_TemplateType_Name",
|
||||
table: "product_spec_templates");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Description",
|
||||
table: "product_spec_templates");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MaxSelect",
|
||||
table: "product_spec_templates");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MinSelect",
|
||||
table: "product_spec_templates");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsEnabled",
|
||||
table: "product_spec_template_options");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Stock",
|
||||
table: "product_spec_template_options");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_product_spec_templates_TenantId_StoreId_Name",
|
||||
table: "product_spec_templates",
|
||||
columns: new[] { "TenantId", "StoreId", "Name" },
|
||||
unique: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4689,6 +4689,12 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("模板描述。");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否启用。");
|
||||
@@ -4697,12 +4703,20 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否必选。");
|
||||
|
||||
b.Property<int>("MaxSelect")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("最大可选数。");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("模板名称。");
|
||||
|
||||
b.Property<int>("MinSelect")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("最小可选数。");
|
||||
|
||||
b.Property<int>("SelectionType")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("选择方式。");
|
||||
@@ -4733,7 +4747,7 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId", "StoreId", "Name")
|
||||
b.HasIndex("TenantId", "StoreId", "TemplateType", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("TenantId", "StoreId", "TemplateType", "IsEnabled");
|
||||
@@ -4774,6 +4788,10 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasComment("附加价格。");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否启用。");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
@@ -4784,6 +4802,10 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("integer")
|
||||
.HasComment("排序值。");
|
||||
|
||||
b.Property<int>("Stock")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("库存数量。");
|
||||
|
||||
b.Property<long>("TemplateId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("模板 ID。");
|
||||
|
||||
Reference in New Issue
Block a user