feat: 商品列表查询改为数据库分页过滤
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 1m53s
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 1m53s
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user