feat: 菜品菜单查询与子资源替换完善

This commit is contained in:
2025-12-04 10:42:58 +08:00
parent de5f13ec83
commit b8d93337f2
25 changed files with 1133 additions and 10 deletions

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Products.Entities;
@@ -27,7 +28,7 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
}
/// <inheritdoc />
public async Task<IReadOnlyList<Product>> SearchAsync(long tenantId, long? storeId, long? categoryId, ProductStatus? status, CancellationToken cancellationToken = default)
public async Task<IReadOnlyList<Product>> SearchAsync(long tenantId, long? storeId, long? categoryId, ProductStatus? status, CancellationToken cancellationToken = default, DateTime? updatedAfter = null)
{
var query = context.Products
.AsNoTracking()
@@ -48,6 +49,11 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
query = query.Where(x => x.Status == status.Value);
}
if (updatedAfter.HasValue)
{
query = query.Where(x => (x.UpdatedAt ?? x.CreatedAt) >= updatedAfter.Value);
}
var products = await query
.OrderBy(x => x.Name)
.ToListAsync(cancellationToken);
@@ -67,6 +73,22 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return categories;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductCategory>> GetCategoriesByStoreAsync(long tenantId, long storeId, bool onlyEnabled = true, CancellationToken cancellationToken = default)
{
var query = context.ProductCategories
.AsNoTracking()
.Where(x => x.TenantId == tenantId && x.StoreId == storeId);
if (onlyEnabled)
{
query = query.Where(x => x.IsEnabled);
}
var categories = await query
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return categories;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductSku>> GetSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
{
@@ -79,6 +101,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return skus;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductSku>> GetSkusByProductIdsAsync(IReadOnlyCollection<long> productIds, long tenantId, CancellationToken cancellationToken = default)
{
if (productIds.Count == 0)
{
return Array.Empty<ProductSku>();
}
var skus = await context.ProductSkus
.AsNoTracking()
.Where(x => x.TenantId == tenantId && productIds.Contains(x.ProductId))
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return skus;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAddonGroup>> GetAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
{
@@ -91,6 +128,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return groups;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAddonGroup>> GetAddonGroupsByProductIdsAsync(IReadOnlyCollection<long> productIds, long tenantId, CancellationToken cancellationToken = default)
{
if (productIds.Count == 0)
{
return Array.Empty<ProductAddonGroup>();
}
var groups = await context.ProductAddonGroups
.AsNoTracking()
.Where(x => x.TenantId == tenantId && productIds.Contains(x.ProductId))
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return groups;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAddonOption>> GetAddonOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
{
@@ -114,6 +166,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return options;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAddonOption>> GetAddonOptionsByGroupIdsAsync(IReadOnlyCollection<long> addonGroupIds, long tenantId, CancellationToken cancellationToken = default)
{
if (addonGroupIds.Count == 0)
{
return Array.Empty<ProductAddonOption>();
}
var options = await context.ProductAddonOptions
.AsNoTracking()
.Where(x => x.TenantId == tenantId && addonGroupIds.Contains(x.AddonGroupId))
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return options;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAttributeGroup>> GetAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
{
@@ -126,6 +193,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return groups;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAttributeGroup>> GetAttributeGroupsByProductIdsAsync(IReadOnlyCollection<long> productIds, long tenantId, CancellationToken cancellationToken = default)
{
if (productIds.Count == 0)
{
return Array.Empty<ProductAttributeGroup>();
}
var groups = await context.ProductAttributeGroups
.AsNoTracking()
.Where(x => x.TenantId == tenantId && productIds.Contains(x.ProductId))
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return groups;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAttributeOption>> GetAttributeOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
{
@@ -149,6 +231,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return options;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductAttributeOption>> GetAttributeOptionsByGroupIdsAsync(IReadOnlyCollection<long> attributeGroupIds, long tenantId, CancellationToken cancellationToken = default)
{
if (attributeGroupIds.Count == 0)
{
return Array.Empty<ProductAttributeOption>();
}
var options = await context.ProductAttributeOptions
.AsNoTracking()
.Where(x => x.TenantId == tenantId && attributeGroupIds.Contains(x.AttributeGroupId))
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return options;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductMediaAsset>> GetMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
{
@@ -161,6 +258,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return assets;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductMediaAsset>> GetMediaAssetsByProductIdsAsync(IReadOnlyCollection<long> productIds, long tenantId, CancellationToken cancellationToken = default)
{
if (productIds.Count == 0)
{
return Array.Empty<ProductMediaAsset>();
}
var assets = await context.ProductMediaAssets
.AsNoTracking()
.Where(x => x.TenantId == tenantId && productIds.Contains(x.ProductId))
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return assets;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductPricingRule>> GetPricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
{
@@ -173,6 +285,21 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return rules;
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductPricingRule>> GetPricingRulesByProductIdsAsync(IReadOnlyCollection<long> productIds, long tenantId, CancellationToken cancellationToken = default)
{
if (productIds.Count == 0)
{
return Array.Empty<ProductPricingRule>();
}
var rules = await context.ProductPricingRules
.AsNoTracking()
.Where(x => x.TenantId == tenantId && productIds.Contains(x.ProductId))
.OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken);
return rules;
}
/// <inheritdoc />
public Task AddCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default)
{