fix(store): enforce UTC holiday dates and exclude deleted stores
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 42s

This commit is contained in:
2026-02-18 17:25:34 +08:00
parent dfa2b3ee52
commit 8139c08b35
2 changed files with 30 additions and 10 deletions

View File

@@ -95,7 +95,8 @@ internal static class StoreApiHelpers
throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 日期格式必须为 yyyy-MM-dd"); throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 日期格式必须为 yyyy-MM-dd");
} }
return parsed.Date; // PostgreSQL timestamptz 仅接受 UTC日期输入统一落盘为 UTC 零点。
return DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
} }
public static string ToDateOnly(DateTime value) public static string ToDateOnly(DateTime value)

View File

@@ -33,7 +33,13 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
query = query.Where(x => x.TenantId == tenantId.Value); query = query.Where(x => x.TenantId == tenantId.Value);
} }
// 3. (空行后) 返回门店实体 // 3. (空行后) 默认排除已删除门店(双保险)
if (!includeDeleted)
{
query = query.Where(x => x.DeletedAt == null);
}
// 4. (空行后) 返回门店实体
return query return query
.Where(x => x.Id == storeId) .Where(x => x.Id == storeId)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
@@ -44,7 +50,10 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
{ {
return await context.Stores return await context.Stores
.AsNoTracking() .AsNoTracking()
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId) .Where(x =>
x.TenantId == tenantId &&
x.MerchantId == merchantId &&
x.DeletedAt == null)
.OrderBy(x => x.Name) .OrderBy(x => x.Name)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
} }
@@ -81,31 +90,37 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
query = query.Where(x => x.MerchantId == merchantId.Value); query = query.Where(x => x.MerchantId == merchantId.Value);
} }
// 4. (空行后) 可选过滤:状态 // 4. (空行后) 默认排除已删除门店(双保险)
if (!includeDeleted)
{
query = query.Where(x => x.DeletedAt == null);
}
// 5. (空行后) 可选过滤:状态
if (status.HasValue) if (status.HasValue)
{ {
query = query.Where(x => x.Status == status.Value); query = query.Where(x => x.Status == status.Value);
} }
// 5. (空行后) 可选过滤:审核状态 // 6. (空行后) 可选过滤:审核状态
if (auditStatus.HasValue) if (auditStatus.HasValue)
{ {
query = query.Where(x => x.AuditStatus == auditStatus.Value); query = query.Where(x => x.AuditStatus == auditStatus.Value);
} }
// 6. (空行后) 可选过滤:经营状态 // 7. (空行后) 可选过滤:经营状态
if (businessStatus.HasValue) if (businessStatus.HasValue)
{ {
query = query.Where(x => x.BusinessStatus == businessStatus.Value); query = query.Where(x => x.BusinessStatus == businessStatus.Value);
} }
// 7. (空行后) 可选过滤:主体类型 // 8. (空行后) 可选过滤:主体类型
if (ownershipType.HasValue) if (ownershipType.HasValue)
{ {
query = query.Where(x => x.OwnershipType == ownershipType.Value); query = query.Where(x => x.OwnershipType == ownershipType.Value);
} }
// 8. (空行后) 可选过滤:关键词 // 9. (空行后) 可选过滤:关键词
if (!string.IsNullOrWhiteSpace(keyword)) if (!string.IsNullOrWhiteSpace(keyword))
{ {
var trimmed = keyword.Trim(); var trimmed = keyword.Trim();
@@ -115,7 +130,7 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
(x.Phone != null && x.Phone.Contains(trimmed))); (x.Phone != null && x.Phone.Contains(trimmed)));
} }
// 9. (空行后) 查询并返回结果 // 10. (空行后) 查询并返回结果
var stores = await query var stores = await query
.OrderBy(x => x.Name) .OrderBy(x => x.Name)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -142,6 +157,7 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
var coordinates = await context.Stores var coordinates = await context.Stores
.AsNoTracking() .AsNoTracking()
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId) .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
.Where(x => x.DeletedAt == null)
.Where(x => x.Longitude.HasValue && x.Latitude.HasValue) .Where(x => x.Longitude.HasValue && x.Latitude.HasValue)
.Select(x => new { Longitude = x.Longitude!.Value, Latitude = x.Latitude!.Value }) .Select(x => new { Longitude = x.Longitude!.Value, Latitude = x.Latitude!.Value })
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -176,7 +192,10 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
query = query.Where(x => x.TenantId == tenantId.Value); query = query.Where(x => x.TenantId == tenantId.Value);
} }
// 2. (空行后) 分组统计门店数量 // 2. (空行后) 排除已删除门店
query = query.Where(x => x.DeletedAt == null);
// 3. (空行后) 分组统计门店数量
return await query return await query
.Where(x => merchantIds.Contains(x.MerchantId)) .Where(x => merchantIds.Contains(x.MerchantId))
.GroupBy(x => x.MerchantId) .GroupBy(x => x.MerchantId)