16 KiB
16 KiB
🏗️ BackOffice — مرجع معماری و الگوها
تاریخ: ۱۴۰۴/۱۱/۲۴ (February 13, 2026)
پروژه: BackOffice Admin Panel (Blazor WebAssembly)
۱. معماری کلی
┌─────────────────────────────────────────────────┐
│ BackOffice │
│ (Blazor WebAssembly) │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ MudBlazor│ │ Mapster │ │ DateTimeCvt │ │
│ │ v8 │ │ (mapping)│ │ (تاریخ شمسی) │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
│ │ │ │ │
│ ┌─────────────────────────────────────────┐ │
│ │ Pages / Components / Shared │ │
│ │ BasePageComponent, Hub Pages, Dialogs │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ gRPC Clients │ │ HTTP REST Services│ │
│ │ (Protobuf) │ │ (DiscountShop) │ │
│ └──────┬───────┘ └────────┬─────────┘ │
└─────────┼──────────────────────┼─────────────────┘
│ │
▼ ▼
┌──────────────────────────────────────────┐
│ CMS Microservice │
│ (ASP.NET Core + gRPC) │
│ Domain → Application (CQRS) → Infra │
└──────────────────────────────────────────┘
۲. Technology Stack
| لایه | تکنولوژی | نسخه |
|---|---|---|
| Frontend Framework | Blazor WebAssembly | .NET 9 |
| UI Library | MudBlazor | v8 |
| Backend Communication (عادی) | gRPC / Protobuf | — |
| Backend Communication (تخفیفی) | HTTP REST | — |
| Object Mapping | Mapster | — |
| تاریخ شمسی | DateTimeConverterCL | — |
| Client State | Blazored.LocalStorage | — |
| Auth | JWT Role-based | Administrator, Admin, Author |
| Permission | IAuthorizationService.HasPermissionAsync | 18 permission |
۳. ساختار پوشهها
BackOffice/src/BackOffice/
├── Common/
│ ├── BaseComponents/ ← کامپوننتهای پایه (BasePageComponent, DateRangePicker, Image)
│ ├── Utilities/ ← RouteConstance, Extensions, Helpers
│ └── ...
├── Pages/
│ ├── Category/ ← دستهبندی فروشگاه عادی
│ ├── Products/ ← محصولات فروشگاه عادی
│ ├── UserOrder/ ← سفارشات + گزارش فروش (Hub)
│ ├── DiscountShop/ ← فروشگاه تخفیفی (محصولات + دستهبندی + سفارشات)
│ │ └── Components/ ← دیالوگها و کامپوننتهای اختصاصی
│ ├── Inventory/ ← انبارداری (4 صفحه)
│ ├── Package/ ← پکیجها
│ ├── Commission/ ← کمیسیون (5 صفحه)
│ ├── Network/ ← شبکه (4 صفحه)
│ ├── Club/ ← باشگاه مشتریان (Hub: اعضا + آمار + فیچرها)
│ ├── Blog/ ← بلاگ (Hub: پست + دستهبندی + تگ)
│ ├── Content/ ← صفحات سایت
│ ├── Wallet/ ← کیفپول (تبها: لیست + تاریخچه)
│ ├── Contract/ ← قراردادها
│ ├── SystemManagement/ ← سیستم (Hub: تنظیمات + Worker + Health)
│ └── ...
├── Services/
│ ├── DiscountProduct/ ← IDiscountProductService + implementation
│ ├── DiscountCategory/ ← IDiscountCategoryService + implementation
│ ├── DiscountOrder/ ← IDiscountOrderService + implementation
│ └── Authorization/ ← IAuthorizationService
├── Shared/
│ ├── MainLayout.razor ← لایوت اصلی (AppBar + NavMenu + MudContainer)
│ ├── NavMenu.razor ← منوی ناوبری
│ ├── GlobalSearch.razor ← جستجوی سراسری
│ └── AppBreadcrumb.razor ← Breadcrumb فارسی
└── wwwroot/
├── js/main.js ← jsSaveAsFile (Excel export)
└── appsettings.json ← تنظیمات endpoints
۴. الگوهای اصلی
۴.۱ BasePageComponent — پترن صفحات لیست
هر صفحه لیست از BasePageComponent استفاده میکند:
┌──────────────────────────────────────┐
│ BasePageComponent │
│ ┌────────────────────────────────┐ │
│ │ 📋 Filter Panel (collapsible) │ │
│ │ [فیلد ۱] [فیلد ۲] [فیلد ۳] │ │
│ │ [پاک کردن فیلتر] [جستجو] │ │
│ └────────────────────────────────┘ │
│ ┌────────────────────────────────┐ │
│ │ 📊 Content (DataGrid) │ │
│ │ ToolBar: [عنوان] [Excel] [+] │ │
│ │ Columns: ... │ │
│ │ Pager: 20/50/100 │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘
فایل: Common/BaseComponents/BasePageComponent.razor
پراپرتیها:
RenderFragment Filters— محتوای فیلترRenderFragment Content— محتوای اصلیEventCallback OnSubmitClick— کلیک جستجوEventCallback OnClearFilterClick— کلیک پاک کردنbool IsFiltered— آیا فیلتر فعال است (نشاندهنده badge «فعال»)
۴.۲ Hub Pages — پترن ادغام صفحات
صفحات مرتبط در یک Hub با MudTabs ادغام میشوند:
| Hub | Routeها | تبها |
|---|---|---|
OrdersHub |
/OrdersPage/, /OrdersSalesReportsPage/ |
سفارشات + گزارش فروش |
DiscountShopHub |
/discount-shop, /discount-orders, /discount-sales-reports |
سفارشات + گزارش فروش |
ClubHub |
/club, /club/members, /club/statistics |
اعضا + آمار |
BlogHub |
/blog, /blog/posts, /blog/categories, /tags |
پست + دستهبندی + تگ |
SystemHub |
/system, /system/configuration, /system/worker-control, /system/health |
تنظیمات + Worker + Health |
۴.۳ Code-Behind — پترن جداسازی markup/logic
MyPage.razor → فقط HTML/Razor markup
MyPage.razor.cs → partial class + [Inject] + methods
قوانین:
- فایلهایی که سرویس inject دارند باید code-behind داشته باشند (محدودیت Razor source generator)
- سرویسهای global (
_Imports.razor) نباید دوباره[Inject]شوند namespaceباید با مسیر فایل match کند
سرویسهای Global (از _Imports.razor):
| سرویس | نام متغیر | توضیح |
|---|---|---|
IDialogService |
DialogService |
دیالوگ MudBlazor |
ISnackbar |
Snackbar |
نوتیفیکیشن MudBlazor |
IJSRuntime |
jsRuntime |
⚠️ حرف کوچک j |
NavigationManager |
Navigation |
ناوبری |
ILocalStorageService |
LocalStorageService |
ذخیره محلی |
AuthenticationStateProvider |
AuthenticationStateProvider |
احراز هویت |
۴.۴ Excel Export — پترن خروجی CSV
private async Task ExportToExcel()
{
var sb = new StringBuilder();
sb.AppendLine("ستون ۱,ستون ۲,ستون ۳"); // هدر فارسی
foreach (var item in items)
{
sb.AppendLine($"{EscapeCsv(item.Col1)},{item.Col2},{item.Col3}");
}
var bytes = Encoding.UTF8.GetPreamble() // UTF-8 BOM
.Concat(Encoding.UTF8.GetBytes(sb.ToString())).ToArray();
var base64 = Convert.ToBase64String(bytes);
await jsRuntime.InvokeVoidAsync("jsSaveAsFile", "filename.csv", base64);
}
private string EscapeCsv(string? value)
{
if (string.IsNullOrEmpty(value)) return "";
if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
return $"\"{value.Replace("\"", "\"\"")}\"";
return value;
}
صفحات دارای Excel: Products, UserOrders, ClubMembers, WithdrawalRequests, WeeklyReports, StockMovements, Users, DiscountOrders, ManualPayments, Inventory, DiscountProducts
۴.۵ Server-Side DataGrid — پترن بارگذاری صفحهای
<MudDataGrid T="MyDto"
ServerData="LoadServerData"
Height="calc(100vh - 240px)"
FixedHeader="true"
Hover="true" Dense="true">
private async Task<GridData<MyDto>> LoadServerData(GridState<MyDto> state)
{
var filter = new MyFilter
{
PageNumber = state.Page + 1, // MudDataGrid is 0-based
PageSize = state.PageSize
};
var (items, totalCount, _) = await MyService.GetAsync(filter);
return new GridData<MyDto> { Items = items, TotalItems = totalCount };
}
۴.۶ Permission System
NavMenu از IAuthorizationService.HasPermissionAsync() برای نمایش/مخفی کردن آیتمها استفاده میکند:
| Permission | صفحه(ها) |
|---|---|
dashboard.view |
داشبورد |
packages.manage |
پکیجها |
products.manage |
محصولات + دستهبندی + ویرایش دستهجمعی |
orders.view |
سفارشات |
inventory.manage |
انبارداری (4 صفحه) |
discountshop.manage |
فروشگاه تخفیفی |
users.view |
کاربران |
roles.manage |
نقشها |
manualpayments.create |
پرداخت دستی |
blog.manage |
بلاگ |
sitepages.manage |
صفحات سایت |
publicmessages.view |
پیامهای عمومی |
settings.manage_configuration |
تنظیمات سیستم |
۵. مسیرهای (Routing)
مسیرهای ثابت (RouteConstance.cs)
/ → Dashboard
/PackagePage/ → Packages
/ProductsPage/ → Products
/CategoryPage/ → Categories
/OrdersPage/ → Orders Hub
/OrdersSalesReportsPage/ → Orders Sales Reports
/InventoryPage/ → Inventory
/InventoryLowStockPage/ → Low Stock
/InventoryWarehousesPage/ → Warehouses
/InventoryMovementsPage/ → Stock Movements
/UserPage/ → Users
/RolePage/ → Roles
/ProductsBulkEditPage/ → Bulk Edit
/ProductCategoriesPage/ → Product-Category DragDrop
/CategoryProductsPage/ → Category-Product DragDrop
مسیرهای hardcode (فروشگاه تخفیفی + سایر)
/discount-products → Discount Products
/discount-categories → Discount Categories
/discount-shop → Discount Orders Hub
/discount-orders → Discount Orders
/discount-sales-reports → Discount Sales Reports
/commission/* → Commission pages
/network/* → Network pages
/club/* → Club pages
/blog/* → Blog pages
/wallets → Wallets
/contracts → Contracts
/payment/manual-payments → Manual Payments
/system/* → System pages
/settings → Settings
/content/pages → Content Pages
/public-messages → Public Messages
۶. ارتباط فروشگاه عادی vs تخفیفی
| جنبه | فروشگاه عادی | فروشگاه تخفیفی |
|---|---|---|
| سرویس محصولات | gRPC ProductsContractClient |
HTTP IDiscountProductService |
| سرویس دستهبندی | gRPC CategoryContractClient |
HTTP IDiscountCategoryService |
| سرویس سفارشات | gRPC UserOrderContractClient |
HTTP IDiscountOrderService |
| Entity بکند | Product |
DiscountProduct |
| پرداخت | فقط درگاه | ترکیبی (کیف تخفیفی + درگاه) |
| فیلد اختصاصی | — | MaxDiscountPercent |
| UI Pattern | BasePageComponent | BasePageComponent (یکسان) |
| ستونها | یکسان | یکسان + ستون تخفیف |
۷. نقشه NavMenu
داشبورد
─────────────────────
کمیسیون و شبکه
├── کمیسیون (NavGroup)
│ ├── داشبورد کمیسیون
│ ├── گزارشهای هفتگی
│ ├── پرداخت کاربران
│ ├── درخواستهای برداشت [Badge]
│ └── گزارش برداشتها
├── شبکه (NavGroup)
│ ├── درخت شبکه
│ ├── گزارش موجودیها
│ └── آمار شبکه
└── باشگاه مشتریان (NavGroup)
├── اعضا و آمار
└── فیچرهای باشگاه
─────────────────────
فروشگاه [AuthorizeView: Administrator]
├── پکیجها
├── فروشگاه عادی (NavGroup)
│ ├── محصولات
│ ├── دستهبندیها
│ └── سفارشات و گزارش
├── انبارداری (NavGroup)
│ ├── موجودی انبار
│ ├── محصولات کمموجود
│ ├── مدیریت انبارها
│ └── تاریخچه تغییرات
└── فروشگاه تخفیفی (NavGroup)
├── محصولات
├── دستهبندیها
└── سفارشات و گزارش
─────────────────────
مدیریت [AuthorizeView: Administrator]
├── کاربران
├── نقشها
├── پرداخت دستی
├── کیفپول
└── قراردادها
─────────────────────
مدیریت محتوا
├── بلاگ
├── صفحات سایت
└── پیامهای عمومی
─────────────────────
سیستم [AuthorizeView: Administrator]
├── مدیریت سیستم
└── نسخه اپلیکیشنها
─────────────────────
تنظیمات