feat: 商品列表查询改为数据库分页过滤
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 1m53s

This commit is contained in:
2026-02-26 10:50:20 +08:00
parent 8f64eb897b
commit 3f5ca9c3ee
3 changed files with 173 additions and 54 deletions

View File

@@ -60,6 +60,93 @@ public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductR
return products;
}
/// <inheritdoc />
public async Task<(IReadOnlyList<Product> Items, int Total)> SearchPagedAsync(
ProductSearchFilter filter,
CancellationToken cancellationToken = default)
{
if (filter.TenantId <= 0)
{
throw new InvalidOperationException("TenantId 不能为空且必须大于 0");
}
var page = filter.Page <= 0 ? 1 : filter.Page;
var pageSize = filter.PageSize <= 0 ? 20 : filter.PageSize;
var query = context.Products
.AsNoTracking()
.Where(x => x.TenantId == filter.TenantId);
if (filter.StoreId.HasValue)
{
query = query.Where(x => x.StoreId == filter.StoreId.Value);
}
if (filter.CategoryId.HasValue)
{
query = query.Where(x => x.CategoryId == filter.CategoryId.Value);
}
if (filter.Status.HasValue)
{
query = query.Where(x => x.Status == filter.Status.Value);
}
if (!string.IsNullOrWhiteSpace(filter.Keyword))
{
var keyword = $"%{filter.Keyword.Trim()}%";
query = query.Where(x =>
EF.Functions.ILike(x.Name, keyword) ||
EF.Functions.ILike(x.SpuCode, keyword));
}
if (filter.Kind.HasValue)
{
query = query.Where(x => x.Kind == filter.Kind.Value);
}
if (filter.IsSoldOut == true)
{
query = query.Where(x => x.SoldoutMode.HasValue);
}
else if (filter.Status == ProductStatus.OffShelf)
{
// 状态为 OffShelf 时,排除“沽清”以保持前台“下架”口径。
query = query.Where(x => !x.SoldoutMode.HasValue);
}
var sorted = ApplySearchSort(query, filter.SortBy, filter.SortDescending);
var total = await sorted.CountAsync(cancellationToken);
var items = await sorted
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync(cancellationToken);
return (items, total);
}
private static IOrderedQueryable<Product> ApplySearchSort(
IQueryable<Product> query,
string? sortBy,
bool sortDescending)
{
return sortBy?.ToLowerInvariant() switch
{
"name" => sortDescending
? query.OrderByDescending(x => x.Name).ThenByDescending(x => x.Id)
: query.OrderBy(x => x.Name).ThenBy(x => x.Id),
"price" => sortDescending
? query.OrderByDescending(x => x.Price).ThenByDescending(x => x.Id)
: query.OrderBy(x => x.Price).ThenBy(x => x.Id),
"status" => sortDescending
? query.OrderByDescending(x => x.Status).ThenByDescending(x => x.Id)
: query.OrderBy(x => x.Status).ThenBy(x => x.Id),
_ => sortDescending
? query.OrderByDescending(x => x.CreatedAt).ThenByDescending(x => x.Id)
: query.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)
};
}
/// <inheritdoc />
public async Task<IReadOnlyList<ProductCategory>> GetCategoriesAsync(long tenantId, CancellationToken cancellationToken = default)
{