ef15fd6247
Full backend implementation: - Multi-tenant cafe/restaurant management (menus, orders, tables, staff) - POS order flow with ZarinPal and Snappfood payment integration - OTP authentication via Kavenegar SMS - QR digital menu with public discover/finder endpoints - Customer loyalty, coupons, CRM - PostgreSQL via EF Core, Redis for caching/sessions - Background jobs, webhook handlers - Full migration history Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
203 lines
7.4 KiB
C#
203 lines
7.4 KiB
C#
using Meezi.Core.Entities;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Meezi.Infrastructure.Data;
|
|
|
|
public static class DemoMenuSeeder
|
|
{
|
|
public static async Task EnsureMenuAsync(AppDbContext db, string cafeId, string taxId, ILogger logger)
|
|
{
|
|
if (!await db.Taxes.AnyAsync(t => t.Id == taxId && t.CafeId == cafeId))
|
|
{
|
|
db.Taxes.Add(new Tax
|
|
{
|
|
Id = taxId,
|
|
CafeId = cafeId,
|
|
Name = "مالیات",
|
|
Rate = 9,
|
|
IsDefault = true,
|
|
IsRequired = true,
|
|
IsCompound = false
|
|
});
|
|
}
|
|
|
|
var existingCategoryIds = await db.MenuCategories
|
|
.Where(c => c.CafeId == cafeId)
|
|
.ToDictionaryAsync(c => c.Id, StringComparer.Ordinal);
|
|
|
|
var categoriesAdded = 0;
|
|
foreach (var cat in DemoMenuCatalog.Categories)
|
|
{
|
|
if (existingCategoryIds.TryGetValue(cat.Id, out var row))
|
|
{
|
|
if (string.IsNullOrWhiteSpace(row.Icon) && !string.IsNullOrWhiteSpace(cat.Icon))
|
|
row.Icon = cat.Icon;
|
|
if (string.IsNullOrWhiteSpace(row.IconPresetId) && !string.IsNullOrWhiteSpace(cat.IconPresetId))
|
|
row.IconPresetId = cat.IconPresetId;
|
|
if (string.IsNullOrWhiteSpace(row.IconStyle) && !string.IsNullOrWhiteSpace(cat.IconStyle))
|
|
row.IconStyle = cat.IconStyle;
|
|
if (string.IsNullOrWhiteSpace(row.NameEn) && !string.IsNullOrWhiteSpace(cat.NameEn))
|
|
row.NameEn = cat.NameEn;
|
|
if (string.IsNullOrWhiteSpace(row.NameAr) && cat.NameAr is not null)
|
|
row.NameAr = cat.NameAr;
|
|
continue;
|
|
}
|
|
|
|
db.MenuCategories.Add(new MenuCategory
|
|
{
|
|
Id = cat.Id,
|
|
CafeId = cafeId,
|
|
Name = cat.Name,
|
|
NameEn = cat.NameEn,
|
|
NameAr = cat.NameAr,
|
|
Icon = cat.Icon,
|
|
IconPresetId = cat.IconPresetId,
|
|
IconStyle = cat.IconStyle,
|
|
SortOrder = cat.SortOrder,
|
|
TaxId = taxId,
|
|
IsActive = true
|
|
});
|
|
categoriesAdded++;
|
|
}
|
|
|
|
var existingItemIds = await db.MenuItems
|
|
.Where(i => i.CafeId == cafeId)
|
|
.Select(i => i.Id)
|
|
.ToListAsync();
|
|
|
|
var itemsAdded = 0;
|
|
foreach (var item in DemoMenuCatalog.Items)
|
|
{
|
|
if (existingItemIds.Contains(item.Id))
|
|
continue;
|
|
|
|
db.MenuItems.Add(new MenuItem
|
|
{
|
|
Id = item.Id,
|
|
CafeId = cafeId,
|
|
CategoryId = item.CategoryId,
|
|
Name = item.Name,
|
|
NameEn = item.NameEn,
|
|
NameAr = item.NameAr,
|
|
Description = item.Description,
|
|
Price = item.PriceToman,
|
|
DiscountPercent = item.DiscountPercent,
|
|
ImageUrl = DemoMenuCatalog.ResolveItemImageUrl(item),
|
|
IsAvailable = true
|
|
});
|
|
itemsAdded++;
|
|
}
|
|
|
|
if (categoriesAdded > 0 || itemsAdded > 0)
|
|
await db.SaveChangesAsync();
|
|
|
|
if (categoriesAdded > 0 || itemsAdded > 0)
|
|
{
|
|
logger.LogInformation(
|
|
"Demo menu seed: +{Categories} categories, +{Items} items (catalog total {Total}) for cafe {CafeId}",
|
|
categoriesAdded,
|
|
itemsAdded,
|
|
DemoMenuCatalog.Items.Count,
|
|
cafeId);
|
|
}
|
|
|
|
await EnsureMenuImagesAsync(db, cafeId, logger);
|
|
await EnsureMenuTranslationsAsync(db, cafeId, logger);
|
|
}
|
|
|
|
/// <summary>Upserts ImageUrl from catalog/manifest/Food-101 fallbacks.</summary>
|
|
public static async Task EnsureMenuImagesAsync(AppDbContext db, string cafeId, ILogger logger)
|
|
{
|
|
var catalogById = DemoMenuCatalog.Items.ToDictionary(i => i.Id, StringComparer.Ordinal);
|
|
var items = await db.MenuItems
|
|
.Include(i => i.Category)
|
|
.Where(i => i.CafeId == cafeId)
|
|
.ToListAsync();
|
|
|
|
var updated = 0;
|
|
foreach (var row in items)
|
|
{
|
|
var resolved = catalogById.TryGetValue(row.Id, out var seed)
|
|
? DemoMenuCatalog.ResolveItemImageUrl(seed)
|
|
: MenuItemImageDefaults.ResolveImageUrl(row.Id, row.CategoryId, row.Category?.Name);
|
|
|
|
if (string.IsNullOrWhiteSpace(resolved)) continue;
|
|
|
|
var inCatalog = catalogById.ContainsKey(row.Id);
|
|
var shouldUpdate = MenuItemImageDefaults.NeedsImageRepair(row.ImageUrl) || inCatalog;
|
|
if (!shouldUpdate || string.Equals(row.ImageUrl, resolved, StringComparison.Ordinal))
|
|
continue;
|
|
|
|
row.ImageUrl = resolved;
|
|
updated++;
|
|
}
|
|
|
|
if (updated > 0)
|
|
{
|
|
await db.SaveChangesAsync();
|
|
logger.LogInformation("Menu image upsert: {Count} items updated for cafe {CafeId}", updated, cafeId);
|
|
}
|
|
}
|
|
|
|
/// <summary>Upserts NameEn/NameAr from catalog for demo menu rows.</summary>
|
|
public static async Task EnsureMenuTranslationsAsync(AppDbContext db, string cafeId, ILogger logger)
|
|
{
|
|
var catalogItems = DemoMenuCatalog.Items.ToDictionary(i => i.Id, StringComparer.Ordinal);
|
|
var catalogCats = DemoMenuCatalog.Categories.ToDictionary(c => c.Id, StringComparer.Ordinal);
|
|
|
|
var items = await db.MenuItems.Where(i => i.CafeId == cafeId && catalogItems.Keys.Contains(i.Id)).ToListAsync();
|
|
var itemUpdated = 0;
|
|
foreach (var row in items)
|
|
{
|
|
if (!catalogItems.TryGetValue(row.Id, out var seed)) continue;
|
|
var changed = false;
|
|
if (string.IsNullOrWhiteSpace(row.NameEn) && !string.IsNullOrWhiteSpace(seed.NameEn))
|
|
{
|
|
row.NameEn = seed.NameEn;
|
|
changed = true;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(row.NameAr) && seed.NameAr is not null)
|
|
{
|
|
row.NameAr = seed.NameAr;
|
|
changed = true;
|
|
}
|
|
if (changed) itemUpdated++;
|
|
}
|
|
|
|
var categories = await db.MenuCategories.Where(c => c.CafeId == cafeId && catalogCats.Keys.Contains(c.Id)).ToListAsync();
|
|
var catUpdated = 0;
|
|
foreach (var row in categories)
|
|
{
|
|
if (!catalogCats.TryGetValue(row.Id, out var seed)) continue;
|
|
var changed = false;
|
|
if (string.IsNullOrWhiteSpace(row.NameEn) && !string.IsNullOrWhiteSpace(seed.NameEn))
|
|
{
|
|
row.NameEn = seed.NameEn;
|
|
changed = true;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(row.NameAr) && seed.NameAr is not null)
|
|
{
|
|
row.NameAr = seed.NameAr;
|
|
changed = true;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(row.Icon) && !string.IsNullOrWhiteSpace(seed.Icon))
|
|
{
|
|
row.Icon = seed.Icon;
|
|
changed = true;
|
|
}
|
|
if (changed) catUpdated++;
|
|
}
|
|
|
|
if (itemUpdated > 0 || catUpdated > 0)
|
|
{
|
|
await db.SaveChangesAsync();
|
|
logger.LogInformation(
|
|
"Menu translation upsert: {Items} items, {Cats} categories for cafe {CafeId}",
|
|
itemUpdated,
|
|
catUpdated,
|
|
cafeId);
|
|
}
|
|
}
|
|
}
|