consolidate: move all project-level docs to totalDoc

- BackOffice/docs → totalDoc/backoffice/ (AUDIT + CHANGELOG)
- FrontOffice/docs → totalDoc/frontoffice/ (CHANGELOG + UI-UNIFICATION-PLAN)
- CMS/README.md → totalDoc/cms/CMS-README.md
- DataMigration/README.md → totalDoc/migration/DATAMIGRATION-README.md
- deployment/README.md → totalDoc/deployment/DEPLOYMENT-README.md
- Updated INDEX.md with new paths and sections
This commit is contained in:
masoodafar-web
2026-02-18 21:43:59 +03:30
parent 0aa0141cec
commit d7c32dab2a
8 changed files with 3776 additions and 7 deletions
+32 -7
View File
@@ -10,7 +10,7 @@
| می‌خواهم بدانم... | مستند |
|-------------------|-------|
| **کل تغییرات BackOffice چه بوده؟** | [`BackOffice/docs/BACKOFFICE-CHANGELOG.md`](../BackOffice/docs/BACKOFFICE-CHANGELOG.md) |
| **کل تغییرات BackOffice چه بوده؟** | [`backoffice/BACKOFFICE-CHANGELOG.md`](backoffice/BACKOFFICE-CHANGELOG.md) |
| **ساختار و معماری BackOffice چیست؟** | [`ui-modernization/BACKOFFICE-ARCHITECTURE.md`](ui-modernization/BACKOFFICE-ARCHITECTURE.md) |
| **یکسان‌سازی فروشگاه‌ها چه بوده؟** | [`ui-modernization/BACKOFFICE-STORE-UNIFICATION.md`](ui-modernization/BACKOFFICE-STORE-UNIFICATION.md) |
| **وضعیت فروشگاه تخفیفی؟** | [`business/DISCOUNT-STORE-STATUS.md`](business/DISCOUNT-STORE-STATUS.md) |
@@ -30,7 +30,12 @@
| **سرویس انقضای سفارش؟** | [`cms/payment-gateway.md`](cms/payment-gateway.md) (بخش ۱۱) |
| **بهبود سیستم موجودی؟** | [`cms/INVENTORY-IMPROVEMENTS.md`](cms/INVENTORY-IMPROVEMENTS.md) |
| **تصاویر مربعی محصولات؟** | [`ui-modernization/PRODUCT-IMAGES-SQUARE.md`](ui-modernization/PRODUCT-IMAGES-SQUARE.md) |
| **Audit report کامل BackOffice؟** | [`BackOffice/docs/BACKOFFICE-AUDIT.md`](../BackOffice/docs/BACKOFFICE-AUDIT.md) |
| **Audit report کامل BackOffice؟** | [`backoffice/BACKOFFICE-AUDIT.md`](backoffice/BACKOFFICE-AUDIT.md) |
| **تغییرات FrontOffice؟** | [`frontoffice/CHANGELOG.md`](frontoffice/CHANGELOG.md) |
| **طرح یکسان‌سازی UI فرانت؟** | [`frontoffice/UI-UNIFICATION-PLAN.md`](frontoffice/UI-UNIFICATION-PLAN.md) |
| **README پروژه CMS؟** | [`cms/CMS-README.md`](cms/CMS-README.md) |
| **README دیپلوی؟** | [`deployment/DEPLOYMENT-README.md`](deployment/DEPLOYMENT-README.md) |
| **README مهاجرت داده؟** | [`migration/DATAMIGRATION-README.md`](migration/DATAMIGRATION-README.md) |
---
@@ -47,10 +52,11 @@
| [discount-shop-business.md](business/discount-shop-business.md) | فروشگاه تخفیفی: پرداخت ترکیبی، درصد تخفیف، entity design |
| [manual-payment-system.md](business/manual-payment-system.md) | پرداخت دستی: کارت به کارت، تأیید ادمین، آپلود FMS |
## 📂 cms/ — مستندات فنی CMS (۱۵ فایل)
## 📂 cms/ — مستندات فنی CMS (۱۹ فایل)
| فایل | توضیح |
|------|-------|
| [CMS-README.md](cms/CMS-README.md) | 📘 README اصلی پروژه CMS |
| [BFF-REMOVAL-PLAN.md](cms/BFF-REMOVAL-PLAN.md) | ✅ پلن حذف BFF — Permission Interceptor + تغییرات config |
| [REMAINING-TASKS.md](cms/REMAINING-TASKS.md) | وضعیت ۴۹/۴۹ متد — همه انجام شده ✅ |
| [FRONTOFFICE-CMS-API-COMPATIBILITY.md](cms/FRONTOFFICE-CMS-API-COMPATIBILITY.md) | ماتریس سازگاری API بین FrontOffice و CMS |
@@ -70,10 +76,11 @@
| [FILE-MANAGEMENT-ARCHITECTURE.md](cms/FILE-MANAGEMENT-ARCHITECTURE.md) | 🆕 معماری جامع مدیریت فایل: IFileManager, LocalFileManager, ImagePathResolverInterceptor, UploadsController (HTTP سرو عمومی + FMS Fallback), ذخیره دیسکی |
| [REGISTRATION-FLOW-FIXES.md](cms/REGISTRATION-FLOW-FIXES.md) | 🆕 فیکس فلوی ثبت‌نام: ایجاد کاربر جدید در VerifyOtpToken، رفع lookup موبایل AcceptContract، رفع sync IsCompleteRegister |
## 📂 deployment/ — مستندات استقرار (۴ فایل)
## 📂 deployment/ — مستندات استقرار (۶ فایل)
| فایل | توضیح |
|------|-------|
| [DEPLOYMENT-README.md](deployment/DEPLOYMENT-README.md) | 📘 README اصلی پوشه deployment |
| [OFFLINE-DEPLOYMENT-GUIDE.md](deployment/OFFLINE-DEPLOYMENT-GUIDE.md) | راهنمای جامع استقرار آفلاین + تنظیمات Nexus |
| [CICD-PIPELINE-GUIDE.md](deployment/CICD-PIPELINE-GUIDE.md) | 🔄 راهنمای CI/CD Pipeline: معماری DinD، فیکس‌های dockerd، Runner ConfigMap، عیب‌یابی، SERVER_PASSWORD، باگ cross-deploy، قالب workflow Production |
| [INFRASTRUCTURE-GUIDE.md](deployment/INFRASTRUCTURE-GUIDE.md) | 🔄 مشخصات سرور Staging + Production، DB credentials دوگانه، Gitea، Kestrel protocol، Ingress annotations، Proto v0.0.179 |
@@ -89,10 +96,11 @@
| [BACKOFFICE-STORE-UNIFICATION.md](ui-modernization/BACKOFFICE-STORE-UNIFICATION.md) | 🆕 یکسان‌سازی فروشگاه عادی و تخفیفی: NavMenu restructure، حذف آمار سفارشات، رفع PaymentDate، بازنویسی ۴ صفحه |
| [PRODUCT-IMAGES-SQUARE.md](ui-modernization/PRODUCT-IMAGES-SQUARE.md) | 🆕 تصاویر مربعی محصولات: aspect-ratio 1:1 در ۹ فایل، هر دو فروشگاه، همه سایزها |
## 📂 migration/ — مستندات مهاجرت BFF→CMS (۶ فایل)
## 📂 migration/ — مستندات مهاجرت BFF→CMS (۷ فایل)
| فایل | توضیح |
|------|-------|
| [DATAMIGRATION-README.md](migration/DATAMIGRATION-README.md) | 📘 README پروژه DataMigration |
| [GATEWAY-REMOVAL-MIGRATION-PLAN.md](migration/GATEWAY-REMOVAL-MIGRATION-PLAN.md) | پلن استراتژیک حذف هر دو Gateway (BFF) |
| [FRONTOFFICE-TO-CMS-MIGRATION.md](migration/FRONTOFFICE-TO-CMS-MIGRATION.md) | مهاجرت کامل FrontOffice: proto changes، field aliasing + لاگ تغییرات |
| [MIGRATION-PROGRESS.md](migration/MIGRATION-PROGRESS.md) | لاگ پیشرفت مهاجرت: نسخه‌های proto، خطاها، وضعیت سرویس‌ها |
@@ -102,7 +110,23 @@
---
## 📊 آمار تجمیع
## backoffice/ — مستندات پنل مدیریت (۲ فایل)
| فایل | توضیح |
|------|-------|
| [BACKOFFICE-AUDIT.md](backoffice/BACKOFFICE-AUDIT.md) | 🔍 گزارش آدیت عمیق BackOffice: ۵۸ آیتم، ۹۸٪ تکمیل، ۶ فاز |
| [BACKOFFICE-CHANGELOG.md](backoffice/BACKOFFICE-CHANGELOG.md) | 📝 چنج‌لاگ BackOffice: فاز ۱ تا ۱۷ |
## 📂 frontoffice/ — مستندات فرانت‌آفیس (۲ فایل)
| فایل | توضیح |
|------|-------|
| [CHANGELOG.md](frontoffice/CHANGELOG.md) | 📝 چنج‌لاگ FrontOffice |
| [UI-UNIFICATION-PLAN.md](frontoffice/UI-UNIFICATION-PLAN.md) | 🎨 طرح یکسان‌سازی UI فروشگاه‌ها |
---
## 📊 آمار تجمیع
| مرحله | تعداد فایل | حذف شده |
|-------|-----------|---------|
@@ -117,4 +141,5 @@
| session File Mgmt + Content (+1 doc) | **36** | — |
| session Registration Flow Fix (+1 doc) | **37** | — |
| session Inventory + Images + Docs (+2 docs) | **39** | — |
| **نهایی** | **39 + INDEX** | **۱۹۱ فایل حذف/ادغام** |
| consolidate project-level docs (+7 files, 2 folders) | **46** | — |
| **نهایی** | **46 + INDEX** | **۱۹۱ فایل حذف/ادغام** |
File diff suppressed because it is too large Load Diff
+850
View File
@@ -0,0 +1,850 @@
# 📝 BackOffice — Changelog
**آخرین بروزرسانی:** اسفند ۱۴۰۴ (February 18, 2026)
---
## 🏷 فاز ۱۷ — بهبود سیستم موجودی + اتوکامپلیت تخفیفی (اسفند ۱۴۰۴)
> **هدف:** UX هوشمند ورود کالا با autocomplete اختصاصی برای هر نوع فروشگاه
### ۱۷.۱ — کامپوننت DiscountProductsAutoComplete ✅
- **فایل‌ها:** `Pages/AutoComplete/DiscountProductsAutoComplete.razor(.cs)`
- **الگو:** مشابه `ProductsAutoComplete` موجود
- **gRPC Client:** `DiscountProductContract.DiscountProductContractClient`
- **دِبانس:** ۷۰۰ms، حداکثر ۹ نتیجه
- **نکته:** `SearchQuery` از نوع `string` (نه `StringValue`)
### ۱۷.۲ — UX هوشمند AddStockDialog ✅
- **فایل:** `Pages/Inventory/Components/AddStockDialog.razor`
- **تغییر:** فیلد عددی خام شناسه محصول → autocomplete هوشمند
- **رفتار:**
- انتخاب «فروشگاه عادی» → نمایش `ProductsAutoComplete`
- انتخاب «فروشگاه اعتباری» → نمایش `DiscountProductsAutoComplete`
- ولیدیشن قبل از submit
### ۱۷.۳ — Merge به production ✅
- **برنچ:** `kub-stage``production`
- **ریموت:** `kub-stage` (Gitea)
---
## 🏷 فاز ۱۶ — ساده‌سازی صفحات سایت + ادیتورهای Shopify-style (اسفند ۱۴۰۴)
> **هدف:** مدیریت typed صفحات سایت با فرم‌های اختصاصی
### ۱۶.۱ — صفحه مدیریت PageSettings ✅
- **فایل‌ها:** `Pages/Content/PageSettingsManagementPage.razor(.cs)`
- **قابلیت‌ها:** لیست ۴ صفحه ثابت (landing/about/contact/licenses)، ویرایش تنظیمات، مدیریت تصاویر
### ۱۶.۲ — ادیتورهای اختصاصی Shopify-style ✅
| فایل | صفحه |
|------|------|
| `AboutSettingsEditor.razor` | درباره ما: ارزش‌ها + اعضای تیم |
| `ContactSettingsEditor.razor` | تماس: آدرس/تلفن/ایمیل/شبکه‌های اجتماعی |
| `LandingSettingsEditor.razor` | لندینگ: Hero + Steps + Features + Stats + FAQ + Testimonials |
| `LicensesSettingsEditor.razor` | مجوزها: JSON array |
### ۱۶.۳ — سرویس gRPC و دیالوگ‌ها ✅
- `ISitePageSettingsService` + `SitePageSettingsService`
- `PageSettingsEditDialog` + `PageImagesDialog` + `PageImageEditDialog`
- `PageSettingsModels.cs` — مدل‌های typed برای JSON
### ۱۶.۴ — Proto v0.0.180 ✅
- **تغییر:** `Foursat.CMSMicroservice.Protobuf``0.0.180`
---
## 🏷 فاز ۱۵ — Production Deploy + فیکس CI/CD Cross-Deploy (اسفند ۱۴۰۴)
> **هدف:** دیپلوی به سرور Production و رفع باگ cross-deploy
### ۱۵.۱ — Merge به برنچ production ✅
- **کامیت:** `4454b8d` — merge `kub-stage``production`
- **ریموت:** `kub-stage` (Gitea)
### ۱۵.۲ — فیکس باگ Cross-Deploy ✅
- **مشکل:** Gitea Act Runner همه workflowهای `.gitea/workflows/` را اجرا می‌کرد — بدون توجه به `branches:` filter
- **عارضه:** push به `production` باعث دیپلوی همزمان به staging و production می‌شد
- **رفع:** حذف فایل‌های workflow staging از برنچ production
- **فایل حذف شده:** `.gitea/workflows/deploy.yml` (staging) از برنچ production
### ۱۵.۳ — بروزرسانی Proto به v0.0.179 ✅
- **تغییر:** `Foursat.CMSMicroservice.Protobuf``0.0.179`
- **فایل:** `BackOffice.csproj`
### ۱۵.۴ — فیکس K8S_SERVER در prod-deploy.yml ✅
- **مشکل:** `K8S_SERVER` هنوز `194.5.195.53` (staging) بود
- **رفع:** تغییر به `45.149.79.127` (production)
- **فیکس اضافی:** `kubectl rollout restart``kubectl set image` (تا ایمیج SHA-tagged واقعاً set بشه)
### ۱۵.۵ — ساخت appsettings.Production.json ✅
- **مشکل:** فایل `appsettings.Production.json` وجود نداشت → `GwUrl` fallback به `localhost`
- **رفع:** ساخت `wwwroot/appsettings.Production.json` با `GwUrl=https://cms.kbs1.ir`
### ۱۵.۶ — فیکس nginx image path در Dockerfile (production branch) ✅
- **مشکل:** Dockerfile روی برنچ production از `library/nginx:alpine` استفاده می‌کرد — وجود نداشت
- **ارور:** `manifest for 194.5.195.53:32082/library/nginx:alpine not found`
- **رفع:** تغییر به `194.5.195.53:32082/nginx:alpine`
- **کامیت:** `743403e` (مستقیم روی برنچ production)
---
## 🏷 فاز ۱۴ — فیکس Docker Build + Deployment (بهمن ۱۴۰۴)
> **هدف:** رفع مشکلات Docker build و CI/CD pipeline
### ۱۴.۱ — تغییر به NuGet Package ✅
- **مشکل:** `ProjectReference` به `../../../CMS/src/CMSMicroservice.Protobuf/` خارج از Docker build context بود
- **رفع:** تغییر به `PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.178"`
- **فایل:** `BackOffice.csproj`
### ۱۴.۲ — حذف COPY libs/ از Dockerfile ✅
- **مشکل:** `COPY ["libs/", "libs/"]` فیل می‌شد — `libs/` خارج از build context
- **رفع:** حذف خط — دیگه نیازی به DLLهای BFF نیست
- **فایل:** `Dockerfile`
### ۱۴.۳ — تغییر nginx:alpine به رجیستری لوکال ✅
- **مشکل:** `FROM nginx:alpine` از Docker Hub → TLS handshake timeout
- **رفع:** `FROM 194.5.195.53:32082/nginx:alpine`
- **فایل:** `Dockerfile`
### ۱۴.۴ — اضافه شدن SERVER_PASSWORD secret ✅
- **مشکل:** مرحله Deploy to Kubernetes با `Permission denied` فیل می‌شد
- **رفع:** سکرت `SERVER_PASSWORD` به ریپو Gitea اضافه شد (BackOffice + FrontOffice)
---
## 🏷 فاز ۱۳ — بهبودهای UI بک‌آفیس (بهمن ۱۴۰۴)
> **هدف:** رفع مشکلات بصری، بهبود UX فرم‌ها و ناوبری
### ۱۳.۱ — فیکس کامپوننت Image (پیشوند `/uploads/`) ✅
- **مشکل:** تصاویر آپلود‌شده (بلاگ، صفحات سایت، سکشن‌ها) در بک‌آفیس نمایش داده نمی‌شدند — مسیر `GwUrl + Src` با مسیر `UploadsController` هماهنگ نبود
- **رفع:** اضافه شدن `/uploads/` بین `FileBaseUrl` و مسیر نسبی تصویر
- **فایل:** `BackOffice/Common/BaseComponents/Image.razor.cs`
### ۱۳.۲ — فیکس فیلتر هفته در گزارش‌های هفتگی ✅
- **مشکل:** فیلتر هفته از `MudTextField` (ورودی متنی) استفاده می‌کرد — کاربر باید عدد حدس می‌زد
- **رفع:** جایگزینی با `WeekNumberPicker` — انتخاب هفته از لیست با بایندینگ `WeekDefinitionId`
- **فایل‌ها:**
- `CMS/src/CMSMicroservice.Protobuf/Protos/commission.proto` — تغییر فیلدها از `StringValue` به `Int64Value`
- `CMS/.../CommissionCQ/Queries/GetAllWeeklyPools/GetAllWeeklyPoolsQuery.cs` + `Handler.cs`
- `BackOffice/Pages/Commission/WeeklyReports.razor`
### ۱۳.۳ — فیکس دانلود PDF قراردادها ✅
- **مشکل:** فیلد `SignedPdfFile` محتوای HTML کامل دارد (نه URL فایل PDF) — دکمه دانلود کار نمی‌کرد
- **رفع:** تابع JS جدید `jsHtmlToPdf()` برای باز کردن HTML در پنجره جدید و چاپ/ذخیره به‌عنوان PDF
- **فایل‌ها:**
- `BackOffice/wwwroot/js/main.js` — تابع `jsHtmlToPdf`
- `BackOffice/Pages/Contract/UserContractPage.razor`
- `BackOffice/Pages/Contract/ContractDetailsDialog.razor`
### ۱۳.۴ — یکسان‌سازی ارتفاع ویرایشگر HTML ✅
- **مشکل:** ارتفاع `MudHtmlEditor` در صفحات مختلف متفاوت بود — برخی خیلی کوتاه
- **رفع:** اضافه شدن `MinHeight="300px"` به تمام ۷ ویرایشگر HTML
- **فایل‌ها:** `BlogPostEditDialog`, `SitePageSectionEditDialog`, `DiscountShop/ProductFormDialog`, `Package/CreateDialog`, `Package/UpdateDialog`, `Products/CreateDialog`, `Products/UpdateDialog`
### ۱۳.۵ — تولید خودکار اسلاگ بلاگ ✅
- **مشکل:** کاربر باید اسلاگ URL را دستی وارد می‌کرد — غیرضروری و مستعد خطا
- **رفع:** حذف فیلد اسلاگ از فرم، تولید خودکار `post-{GUID}` در بک‌اند (حداکثر ۲۰ کاراکتر)
- **فایل‌ها:**
- `CMS/.../BlogPostCQ/Commands/CreateBlogPost/CreateBlogPostCommandHandler.cs`
- `CMS/.../BlogPostCQ/Commands/UpdateBlogPost/UpdateBlogPostCommandHandler.cs`
- `BackOffice/Pages/Blog/Components/BlogPostEditDialog.razor`
### ۱۳.۶ — انتقال تگ‌ها به منوی جداگانه ✅
- **مشکل:** تگ‌ها فقط از تب سوم بلاگ قابل دسترسی بودند — ولی تگ‌ها بین محصولات و بلاگ مشترکند
- **رفع:** اضافه شدن مسیر `/tags` به `TagManagementPage`، لینک جدید «مدیریت تگ‌ها» در منوی ناوبری، حذف تب تگ‌ها از `BlogHub`
- **فایل‌ها:**
- `BackOffice/Pages/Tag/TagManagementPage.razor`
- `BackOffice/Shared/NavMenu.razor`
- `BackOffice/Pages/Blog/BlogHub.razor`
### ۱۳.۷ — تولید خودکار کلید سکشن ✅
- **مشکل:** کاربر باید کلید سکشن صفحه سایت را دستی وارد می‌کرد (مثلاً `about-us-section`)
- **رفع:** فیلد اختیاری شد، تولید خودکار `section-{GUID}` (حداکثر ۲۰ کاراکتر) در هندلر CMS
- **فایل‌ها:**
- `CMS/.../SitePageCQ/Commands/CreateSitePageSection/CreateSitePageSectionCommandHandler.cs`
- `CMS/.../SitePageCQ/Commands/CreateSitePageSection/CreateSitePageSectionCommandValidator.cs`
- `BackOffice/Pages/Content/Components/SitePageSectionEditDialog.razor`
---
## 🏷 فاز ۱۲ — فیکس فلوی ثبت‌نام / ورود FrontOffice (بهمن ۱۴۰۴)
> **هدف:** رفع ۳ باگ بحرانی در فلوی ثبت‌نام و ورود FrontOffice
> **مستند کامل:** [`totalDoc/cms/REGISTRATION-FLOW-FIXES.md`](../../totalDoc/cms/REGISTRATION-FLOW-FIXES.md)
### ۱۲.۱ — رفع ایجاد کاربر جدید در VerifyOtpToken 🔴 ✅
- **مشکل:** `UserCQ/VerifyOtpTokenCommandHandler` کاربران جدید ایجاد نمی‌کرد — فقط خطای «کاربر یافت نشد»
- **رفع:** اضافه شدن لاجیک کامل: اعتبارسنجی کد معرف، بررسی ظرفیت درخت باینری (حداکثر ۲ فرزند)، تعیین شاخه (چپ/راست)، ایجاد User + UserRole + UserWallet، رویدادهای دامنه
- **فایل:** `CMS/src/CMSMicroservice.Application/UserCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs`
### ۱۲.۲ — رفع جستجوی موبایل در AcceptContract 🔴 ✅
- **مشکل:** `_currentUserService.Username` = `"{FirstName} {LastName}"` — نه شماره موبایل! در نتیجه OTP و کاربر هیچوقت پیدا نمی‌شد
- **رفع:** ابتدا کاربر از `UserId` پیدا شود، سپس `user.Mobile` برای جستجوی OTP استفاده شود
- **فایل:** `CMS/src/CMSMicroservice.Application/UserCQ/Commands/AcceptContract/AcceptContractCommandHandler.cs`
### ۱۲.۳ — رفع `IsCompleteRegister()` در FrontOffice 🟡 ✅
- **مشکل:** `InitUserAuthInfo().GetAwaiter()` بدون `.GetResult()` — عملیات async اجرا نمی‌شود، منوی کناری وضعیت نادرست
- **رفع:** تغییر به `InitUserAuthInfo().GetAwaiter().GetResult()`
- **فایل:** `FrontOffice/src/FrontOffice.Main/Utilities/AuthService.cs`
---
## 🏷 فاز ۱۱ — رفع خطای صفحات + سرویس عمومی تصاویر (بهمن ۱۴۰۴)
> **هدف:** رفع خطای ۳ صفحه BackOffice + ایجاد اندپوینت HTTP عمومی برای سرو تصاویر
### ۱۱.۱ — سرویس عمومی سرو تصاویر (CMS) ✅
- **`UploadsController`** — اندپوینت `GET /uploads/{path}` برای سرو مستقیم فایل‌ها
- `[AllowAnonymous]` — بدون نیاز به احراز هویت
- کش مرورگر ۲۴ ساعته (`ResponseCache`)
- پشتیبانی از Range Requests
- **FMS Fallback:** اگر فایل محلی نباشد، از `dl.afrino.co` دانلود و کش می‌شود
- `IHttpClientFactory` با named client `"FMS"` (timeout: 30s)
- **فایل‌ها:** `CMS/WebApi/Controllers/UploadsController.cs`, `CMS/WebApi/Program.cs`
### ۱۱.۲ — رفع صفحه «صفحات سایت» ✅
- اضافه شدن **دکمه «افزودن صفحه جدید»** به بالای صفحه
- اضافه شدن **دکمه «حذف»** (آیکون قرمز) برای هر ردیف
- ایجاد **`SitePageCreateDialog`** — دیالوگ ایجاد صفحه جدید با فیلدها:
- کلید صفحه (PageKey)، عنوان، توضیحات متا، عنوان هیرو، زیرعنوان هیرو، تصویر هیرو، وضعیت
- Proto: اضافه شدن `CreateSitePage` و `DeleteSitePage` RPC
- CQRS: `CreateSitePageCommand/Handler` و `DeleteSitePageCommand/Handler`
- **فایل‌ها:**
- `sitepage.proto` — 2 RPC جدید + پیام‌های Request/Response
- `SitePageCQ/Commands/CreateSitePage/` — Command + Handler
- `SitePageCQ/Commands/DeleteSitePage/` — Command + Handler
- `CMS/WebApi/Services/SitePageService.cs` — 2 override جدید
- `BackOffice/Services/Content/ISitePageService.cs``CreateAsync` + `DeleteAsync` + `SitePageCreateDto`
- `BackOffice/Services/Content/SitePageService.cs` — پیاده‌سازی Create/Delete
- `BackOffice/Pages/Content/SitePageManagementPage.razor` — دکمه‌های Add/Delete
- `BackOffice/Pages/Content/SitePageManagementPage.razor.cs` — متدهای CreatePage/DeletePage
- `BackOffice/Pages/Content/Components/SitePageCreateDialog.razor` + `.razor.cs`
### ۱۱.۳ — رفع صفحه «قراردادها» (Contracts) ✅
- **مشکل:** صفحه `/contracts` اصلاً بارگذاری نمی‌شد — crash در DI
- **علت:** `UserContractContract.UserContractContractClient` در DI ثبت نشده بود
- **رفع:** اضافه شدن `using CMSMicroservice.Protobuf.Protos.UserContract` و ثبت gRPC client
- **فایل:** `BackOffice/Common/Configure/ConfigureService.cs`
### ۱۱.۴ — رفع صفحه «کیف پول کاربران» (Wallets) ✅
- **مشکل:** صفحه `/wallets` اصلاً بارگذاری نمی‌شد — crash در DI
- **علت:** دو سرویس ثبت نشده بود:
- `UserWalletContract.UserWalletContractClient`
- `UserWalletChangeLogContract.UserWalletChangeLogContractClient`
- **رفع:** اضافه شدن using‌ها و ثبت هر دو gRPC client
- **فایل:** `BackOffice/Common/Configure/ConfigureService.cs`
---
## 🏷 فاز ۱۰ — بهبود مدیریت محتوا: بلاگ + صفحات سایت (بهمن ۱۴۰۴)
> **هدف:** فعال‌سازی آپلود تصویر باینری و ادیتور HTML در صفحات بلاگ و صفحات سایت
### ۱۰.۱ — بهبود ادیتور پست بلاگ ✅
- **`MudTextField Lines="10"`** → **`MudHtmlEditor`** برای ویرایش محتوای HTML
- **فیلد متنی مسیر تصویر** → **`MudFileUpload`** با پیش‌نمایش تصویر (`Image` component)
- حذف فیلد `FeaturedImageThumbnailPath` (تولید خودکار توسط بک‌اند)
- اضافه شدن `MudForm` با اعتبارسنجی + حالت بارگذاری (`_loading`)
- مدیریت خطا: try-catch + Snackbar
- **فایل‌ها:** `Pages/Blog/Components/BlogPostEditDialog.razor` + `.razor.cs`
### ۱۰.۲ — بهبود ادیتور صفحه سایت ✅
- **فیلد متنی Hero Image** → **`MudFileUpload`** با پیش‌نمایش تصویر
- اضافه شدن `MudForm` + حالت آپلود + مدیریت خطا
- **فایل‌ها:** `Pages/Content/Components/SitePageEditDialog.razor` + `.razor.cs`
### ۱۰.۳ — بهبود ادیتور بخش صفحه سایت (Section) ✅
- **`MudTextField Lines="8"`** → **`MudHtmlEditor`** برای ویرایش محتوای HTML بخش‌ها
- **فیلدهای متنی تصویر/thumbnail** → **`MudFileUpload`** با پیش‌نمایش
- اضافه شدن `MudForm` + حالت آپلود + مدیریت خطا
- **فایل‌ها:** `Pages/Content/Components/SitePageSectionEditDialog.razor` + `.razor.cs`
### ۱۰.۴ — تغییرات Proto ✅
| Proto | تغییر |
|-------|-------|
| `blogpost.proto` | اضافه شدن `BlogImageFileModel` + فیلد `image_file` در Create/Update |
| `sitepage.proto` | اضافه شدن `SitePageImageFileModel` + فیلد `image_file` در Update/CreateSection/UpdateSection |
### ۱۰.۵ — تغییرات CQRS (بک‌اند CMS) ✅
- **Commands:** اضافه شدن `ImageFileBytes`, `ImageMime`, `ImageFileName` به:
- `CreateBlogPostCommand`, `UpdateBlogPostCommand`
- `UpdateSitePageCommand`, `CreateSitePageSectionCommand`, `UpdateSitePageSectionCommand`
- **Handlers:** تزریق `IFileManager` + فراخوانی `UploadImageAsync`:
- BlogPost → `Images/BlogPosts/`
- SitePage → `Images/SitePages/`
- SitePageSection → `Images/SitePageSections/`
### ۱۰.۶ — تغییرات سرویس‌های BackOffice ✅
- `BlogPostService.cs` — ارسال `BlogImageFileModel` با `ByteString.CopyFrom(dto.ImageFile)`
- `SitePageService.cs` — ارسال `SitePageImageFileModel` در Update/CreateSection/UpdateSection
- **DTOs:** اضافه شدن `ImageFile`, `ImageMime`, `ImageFileName` به `BlogPostEditDto`, `SitePageEditDto`, `SitePageSectionEditDto`
### خلاصه آمار فاز ۱۰
| معیار | تعداد |
|---|---|
| فایل‌های proto تغییریافته | 2 |
| Command‌های بروزشده | 5 |
| Handler‌های بروزشده | 5 |
| سرویس‌های gRPC تغییریافته | 2 (BlogPostService + SitePageService) |
| دیالوگ‌های UI بازنویسی‌شده | 3 (BlogPost, SitePage, SitePageSection) |
| سرویس‌های BackOffice بروزشده | 2 |
| DTO‌های بروزشده | 3 |
| Build | 0 Error ✅ (هر ۳ پروژه) |
---
## 🏷 فاز ۹ — معماری مدیریت فایل + رفع سفارشات + مهاجرت DB (بهمن ۱۴۰۴)
> **مستند کامل معماری فایل:** [`totalDoc/cms/FILE-MANAGEMENT-ARCHITECTURE.md`](../../totalDoc/cms/FILE-MANAGEMENT-ARCHITECTURE.md)
### ۹.۱ — حذف کد مرده FMS ✅
- حذف ۳ فایل غیرقابل استفاده مرتبط با FMS خارجی (`https://dl.afrino.co`):
| فایل حذف‌شده | شرح |
|-------------|------|
| `Infrastructure/Services/FmsFileManager.cs` | پیاده‌سازی HTTP upload به FMS |
| `Application/Common/FileManager/FileManagementService.cs` | سرویس قدیمی مدیریت فایل |
| `Application/Common/FileManager/IFileManagementService.cs` | اینترفیس قدیمی |
### ۹.۲ — رفع صفحه سفارشات (Orders) ✅
**مشکل ۱ — صفحه خالی (بدون پیغام خطا)**
- **علت:** `ServerReload` بدون try-catch — exception‌های gRPC ساکت می‌شدند
- **رفع:** اضافه شدن try-catch + نمایش خطا با Snackbar
- **فایل:** `Pages/UserOrder/UserOrderMainPage.razor.cs`
**مشکل ۲ — Shadow FK تکراری در OrderVAT**
- **علت:** `.WithOne()` بدون inverse navigation → EF Core یک shadow FK اضافی می‌ساخت
- **رفع:** `.WithOne()``.WithOne(x => x.OrderVAT)`
- **فایل:** `Persistence/Configurations/OrderVATConfiguration.cs`
**مشکل ۳ — فیلترهای oneof همیشه فعال**
- **علت:** فیلدهای proto `oneof` مقدار پیش‌فرض enum دارند (هیچ‌وقت null نیستند)، پس `PaymentStatus != null` همیشه true بود
- **رفع:** `PaymentStatus != null``HasPaymentStatus == true` (و مشابه برای DeliveryStatus و PaymentMethod)
- **فایل‌ها:** `UserOrderService.cs` — هر دو متد `GetAllUserOrderByFilter` و `GetCustomerOrders`
### ۹.۳ — مهاجرت DB: حذف محدودیت طول ستون‌های تصویر ✅
- **علت:** data URI‌های base64 بزرگ‌تر از `nvarchar(500)` بودند → truncation خاموش
- **رفع:** حذف `HasMaxLength(500)` از تمام ستون‌های image path
- **Migration:** `RemoveImagePathMaxLength`
| Entity | ستون‌ها |
|--------|---------|
| Product | `ImagePath`, `ImageThumbnailPath` |
| DiscountProduct | `ImagePath`, `ImageThumbnailPath` |
| Category | `ImagePath` |
| DiscountCategory | `ImagePath` |
| BlogPost | `FeaturedImagePath`, `FeaturedImageThumbnailPath` |
| SitePage | `HeroImagePath` |
| SitePageSection | `ImagePath`, `ImageThumbnailPath` |
### ۹.۴ — بازنویسی کامل مدیریت فایل: ذخیره دیسکی ✅
**معماری قبلی:** ذخیره data URI مستقیم در DB (ناکارآمد + حجم بالا)
**معماری جدید:**
1. فایل‌ها در دیسک ذخیره می‌شوند: `Uploads/Images/{folder}/{guid}.jpg`
2. مسیر نسبی در DB ذخیره می‌شود
3. `ImagePathResolverInterceptor` هنگام serve تبدیل به base64 می‌کند
**فایل‌های اصلی:**
| فایل | شرح |
|------|------|
| `Application/Common/FileManager/IFileManager.cs` | اینترفیس: Upload, UploadImage, Delete, ResolveImageUrl |
| `Infrastructure/Services/LocalFileManager.cs` | پیاده‌سازی: ImageSharp, main 1200×1200 + thumb 300×300, JPEG Q75 |
| `WebApi/Interceptors/ImagePathResolverInterceptor.cs` | gRPC Interceptor: walk بازگشتی response → تبدیل path به data URI |
**تنظیمات:**
- `FileStorage:UploadPath` → پوشه ریشه آپلود (پیش‌فرض: `AppContext.BaseDirectory/Uploads`)
- gRPC MaxReceiveMessageSize: 50MB
### ۹.۵ — پاکسازی LoggingBehaviour ✅
- **قبل:** فیلدهای باینری (byte[] تصاویر) کامل در لاگ چاپ می‌شدند → لاگ‌های چند مگابایتی
- **بعد:**
- فرمت: `JsonFormatter.Default.Format()` به جای `{@Request}`
- Regex: حذف فیلدهای باینری (`File`, `ImageFile`, `image_file`, `file`)
- محدودیت: حداکثر 2000 کاراکتر
- **فایل:** `WebApi/Common/Behaviours/LoggingBehaviour.cs`
### خلاصه آمار فاز ۹
| معیار | تعداد |
|---|---|
| فایل‌های حذف‌شده (کد مرده) | 3 |
| فایل‌های جدید | 2 (LocalFileManager بازنویسی + ImagePathResolverInterceptor) |
| فایل‌های ویرایش‌شده | ~12 |
| باگ‌های رفع‌شده | 4 (Orders page ×3 + DB truncation) |
| Migration‌های جدید | 1 (RemoveImagePathMaxLength) |
| ستون‌های DB تغییریافته | 9 (nvarchar(500) → nvarchar(max)) |
| Build | 0 Error ✅ (هر ۳ پروژه) |
---
## 🏷 فاز ۸ — یکسان‌سازی فروشگاه عادی و تخفیفی (بهمن ۱۴۰۴)
> **مستند کامل:** [`totalDoc/ui-modernization/BACKOFFICE-STORE-UNIFICATION.md`](../../totalDoc/ui-modernization/BACKOFFICE-STORE-UNIFICATION.md)
### ۸.۱ — بازسازی NavMenu ✅
- ایجاد دو NavGroup مجزا: «فروشگاه عادی» و «فروشگاه تخفیفی»
- حذف «ویرایش دسته‌جمعی» از منو
- انبارداری → گروه مستقل | پکیج‌ها → آیتم مستقل
- **فایل:** `Shared/NavMenu.razor`
### ۸.۲ — حذف گزارش‌های کوچک از سفارشات ✅
- حذف کارت‌های آماری (TotalOrders + TotalAmount) و نمودار Bar ارسال
- حذف فیلدها: `_stats`, `_statusChartLabels`, `_statusChartSeries`
- حذف متد `UpdateStats()` و کلاس `OrderStatsViewModel`
- عنوان: «سفارش‌های کاربر» → «لیست سفارشات»
- **فایل‌ها:** `Pages/UserOrder/UserOrderMainPage.razor` + `.razor.cs`
### ۸.۳ — رفع باگ لیست خالی سفارشات ✅
- **علت:** `PaymentDate.ToDateTime()` بدون null check → exception ساکت در WASM
- **رفع:** `@if (context.Item.PaymentDate != null)` + fallback "-"
- **فایل:** `Pages/UserOrder/UserOrderMainPage.razor`
### ۸.۴ — بازنویسی صفحه محصولات تخفیفی ✅
- تبدیل به `BasePageComponent` (مطابق فروشگاه عادی)
- فیلترهای جدید: وضعیت + موجودی
- ستون‌های جدید: تصویر inline + عنوان truncate + چیپ رنگی موجودی + چیپ وضعیت
- **فایل‌ها:** `Pages/DiscountShop/DiscountProductsMainPage.razor` + `.razor.cs` (بازنویسی کامل)
### ۸.۵ — بازنویسی صفحه دسته‌بندی‌های تخفیفی ✅
- تبدیل به `BasePageComponent` (مطابق فروشگاه عادی)
- ستون‌های جدید: نام لاتین، دسته‌بندی والد (resolve شده)، تعداد محصولات، ترتیب
- code-behind جدید (`@code` درون‌خطی → `.razor.cs`)
- **فایل‌ها:** `Pages/DiscountShop/DiscountCategoriesMainPage.razor` + `.razor.cs` (بازنویسی کامل)
### خلاصه آمار فاز ۸
| معیار | تعداد |
|---|---|
| فایل‌های بازنویسی‌شده | 4 |
| فایل‌های ویرایش‌شده | 3 |
| باگ‌های رفع‌شده | 2 (PaymentDate null + لیست خالی) |
| Build | 0 Error ✅ |
---
## 🏷 فاز ۷ — پس از اجرای Audit v7.0 (تیر ۱۴۰۴)
### خلاصه تغییرات
- **۵۷ مورد** از ۵۸ یافته audit اجرا شد (≈98%)
- **تمام ۱۶ مورد بحرانی 🔴** حل شد
- **تمام ۱۸ مورد جزئی 🟢** حل شد
- **فازهای ۱ تا ۶ تکمیل شدند** (به‌جز DayaLoan و Audit Trail که نیاز به بک‌اند دارند)
- **فاز ۷** — Global Search, Notification Bell, NavMenu Badge, Export Excel 4 صفحه جدید
- **۱۳+ فایل جدید** ساخته شد
- **۹ صفحه** ادغام شدند (۱۴ صفحه → ۵ صفحه)
- **Build**: 0 error ✅
---
## فاز ۷ — Global Search + Notifications + Export All + NavMenu Badge (v7.0)
### 6.5 — Badge Pending در NavMenu — ✅
- **`NavMenu.razor`**: اضافه شدن `MudBadge` روی لینک «درخواست‌های برداشت»
- نمایش تعداد درخواست‌های Pending بصورت real-time از gRPC (`CommissionContract.GetWithdrawalRequestsAsync` با `Status=0`)
- fire-and-forget در `OnInitializedAsync` با try/catch silent
### 6.2 — Global Search در AppBar — ✅
- **فایل جدید**: `Shared/GlobalSearch.razor`
- `MudAutocomplete<SearchResultItem>` با Debounce 400ms
- دو نوع جستجو: ۱) نام صفحات (۱۵ صفحه اصلی) ۲) کاربران از gRPC (`UserContract.GetAllUserByFilterAsync`)
- Template سفارشی با آیکون + عنوان + زیرنویس
- ناوبری خودکار با `Navigation.NavigateTo`
- اضافه شده در `MainLayout.razor` داخل `<AuthorizeView>`
- CSS در `app.css` — rounded border-radius 24px, compact padding
### 6.1 — Notification Bell (placeholder) — ✅
- **`MainLayout.razor`**: اضافه شدن `MudMenu` + `MudBadge` (Dot) + آیکون Notifications
- Dropdown با هدر «اعلان‌ها» + placeholder «اعلان جدیدی وجود ندارد»
- آماده اتصال به بک‌اند — فقط `Visible=true` و populate items
### 6.3 — Export Excel صفحات جدید — ✅ (۱۰ صفحه کامل)
- **۴ صفحه جدید** اضافه شدند به ۶ صفحه قبلی:
1. **UserMainPage** — خروجی CSV: شناسه, موبایل, نام, نام خانوادگی, کدملی
2. **DiscountOrdersMainPage** — خروجی CSV: شماره سفارش, تاریخ, مبلغ کل, تخفیف, قابل پرداخت, تعداد, وضعیت, پرداخت
3. **ManualPayments** — خروجی CSV: شناسه, شناسه کاربر, نام کاربر, مبلغ, نوع, وضعیت, تاریخ
4. **InventoryMainPage** — خروجی CSV: شناسه, محصول, انبار, موجودی, رزرو, قابل فروش, حد هشدار, وضعیت
- هر ۴ صفحه: دکمه «خروجی Excel» + `ExportToExcel()` + `EscapeCsv()` + UTF-8 BOM + `jsSaveAsFile`
- فایل‌های code-behind جدید: `ManualPayments.razor.cs`, `DiscountOrdersMainPage.razor.cs`
---
## فاز ۶ — تاریخ شمسی + تاریخچه‌ها + Export Excel (v6.0)
### تاریخ شمسی یکپارچه (3.6) — ✅ کامل
- **۱۹+ فایل** آپدیت شدند: تمام تاریخ‌ها از فرمت میلادی (`yyyy/MM/dd`) به شمسی تبدیل شدند
- از extension method‌های `DateTimeConverterCL` استفاده شد:
- `.MiladiToJalali()` — فرمت تاریخ فقط (مثال: ۱۴۰۴/۰۴/۱۲)
- `.MiladiToJalaliWithTime()` — فرمت تاریخ و ساعت
- **صفحات اصلاح‌شده شامل**: Club (Members, Statistics, Features, MemberDetailsDialog)، Commission (Dashboard, WeeklyReports, WithdrawalRequests, Payouts, WithdrawalReports)، Network (UserNetworkInfo, BalancesReport, Statistics, NetworkTreeViewer)، UserOrder, Products, Package, Inventory, DiscountShop, Blog, Content, و غیره
- `PropertyColumn Format="yyyy/MM/dd"``TemplateColumn` با `.MiladiToJalali()`
- `MudDatePicker DateFormat` تغییر نکرد (قابلیت تقویم شمسی نیاز به کتابخانه جداگانه دارد)
### تاریخچه عضویت باشگاه (5.7) — ✅
- **`MemberDetailsDialog.razor`**: تب تاریخچه با فیلدهای صحیح proto:
- ستون‌ها: `Action`, `Created`, `PackageName`, `Reason`, `PerformedBy`
- Request: `PageIndex = 0` (نه PageNumber)
### تاریخچه تغییرات شبکه (5.8) — ✅
- **`UserNetworkInfo.razor`**: تب تاریخچه با فیلدهای تخصصی:
- ستون‌ها: `Action`, `Created`, `OldParentId → NewParentId`, `OldNetworkLeg → NewNetworkLeg` (چپ/راست)
- ستون `Reason` و `PerformedBy` اضافه شد
- Request: `PageIndex = 0`
### لاگ اجرای Worker‌ها (5.6) — ✅
- در تب WorkerControl از SystemHub قبلاً پیاده‌سازی شده بود
### Export Excel / CSV (6.3) — ✅ ۶ صفحه
قابلیت خروجی CSV (سازگار با Excel) به ۶ صفحه اصلی اضافه شد:
| صفحه | فایل(ها) | ستون‌های خروجی |
|---|---|---|
| **Products** (قبلاً موجود) | ProductsMainPage.razor.cs | — |
| **Club Members** | ClubMembers.razor + .razor.cs | شناسه,کاربر,نام,پکیج,کد فعال‌سازی,تاریخ فعال‌سازی,تاریخ انقضا,فعال,منقضی |
| **Withdrawal Requests** | WithdrawalRequests.razor + .razor.cs | شناسه,کاربر,نام,مبلغ,وضعیت,روش,شبا,تاریخ درخواست,تاریخ پردازش,مرجع بانکی |
| **Weekly Reports** | WeeklyReports.razor + .razor.cs | هفته,مبلغ استخر,موجودی‌ها,ارزش,وضعیت,تاریخ محاسبه |
| **User Orders** | UserOrderMainPage.razor + .razor.cs | شناسه,کاربر,نام,مبلغ,وضعیت پرداخت,وضعیت ارسال,روش پرداخت,تاریخ |
| **Stock Movements** | MovementsPage.razor + .razor.cs | شناسه,محصول,نوع,تعداد,قبل,بعد,مرجع,یادداشت,تاریخ |
**الگوی پیاده‌سازی:**
- دکمه `<MudButton>` با آیکون `FileDownload` در ToolBarContent
- `StringBuilder` → CSV با هدرهای فارسی
- UTF-8 BOM (`Encoding.UTF8.GetPreamble()`) برای نمایش صحیح فارسی در Excel
- `jsSaveAsFile(filename, base64)` از `wwwroot/js/main.js`
- متد `EscapeCsv()` برای مقادیر حاوی کاما/کوتیشن
- تاریخ‌ها در خروجی نیز شمسی هستند
### فایل‌های جدید (این نسخه)
| فایل | عملکرد |
|---|---|
| `Pages/Commission/WeeklyReports.razor.cs` | Code-behind برای export (به‌دلیل محدودیت Razor parser با escaped quotes) |
---
## نسخه قبلی — Audit v5.0 (خرداد ۱۴۰۴)
### تم رنگی یکپارچه
- **`CustomMudTheme.cs`**: تم Primary از `#0380C0` به `#6366f1` (Indigo) تغییر کرد — هماهنگ با FrontOffice
- **`PaletteDark`** اضافه شد: Surface `#1a1a2e`, Background `#16213e`, AppbarBackground `#0f3460`
- **`app.css`**: رنگ loading از `#1b6ec2` به `var(--mud-palette-primary)` تغییر کرد
- **`NavMenu.razor.css`**: رنگ active از `#0380C0` هاردکد به CSS variable تغییر کرد
- **`MainLayout.razor`**: حذف inline style `border: 1px solid #5e4df9`
### Dark Mode Toggle
- آیکون ماه/آفتاب در AppBar اضافه شد
- `_isDarkMode` state با `MudThemeProvider` اتصال دارد
- تعویض بین `PaletteLight` و `PaletteDark` در CustomMudTheme
### عنوان اپلیکیشن
- "پنل مدیریت **فرصت**" → "پنل مدیریت **کارا بازار سلامت**"
---
## فاز ۲ — بازطراحی لایوت صفحات
### BasePageComponent بازنویسی کامل
- **قبل**: فیلتر ثابت سمت راست (20%) + جدول (80%)، ارتفاع 85vh هاردکد
- **بعد**: فیلتر collapsible بالای جدول، فیلدها در `MudGrid` با `MudItem`، responsive
- **۱۳ صفحه** آپدیت شدند: User, Products, Package, Category, UserOrder, UserAddress, UserRole, Role, BlogPost, BlogCategory, Tag, Transactions, Content
### DataGrid Height یکسان‌سازی
- همه DataGrid‌ها: `Height="calc(100vh - 240px)"`
- **صفحات اصلاح‌شده**: تمام ~33 صفحه دارای DataGrid
### Pager یکسان‌سازی
- **PageSize**: همه `20, 50, 100`
- **متن فارسی**: `RowsPerPageString="تعداد در صفحه"` و `InfoFormat="سطر {first_item} تا {last_item} از {all_items}"`
### MaxWidth یکسان‌سازی
- `MainLayout.razor` MudContainer wrapper اضافه شد → MaxWidth یکسان برای همه صفحات
- **۳۱ صفحه**: حذف `MudContainer` تکراری (double nesting)
### Breadcrumb
- **`Shared/AppBreadcrumb.razor`** ساخته شد
- جداکننده چپ‌به‌راست (`ChevronLeft`)
- نقشه فارسی URL segments (60+ مسیر)
- آخرین آیتم disabled (صفحه فعلی)
### Login + VerifyCode بازطراحی
- عرض ثابت 35% → responsive `MaxWidth.Small`
- لوگو + برندینگ "کارا بازار سلامت" اضافه شد
- طراحی gradient background
---
## فاز ۲.۵ — پاکسازی ساختاری
### NavMenu بازسازی کامل
- **لینک مرده `categories-dragdrop`**: حذف شد
- **Transactions**: حذف از NavMenu (صفحه stub خالی)
- **AlertsMonitoring**: حذف از NavMenu (100% mock)
- **WorkerControl**: به SystemHub منتقل شد (قبلاً لینک نداشت)
- **«پیام‌های عمومی»**: از بخش فروشگاه تخفیفی به «مدیریت محتوا» منتقل شد
- **ساختار گروه‌بندی** اصلاح شد:
- کمیسیون و شبکه: NavGroup واضح
- باشگاه مشتریان: اعضا و آمار + فیچرها
- فروشگاه: پکیج‌ها، محصولات (group)، سفارش‌ها، فروشگاه تخفیفی (group)
- مدیریت: کاربران، نقش‌ها، پرداخت دستی، **کیف‌پول (جدید)**، **قراردادها (جدید)**
- محتوا: بلاگ، صفحات سایت، پیام‌های عمومی
- سیستم: مدیریت سیستم، نسخه اپ‌ها
---
## فاز ۳ — UX بهبودها
### NoRecordsContent (Empty State)
- **۳۳ DataGrid** آپدیت شدند
- پیام: `<MudAlert Severity="Info">موردی یافت نشد.</MudAlert>`
### Delete Confirmation با نام آیتم
- **۱۱ صفحه** اصلاح شدند:
- Role → `«{model.Title}»`
- User → `«{model.FirstName} {model.LastName}»`
- Package → `«{model.Title}»`
- UserAddress → `«{model.Title}»`
- UserRole → `شناسه «{model.Id}»`
- UserOrder → `شماره «{model.Id}»`
- Category → `«{model.Title}»`
- Products → `«{model.Title}»`
- DiscountCategories → `«{category?.Title}»` (lookup اضافه شد)
- DiscountProducts → `«{product?.Title}»` (lookup اضافه شد)
- PublicMessages → `«{message?.Title}»` (lookup اضافه شد)
### User Menu در AppBar
- `MudMenu` جایگزین متن ساده شماره موبایل شد
- آیتم‌ها: نام کاربر (غیرفعال) + خروج
- آیکون `AccountCircle`
- خروج (`signout`) از NavMenu به AppBar منتقل شد
---
## فاز ۳.۵ — ادغام صفحات
### Dashboard Merge ← `Pages/Index.razor`
- **`Index.razor` + `SystemOverview.razor`** ادغام شدند
- Routes: `/` + `/dashboard/overview`
- بخش‌ها:
- 6 کارت آمار CMS (کاربران، محصولات، پکیج‌ها، دسته‌بندی‌ها، سفارش‌ها، مبلغ پرداخت‌شده)
- آمار کمیسیون (هفته جاری، استخر، موجودی‌ها، وضعیت محاسبه)
- آمار باشگاه (کل/فعال/غیرفعال)
- جدول آخرین ۵ سفارش پرداخت‌شده
- ۶ دکمه Quick Action
- بارگذاری موازی با `Task.WhenAll` + `.ResponseAsync`
- `SystemOverview.razor` **حذف شد**
### DiscountShopHub ← `Pages/DiscountShop/DiscountShopHub.razor`
- Routes: `/discount-shop`, `/discount-orders`, `/discount-sales-reports`
- ۲ تب: DiscountOrdersMainPage + SalesReports
- انتخاب تب از URL
### ClubHub ← `Pages/Club/ClubHub.razor`
- Routes: `/club`, `/club/members`, `/club/statistics`
- ۲ تب: ClubMembers + Statistics
### BlogHub ← `Pages/Blog/BlogHub.razor`
- Routes: `/blog`, `/blog/posts`, `/blog/categories`, `/tags`
- ۳ تب: BlogPostManagementPage + BlogCategoryManagementPage + TagManagementPage
### SystemHub ← `Pages/SystemManagement/SystemHub.razor`
- Routes: `/system`, `/system/configuration`, `/system/worker-control`, `/system/health`
- ۳ تب: Configuration + WorkerControl + HealthDashboard
### ۱۰ صفحه — `@page` directive حذف شد
صفحاتی که به hub منتقل شدند دیگر route مستقل ندارند:
DiscountOrdersMainPage, SalesReports, ClubMembers, Statistics, BlogPostManagementPage, BlogCategoryManagementPage, TagManagementPage, Configuration, WorkerControl, HealthDashboard
---
## فاز ۴ — فیکس‌های بیزینسی
### Dashboard آمار اصلاح شد
- **قبل**: آمار از ۱۰ رکورد اول محاسبه می‌شد (نادرست)
- **بعد**: از `metadata.TotalCount` و API‌های جداگانه استفاده می‌شود
### ManualPayments — تأیید/رد
- دکمه‌های Approve/Reject وقتی `Status==0` (در انتظار) نمایش داده می‌شوند
- Confirmation dialog قبل از اجرا
- از `ManualPaymentId` (نه `Id`) استفاده می‌شود
### UserOrder — فیلتر Cancelled
- `<MudSelectItem Value="@(5)">لغو شده</MudSelectItem>` اضافه شد
### WithdrawalRequests — بهبود کامل
- **Pager**: 10/25/50/100 → 20/50/100
- **`WithdrawalDetailsDialog.razor`** (جدید): نمایش کامل اطلاعات کاربر، درخواست، و بانکی
- **`RejectReasonDialog.razor`** (جدید): ورود دلیل رد با اعتبارسنجی
- ViewDetails: از Snackbar TODO به دیالوگ واقعی تغییر کرد
- RejectRequest: از دلیل هاردکد به دیالوگ custom تغییر کرد
---
## فاز ۵ — صفحات مفقود (CMS-Derived)
### WalletManagementPage ← `Pages/Wallet/WalletManagementPage.razor`
- Route: `/wallets`
- **تب ۱ — کیف‌پول‌ها**: DataGrid با ServerData، فیلتر UserAutoComplete
- ستون‌ها: شناسه، کاربر، موجودی اصلی، موجودی شبکه، مشاهده تغییرات
- نوع: `GetAllUserWalletByFilterResponseModel`
- **تب ۲ — تاریخچه تغییرات**: DataGrid با ServerData، فیلتر کاربر + نوع (افزایش/کاهش)
- ستون‌ها: شناسه، کیف‌پول، نوع (چیپ رنگی)، تغییر موجودی، موجودی فعلی، تغییر شبکه، موجودی شبکه فعلی، شناسه مرجع، تاریخ
- نوع: `GetAllUserWalletChangeLogByFilterResponseModel`
- فیلدهای proto با typo: `ChangeNerworkValue`, `RefrenceId`
### UserContractPage ← `Pages/Contract/UserContractPage.razor`
- Route: `/contracts`
- DataGrid با ServerData، فیلتر UserAutoComplete
- ستون‌ها: شناسه، کاربر، قرارداد، کد امضا، لینک PDF، جزئیات
- نوع: `GetAllUserContractByFilterResponseModel`
### ContractDetailsDialog ← `Pages/Contract/ContractDetailsDialog.razor`
- جدول ساده: شناسه، کاربر، قالب قرارداد، کد امضا، لینک دانلود PDF
### ClubFeaturesPage ← `Pages/Club/ClubFeaturesPage.razor`
- Route: `/club/features`
- **تب ۱ — فیچرهای تعریف‌شده**: کارت‌های MudPaper با عنوان، توضیحات، وضعیت (فعال/غیرفعال)، ترتیب
- از `ConfigClient.GetClubFeaturesAsync`
- **تب ۲ — فیچرهای کاربران**: DataGrid با فیلتر کاربر، سوئیچ فعال/غیرفعال
- از `ClubClient.GetUserClubFeaturesAsync` + `ToggleUserClubFeatureAsync`
---
## ⚠️ نکات فنی مهم
### الگوهای Proto
- **فیلتر**: `new GetAllXxxByFilterFilter()` (سطح بالا، بدون `.Types.`)
- **پاسخ لیست**: `GetAllXxxByFilterResponseModel` (نه `GetXxxResponse`)
- **Typo‌ها در proto**: `ChangeNerworkValue` و `RefrenceId` — باید دقیقاً همین‌ها استفاده شوند
### _Imports.razor — سرویس‌های Global
سرویس‌های زیر از `_Imports.razor` تزریق می‌شوند و نباید در code-behind با `[Inject]` دوباره تعریف شوند:
- `NavigationManager Navigation`
- `ILocalStorageService LocalStorageService`
- `ISnackbar Snackbar`
- `IJSRuntime jsRuntime`
- `IDialogService DialogService`
- `AuthenticationStateProvider`
### MudBlazor محدودیت‌ها
- `Variant.Flat` وجود ندارد → از `Variant.Text` استفاده شود
- `GutterSize` روی `MudGrid` deprecated است
---
## فاز ۴ (ادامه) — تکمیل فیکس‌های بیزینسی
### UserSettings — حذف تب‌های جعلی (2.5.5)
- **`UserSettings.razor`**: تب Notifications (۷ تاگل جعلی بدون بک‌اند) → placeholder «به‌زودی فعال می‌شود» با آیکون NotificationsActive
- **`UserSettings.razor`**: تب Security (فرم تغییر رمز + 2FA بدون بک‌اند) → placeholder «به‌زودی فعال می‌شود» با آیکون Lock
- **حذف کد مرده**: ~۱۵ متغیر notification/security + متدهای `SaveNotificationSettings()`, `SaveSecuritySettings()`, `ChangePassword()`
### HealthDashboard — حذف متریک‌های Mock (2.5.6)
- **`HealthDashboard.razor`**: بخش System Resources (۴ کارت CPU=45.2%, Memory=62.8%, Disk=73.5%, Network) → یک کارت با پیام «مانیتورینگ منابع سیستم در نسخه‌های آینده با اتصال به Prometheus/Grafana فعال خواهد شد»
- **حذف کد مرده**: ۹ متغیر mock (`_cpuUsage`, `_memoryUsage`, etc.) + متد `GetResourceColor()`
### حذف cursor:pointer زائد (4.10)
- `Style="cursor:pointer;"` از **۱۷ فایل** حذف شد (MudButton/MudIconButton خودشان cursor:pointer دارند)
- شامل: ProductsMainPage, GalleryDialog, Image.razor (base component), RoleMainPage, UserOrderMainPage, PackageMainPage, etc.
### حذف Console.WriteLine (4.11)
- از **۱۱ فایل** حذف شد: DiscountProductService, DiscountCategoryService, NetworkTreeViewer, UserSettings, CategoryMainPage, UserOrderMainPage, Commission/Dashboard, DiscountProductsMainPage, DiscountCategoryMultiSelectCombo, UserRole/CreateDialog
- **`Commission/Dashboard.razor.cs`**: `using System.Text.Json` هم حذف شد (دیگر استفاده نمی‌شد)
### DiscountProducts — فیلتر دسته‌بندی (4.7)
- **`DiscountProductsMainPage.razor`**: `@inject IDiscountCategoryService` اضافه شد
- دسته‌بندی‌های فعال از سرویس بارگذاری و در MudSelect نمایش داده می‌شوند
- جایگزینی `@* TODO: Load categories *@` با `@foreach` واقعی
### DiscountProducts — Items → ServerData (4.8)
- **`DiscountProductsMainPage.razor`**: تبدیل از `Items="@_products"` به `ServerData="LoadServerData"`
- متد `LoadServerData(GridState<T>)` اضافه شد با `PageNumber` و `PageSize` از state
- `Height="calc(100vh - 300px)"` + `FixedHeader="true"` اضافه شد
- Snackbar اطلاع‌رسانی اضافی در هر بارگذاری حذف شد (redundant)
### UserOrder — فعال‌سازی آمار (4.6)
- **`UserOrderMainPage.razor`**: بخش آمار (MudCard + MudChart) از حالت comment خارج شد
- نمایش تعداد سفارش‌ها + مجموع مبلغ + نمودار Bar وضعیت ارسال
### UserSettings — persist با localStorage (4.9)
- تب General از قبل با localStorage کار می‌کرد ✅ (فقط تب‌های جعلی حذف شدند)
---
## فاز ۴.۵ — Cross-Navigation
### لینک نام کاربر → پروفایل (4.5.1, 4.5.2)
- **`UserOrderMainPage.razor`**: ستون `UserFullName` از PropertyColumn به TemplateColumn تبدیل شد — MudLink به `/network/user-info/{UserId}`
- **`UserPayouts.razor`**: نام/شناسه کاربر به MudLink تبدیل شد → `/network/user-info/{UserId}`
- **`WithdrawalRequests.razor`**: نام کاربر به MudLink تبدیل شد → `/network/user-info/{UserId}`
### لینک محصول ↔ انبار (4.5.3, 4.5.4) — Skip
- صفحه جزئیات محصول وجود ندارد (فقط لیست + دیالوگ ویرایش)
- لینک cross-navigation بدون صفحه مقصد معنادار نیست
### Extract shared status helpers (3.5.6) — Skip
- بررسی نشان داد هر context مقادیر متفاوتی دارد:
- UserPayouts: status 1 = Success/پرداخت شده
- WithdrawalRequests: status 1 = Info/واریز شده
- PayoutDetailsDialog: فقط ۳ وضعیت (نه ۶)
- استخراج به helper مشترک باعث از دست رفتن معنای domain-specific می‌شود
---
## 📈 آمار تغییرات
| معیار | تعداد |
|---|---|
| فایل‌های جدید | 11 |
| فایل‌های حذف‌شده | 1 (SystemOverview.razor) |
| فایل‌های ویرایش‌شده | ~65+ |
| DataGrid‌های آپدیت‌شده | 33 |
| Delete confirmation اصلاح‌شده | 11 |
| صفحات ادغام‌شده | 14 → 5 |
| خطاهای build حل‌شده | 9+ |
| Console.WriteLine حذف‌شده | 22+ (از ۱۱ فایل) |
| cursor:pointer حذف‌شده | 17 فایل |
| Cross-link اضافه‌شده | 3 صفحه (UserOrder, UserPayouts, WithdrawalRequests) |
+445
View File
@@ -0,0 +1,445 @@
# CMS Microservice - Network & Club Commission + Inventory Management System
[![Status](https://img.shields.io/badge/Status-Active%20Development-success)]()
[![Progress](https://img.shields.io/badge/Inventory%20System-Phase%202%20Complete-blue)]()
[![Phase](https://img.shields.io/badge/Next-Business%20Services-orange)]()
## 📊 Project Status (January 2026)
### 🏪 Inventory Management System - NEW!
**Progress**: Phase 2 Complete (50%)
**Architecture**: Clean Architecture + CQRS + Repository Pattern
#### ✅ Completed Phases
1.**Phase 1: Infrastructure & Domain Layer**
- Domain Entities: `InventoryItem`, `StockMovement`, `Warehouse`
- Domain Enums: `StockMovementType`
- EF Core Configurations with proper indexing
- Database migration applied
2.**Phase 2: Repository Pattern & CQRS**
- Repository Interfaces & Implementations
- CQRS Commands (17 commands)
- CQRS Queries (35 queries)
- MediatR Handlers (52 handlers)
#### 🔄 In Progress
3. 🔄 **Phase 3: Business Services Layer**
4.**Phase 4: DTOs & AutoMapper**
5.**Phase 5: API Controllers**
---
### 💼 Commission System - Production Ready
**Progress**: 85% Complete
**MVP Status**: ✅ 100% Complete
#### ✅ Completed Features
- ✅ Binary network tree with automatic placement
- ✅ Club membership (Member/Trial) with commission rates
- ✅ Weekly commission calculation (Lesser Leg algorithm)
- ✅ Background worker with Hangfire
- ✅ Email + SMS notifications (MailKit + Kavenegar)
- ✅ Health check endpoints (Kubernetes-ready)
### 🟡 Partially Complete
- Phase 10: Withdrawal & Settlement (40%)
- ✅ Commands & Database
- ❌ Payment Gateway Integration
### ❌ Not Started
- Phase 9: Club Shop & Product Integration (0%)
---
## 🚀 Recent Updates (January 2026)
### 🏪 Inventory Management System - NEW! ✅
**Complete CQRS-based inventory management with:**
#### Domain Layer:
-`InventoryItem` - Multi-warehouse product tracking with min/max thresholds
-`StockMovement` - Complete audit trail with 8 movement types
-`Warehouse` - Multi-location support with default warehouse
#### Repository Pattern:
-`IInventoryItemRepository` - 25+ methods for inventory operations
-`IStockMovementRepository` - Movement tracking & analytics
-`IWarehouseRepository` - Warehouse management & statistics
#### CQRS Commands (17 total):
- **Inventory:** Create, Update, Delete, Reserve, Release, Reduce, Increase
- **Movement:** Create, BulkCreate, Delete
- **Warehouse:** Create, Update, Delete, SetDefault, Activate, BulkCreate
#### CQRS Queries (35 total):
- **Inventory:** GetById, Search, LowStock, OutOfStock, CheckAvailability
- **Movement:** GetHistory, GetByOrder, Search, Analytics, DailyVolume, TopMoving
- **Warehouse:** GetById, Search, GetStats, GetLowStock, GetAllStats
#### Business Features:
- ✅ Multi-warehouse inventory management
- ✅ Stock reservation system for orders
- ✅ Automatic movement tracking
- ✅ Low stock & out-of-stock alerts
- ✅ Advanced analytics & reporting
- ✅ Bulk operations support
- ✅ Transaction-safe operations
---
### Email & SMS Notifications - COMPLETED ✅
-**MailKit 4.14.1** for Email (SMTP with HTML templates)
-**Kavenegar 1.2.5** for SMS (Iranian SMS gateway)
- ✅ User.Email field added with migration
- ✅ 3 notification types: Commission, Club activation, Errors
- ✅ Persian RTL templates with rich formatting
- ✅ Production configuration guide created
### Hangfire Job Scheduling - COMPLETED ✅
- ✅ Dashboard UI at `/hangfire`
- ✅ Cron schedule: Sunday 00:05 UTC
- ✅ SQL Server persistence
- ✅ Manual trigger API endpoints
- ✅ Distributed execution support
### Infrastructure Enhancements - COMPLETED ✅
- ✅ Health Check endpoints (`/health`, `/health/ready`, `/health/live`)
- ✅ AlertService (structured logging for Sentry/Slack)
- ✅ Retry logic (Polly 8.5.0 with exponential backoff)
- ✅ WorkerExecutionLog (database audit trail)
- ✅ CurrentUserService (JWT authentication context)
---
## 🏗️ Architecture
**Clean Architecture** with 4 layers:
```
CMSMicroservice.Domain/ # Entities, Enums, Interfaces
├── Entities/
│ ├── InventoryItem.cs # NEW: Inventory tracking
│ ├── StockMovement.cs # NEW: Movement audit
│ └── Warehouse.cs # NEW: Multi-warehouse
├── Enums/
│ └── StockMovementType.cs # NEW: Movement types
CMSMicroservice.Application/ # CQRS (Commands, Queries, MediatR)
├── Features/
│ ├── InventoryItems/ # NEW: Inventory CQRS
│ │ ├── Commands/
│ │ ├── Queries/
│ │ └── Handlers/
│ ├── StockMovements/ # NEW: Movement CQRS
│ │ ├── Commands/
│ │ ├── Queries/
│ │ └── Handlers/
│ └── Warehouses/ # NEW: Warehouse CQRS
│ ├── Commands/
│ ├── Queries/
│ └── Handlers/
└── Common/Interfaces/
└── Repositories/ # NEW: Repository interfaces
CMSMicroservice.Infrastructure/ # DbContext, Services, Background Jobs
├── Persistence/
│ ├── Context/
│ ├── Configurations/ # NEW: EF Core configs
│ ├── Repositories/ # NEW: Repository implementations
│ └── Migrations/
└── DependencyInjection.cs # NEW: DI setup
CMSMicroservice.WebApi/ # gRPC Services, Controllers
CMSMicroservice.Protobuf/ # Protocol Buffers definitions
```
**Technology Stack**:
- .NET 9.0
- Entity Framework Core 9.0.11
- gRPC + JSON Transcoding
- Hangfire 1.8.22 (Job Scheduling)
- MediatR 13.0.0 (CQRS)
- Polly 8.5.0 (Resilience)
- MailKit 4.14.1 (Email)
- Kavenegar 1.2.5 (SMS)
- SQL Server
---
## 📖 Documentation
- **[Development Plan](docs/development-plan.md)** - NEW: Inventory system roadmap
- **[Implementation Progress](docs/implementation-progress.md)** - Detailed phase-by-phase progress
- **[Email/SMS Configuration Guide](docs/email-sms-configuration-guide.md)** - Production setup instructions
- **[Balance Calculation Logic](docs/balance-calculation-carryover-logic.md)** - Commission algorithm details
- **[Binary Tree Registration](docs/binary-tree-registration-guide.md)** - Network tree guide
- **[Network Club Commission System](docs/network-club-commission-system-v1.1.md)** - Full system specification
---
## 🏪 Inventory System Usage
### Create Warehouse
```csharp
await mediator.Send(new CreateWarehouseCommand
{
Name = "Main Warehouse",
Code = "WH-001",
IsDefault = true,
IsActive = true
});
```
### Create Inventory Item
```csharp
await mediator.Send(new CreateInventoryItemCommand
{
ProductId = 1,
WarehouseId = 1,
Quantity = 100,
MinQuantity = 10,
MaxQuantity = 1000
});
```
### Reserve Stock for Order
```csharp
await mediator.Send(new ReserveInventoryCommand
{
Id = inventoryId,
Quantity = 5,
OrderId = 12345
});
```
### Check Availability
```csharp
bool available = await mediator.Send(
new CheckInventoryAvailabilityQuery(inventoryId, 10));
```
### Get Low Stock Alerts
```csharp
var lowStock = await mediator.Send(new GetLowStockItemsQuery
{
WarehouseId = 1,
Count = 50
});
```
### Get Movement Analytics
```csharp
var summary = await mediator.Send(new GetMovementSummaryQuery
{
FromDate = DateTime.Now.AddDays(-7),
ToDate = DateTime.Now
});
var topProducts = await mediator.Send(new GetTopMovingProductsQuery
{
FromDate = DateTime.Now.AddDays(-30),
ToDate = DateTime.Now,
Count = 10
});
```
---
## 🚀 Quick Start
### Prerequisites
- .NET 9.0 SDK
- SQL Server (local or remote)
- (Optional) Gmail account for Email
- (Optional) Kavenegar account for SMS
### 1. Clone & Build
```bash
cd /home/masoud/Apps/project/FourSat/CMS/src
dotnet build
```
### 2. Configure Database
Update `appsettings.json` with your SQL Server connection:
```json
"ConnectionStrings": {
"DefaultConnection": "Server=YOUR_SERVER;Database=Foursat_CMS;..."
}
```
### 3. Apply Migrations
```bash
cd CMSMicroservice.WebApi
dotnet ef database update
```
### 4. Configure Notifications (Optional)
See [Email/SMS Configuration Guide](docs/email-sms-configuration-guide.md)
### 5. Run
```bash
dotnet run --urls="http://localhost:5133"
```
### 6. Access Endpoints
- **Health**: http://localhost:5133/health
- **Hangfire Dashboard**: http://localhost:5133/hangfire
- **gRPC**: localhost:5133 (HTTP/2)
---
## 🔧 Configuration
### Email (SMTP)
```json
"Email": {
"Enabled": true,
"SmtpHost": "smtp.gmail.com",
"SmtpPort": 587,
"SmtpUsername": "your-email@gmail.com",
"SmtpPassword": "your-gmail-app-password",
"FromEmail": "noreply@foursat.com",
"FromName": "FourSat CMS",
"EnableSsl": true
}
```
### SMS (Kavenegar)
```json
"Sms": {
"Enabled": true,
"Provider": "Kavenegar",
"KavenegarApiKey": "YOUR_API_KEY",
"Sender": "10008663"
}
```
### Background Worker
```csharp
// Cron: "5 0 * * 0" = Every Sunday at 00:05 UTC
RecurringJob.AddOrUpdate<WeeklyCommissionJob>(
"weekly-commission-calculation",
job => job.ExecuteAsync(CancellationToken.None),
"5 0 * * 0");
```
---
## 🧪 Testing
### Manual Trigger (via API)
```bash
# Trigger weekly calculation immediately
curl -X POST http://localhost:5133/api/admin/trigger-weekly-calculation
# Trigger recurring job now
curl -X POST http://localhost:5133/api/admin/trigger-recurring-job-now
# Get recurring jobs status
curl http://localhost:5133/api/admin/recurring-jobs-status
```
### Health Checks
```bash
curl http://localhost:5133/health # Overall health
curl http://localhost:5133/health/ready # Readiness probe (K8s)
curl http://localhost:5133/health/live # Liveness probe (K8s)
```
---
## 📊 What's Remaining?
### 🏪 Inventory System (Current Focus)
1. **Phase 3: Business Services** (In Progress)
- `IInventoryManagementService` - High-level operations
- `IStockMovementService` - Movement orchestration
- `IWarehouseService` - Warehouse business logic
- `IInventoryReportingService` - Advanced reporting
2. **Phase 4: DTOs & AutoMapper** (Next)
- Request/Response DTOs
- AutoMapper profiles
- Validation rules
3. **Phase 5: API Controllers** (Planned)
- `InventoryController` - REST API
- `WarehouseController` - Warehouse management
- `StockMovementController` - Movement tracking
- Swagger documentation
### 💼 Commission System
1. **Payment Gateway Integration** (Phase 10 - 1 week)
- Daya or Bank Mellat API integration
- IBAN transfer automation
- Admin approval UI in BackOffice
2. **Production Configuration** (30 minutes)
- Gmail App Password setup
- Kavenegar API key registration
- Update `appsettings.Production.json`
### Medium Priority
3. **Club Shop Integration** (Phase 9 - 2 weeks)
- Product catalog for club memberships
- Shopping cart integration
- Auto-activation on purchase
### Low Priority
4. **Testing** (Phase 7 - Postponed)
- Unit tests for business logic
- Integration tests for API
- Load testing for background worker
### Optional Enhancements
- Redis distributed locks (multi-server deployment)
- Sentry error tracking (API key needed)
- Slack notifications (webhook needed)
- FCM push notifications
---
## 🎯 MVP Features (100% Complete)
### 💼 Commission System:
✅ Binary network tree with automatic placement
✅ Club membership (Member/Trial) with different commission rates
✅ Weekly commission calculation (Lesser Leg algorithm)
✅ Background worker with Hangfire (cron scheduling)
✅ Balance carryover logic (rollover unused volumes)
✅ MaxWeeklyBalances cap enforcement
✅ Health check endpoints (Kubernetes-ready)
✅ Manual trigger API (admin control)
✅ Email + SMS notifications (MailKit + Kavenegar)
✅ Retry logic with exponential backoff (Polly)
✅ Audit trail (WorkerExecutionLog, History tables)
✅ Structured logging (AlertService for Sentry/Slack)
✅ JWT authentication context (CurrentUserService)
### 🏪 Inventory System (Phase 2 Complete):
✅ Domain entities (InventoryItem, StockMovement, Warehouse)
✅ Multi-warehouse inventory management
✅ Stock reservation system for orders
✅ 8 movement types with complete audit trail
✅ Repository pattern with 25+ methods per repository
✅ CQRS with 17 commands and 35 queries
✅ 52 MediatR handlers with business logic
✅ Low stock and out-of-stock alerts
✅ Advanced analytics (top products, daily volume)
✅ Bulk operations support
✅ Transaction-safe operations with rollback
✅ DI container configuration
---
## 👥 Team
**Development**: FourSat Team
**Last Updated**: January 2026
---
## 📝 License
Proprietary - FourSat Company
# Multi-remote push enabled
+161
View File
@@ -0,0 +1,161 @@
# 🚀 FourSat Offline Deployment
این دایرکتوری شامل scripts و مستندات برای deploy کردن سرویس‌های FourSat **بدون نیاز به اینترنت** است.
## ⚡ Quick Start
### گزینه 1: Setup خودکار (پیشنهادی)
**روی ماشینی با Docker و اینترنت:**
```bash
cd /home/masoud/Apps/project/FourSat/deployment
./setup-offline-complete.sh
```
این اسکریپت:
- ✅ Images را pull می‌کند
- ✅ Export می‌کند به `.tar`
- ✅ به server transfer می‌کند
- ✅ در containerd server import می‌کند
- ✅ تمام!
### گزینه 2: Setup دستی
اگر Docker روی این ماشین نیست، مستندات کامل را بخوانید:
```bash
cat OFFLINE-SETUP-GUIDE.md
```
---
## 📁 فایل‌های مهم
### 🎯 اسکریپت‌های اصلی
- **`setup-offline-complete.sh`** ⭐ - Setup کامل خودکار (نیاز به Docker)
- **`export-import-images.sh`** - Export/Import دستی images
- **`OFFLINE-SETUP-GUIDE.md`** 📚 - راهنمای کامل قدم به قدم
### 📖 مستندات
- **`README.md`** - این فایل
- **`QUICK-REFERENCE.md`** - مرجع سریع commands
- **`DEPLOYMENT-GUIDE.md`** - راهنمای deployment
- **`SERVER-SETUP-GUIDE.md`** - تنظیمات server
- **`CHANGES-SUMMARY.md`** - لیست تغییرات انجام شده
- **`SETUP-INSTRUCTIONS.md`** - دستورالعمل‌های setup
### 🛠️ اسکریپت‌های کمکی
- `pull-base-images.sh` - Pull کردن base images
- `save-images.sh` - ذخیره images به tar
- `load-images.sh` - بارگذاری images از tar
- `cache-nuget-packages.sh` - Cache کردن NuGet packages
- `build-all-offline.sh` - Build تمام سرویس‌ها offline
- `cleanup.sh` - پاکسازی فایل‌های build
- `test-services.sh` - تست سرویس‌های deploy شده
- `k8s-deploy.sh` - Deploy به Kubernetes
- `k8s-health-check.sh` - بررسی سلامت deployments
- `quick-start.sh` - Setup تعاملی
- `server-cache-images.sh` - Cache images روی server
- `load-cached-images.sh` - بارگذاری cached images
- `verify-cache.sh` - بررسی cache
- `pull-images-to-node.sh` - Pull به Kubernetes node
---
## 🔍 چک‌لیست Setup
- [ ] **Base images در containerd server** (روی node نه pod)
- [ ] **Workflows بدون proxy** تنظیم شده‌اند
- [ ] **DNS = "0.0.0.0"** برای جلوگیری از دسترسی به اینترنت
- [ ] **DOCKER_BUILDKIT=0** برای استفاده از legacy builder
- [ ] **Test یک workflow** برای اطمینان از کار کردن offline
---
## 🏗️ معماری Deployment
```
┌─────────────────────────────────────────┐
│ Kubernetes Cluster (194.5.195.53) │
│ │
│ ┌───────────────────────────────────┐ │
│ │ Node (containerd) │ │
│ │ ├─ mcr.microsoft.com/dotnet/* │ │
│ │ └─ nginx:alpine │ │
│ └───────────────────────────────────┘ │
│ ▲ │
│ │ images available to pods │
│ │ │
│ ┌──────┴──────────────────────────┐ │
│ │ gitea-runner pod │ │
│ │ ├─ dind (Docker-in-Docker) │ │
│ │ └─ runner (Gitea Actions) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
**کلید موفقیت**: Images باید در **containerd روی node** باشند، نه در pod!
---
## 🐛 عیب‌یابی
### ❌ Workflow هنوز download می‌کند؟
```bash
# بررسی images روی server
ssh root@194.5.195.53 "ctr -n k8s.io images list | grep mcr.microsoft.com"
```
اگر خالی است، setup را دوباره اجرا کنید.
### ❌ Build با error می‌خورد؟
```bash
# بررسی logs workflow در Gitea
# اگر "TLS handshake timeout" می‌بینید → DNS هنوز active است
# اگر "image not found" می‌بینید → images در containerd نیستند
```
### ❌ Import به containerd کار نمی‌کند?
```bash
# مطمئن شوید namespace درست است
ssh root@194.5.195.53 "ctr namespaces list"
# باید k8s.io را ببینید
# برای import همیشه از -n k8s.io استفاده کنید:
ctr -n k8s.io images import /path/to/image.tar
```
---
## 📞 Support
مشکل دارید? مستندات کامل را بخوانید:
```bash
# راهنمای کامل setup offline
cat OFFLINE-SETUP-GUIDE.md
# مرجع سریع commands
cat QUICK-REFERENCE.md
# راهنمای deployment
cat DEPLOYMENT-GUIDE.md
```
---
## ✨ ویژگی‌ها
-**کاملاً Offline** - هیچ download در حین build
-**خودکار** - یک اسکریپت برای setup کامل
-**مستند شده** - راهنماهای جامع فارسی
-**تست شده** - روی Kubernetes با k3s
-**سریع** - build بدون تاخیر network
---
**نوشته شده برای پروژه FourSat** 🛰️
+311
View File
@@ -0,0 +1,311 @@
# 📋 تاریخچه تغییرات FrontOffice — کارا بازار سلامت
> **آخرین بروزرسانی**: اسفند ۱۴۰۴ (فوریه ۲۰۲۶)
> **فریمورک**: Blazor Server + MudBlazor 8.14 + .NET 9
---
## 🔖 نسخه ۲.۷.۰ — اسفند ۱۴۰۴ (February 17, 2026)
### ۱. اجبار تخفیف ۱۰۰٪ و حذف اسلایدر
- **حذف** `MudSlider` و `MudNumericField` از `Checkout.razor` — کاربر دیگه درصد تخفیف انتخاب نمی‌کنه
- **حذف** کامل بخش نمایش موجودی اعتباری
- بکند همیشه حداکثر تخفیف (`MaxDiscountPercent`) رو اعمال می‌کنه
### ۲. رفع متن Badge تخفیف
- **مشکل:** Badge روی محصولات "۱۰۰٪ اعتباری" نشون می‌داد — گمراه‌کننده
- **رفع:** نمایش درصد واقعی محصول (مثلاً "۳۰٪ تخفیف")
- **فایل‌ها:** `Products.razor`, `Cart.razor`, `ProductDetail.razor`
### ۳. نمایش جدول مالیات (VAT) در Checkout
- جمع کل، تخفیف، مبلغ پس از تخفیف، مالیات ۹٪، مبلغ قابل پرداخت
- استفاده از `VatCalculator` برای محاسبه VAT
### ۴. نمایش وضعیت پرداخت در سفارشات
- **فیلد جدید** `payment_status` در proto `discountorder.proto` (v0.0.179)
- **صفحه Orders:** نمایش Chip رنگی بر اساس PaymentStatus (موفق/ناموفق/در انتظار)
- **صفحه OrderDetail:** Alert برای سفارشات ناموفق + دکمه بازگشت + جزئیات وضعیت
- **Helper متدها:** `GetPaymentStatusText()`, `GetPaymentStatusColor()`, `GetDeliveryStatusText()`
### ۵. فیکس مپینگ PaymentStatus و DeliveryStatus
- **مشکل:** Domain enum مقادیر متفاوتی از Proto داشت — مستقیم cast می‌شد
- **رفع:** `MapPaymentStatus()` و `MapDeliveryStatus()` در `DiscountOrderService.cs` (WebApi)
- سفارشات ناموفق حالا `DeliveryStatus=Cancelled` دارن (بجای "در حال پردازش")
### ۶. فیکس Proto به NuGet v0.0.179
- `FrontOffice.Main.csproj`: `PackageReference` به `Foursat.CMSMicroservice.Protobuf` v0.0.179
---
## 🔖 نسخه ۲.۶.۰ — بهمن ۱۴۰۴ (February 16, 2026)
### ۱. درگاه پرداخت زرین‌پال
- **یکپارچه‌سازی ZarinPal** — پرداخت مستقیم بدون PYMS واسط
- صفحه checkout فروشگاه اعتباری: ریدایرکت به ZarinPal → callback → تأیید
- صفحه checkout فروشگاه عادی: همان جریان
- پشتیبانی از sandbox و production
### ۲. جدول PaymentTransaction
- **Entity جدید** `PaymentTransaction` — ذخیره جزئیات سطح درگاه (Authority, CardPan, CardHash, RefId)
- جدا از جدول Transaction اصلی
- Migration: `AddPaymentTransactionTable`
### ۳. فیکس نمایش وضعیت پرداخت
- **مشکل:** سفارشات اعتباری «در انتظار پرداخت» نشان می‌دادند حتی بعد از پرداخت موفق
- **علت:** Mapster نمی‌تونست `PaymentStatus` (enum) رو به `payment_completed` (bool) مپ کنه
- **رفع:** مپینگ دستی در `DiscountOrderService`
### ۴. فیکس DeliveryStatus
- **مشکل:** فروشگاه اعتباری بعد از پرداخت `InTransit` ست می‌کرد
- **رفع:** تغییر به `Pending` — ادمین باید وضعیت پست رو مشخص کنه
### ۵. تغییر به NuGet Package (Docker build fix)
- `ProjectReference` به CMS Protobuf → `PackageReference` (v0.0.178)
- Docker build context فقط `src/` داره → مسیر `../../../CMS` قابل دسترسی نیست
---
## 🔖 نسخه ۲.۵.۰ — بهمن ۱۴۰۴
### ۱. محافظت صفحات نیازمند احراز هویت (Auth Guard)
**فایل‌های تغییریافته:**
- `Shared/MainLayout.razor.cs`
**شرح:**
قبلاً هیچ محافظتی در سطح مسیریابی وجود نداشت — کاربر غیرلاگین می‌توانست مستقیماً به `/profile/*`، `/commission/*`، `/cart` و... دسترسی پیدا کند.
**تغییرات:**
- متد `EnforceAuthGuardAsync()` اضافه شد — در هر تغییر مسیر و اولین بار رندر اجرا می‌شود
- متد `IsProtectedRoute(path)` مسیرهای محافظت‌شده را تشخیص می‌دهد
- کاربر غیرلاگین → ریدایرکت به `/` (صفحه اصلی)
**مسیرهای محافظت‌شده:**
| گروه | مسیرها |
|---|---|
| پروفایل | `/profile/*` |
| کمیسیون | `/commission/*` |
| شبکه | `/network/*` |
| باشگاه | `/club/*` |
| سبد خرید | `/cart`, `/checkout*`, `/orders`, `/order/*`, `/order-tracking/*` |
| پکیج‌ها | `/my-packages` |
| دروازه | `/my-orders`, `/my-cart` |
| فروشگاه اعتباری | `/discount-store/cart`, `/discount-store/checkout`, `/discount-store/orders`, `/discount-store/order/*` |
**مسیرهای عمومی:**
`/`, `/register`, `/about`, `/faq`, `/contact`, `/blog/*`, `/packages`, `/package/*`, `/products`, `/product/*`, `/categories`, `/stores`, `/discount-store`, `/discount-store/product/*`
---
### ۲. استخراج کامپوننت PhoneVerifyForm
**فایل‌های جدید:**
- `Shared/PhoneVerifyForm.razor`
- `Shared/PhoneVerifyForm.razor.cs`
**فایل‌های تغییریافته:**
- `Shared/AuthDialog.razor`
- `Shared/AuthDialog.razor.cs`
**شرح:**
فرم تلفن + تایید OTP + کپچا که قبلاً به‌صورت `RenderFragment` با `__builder` مستقیم در `AuthDialog` نوشته شده بود، به کامپوننت مستقل `PhoneVerifyForm` استخراج شد.
**ساختار قبل:**
```
AuthDialog.razor
└── @code { PhoneOrVerifyContent() => __builder => { ... } } ← RenderFragment پیچیده
```
**ساختار بعد:**
```
AuthDialog.razor
└── <PhoneVerifyForm @ref="_phoneVerifyForm" ... /> ← کامپوننت مستقل
PhoneVerifyForm.razor ← مارکاپ فرم
PhoneVerifyForm.razor.cs ← پارامترها و فرم رف‌ها
```
**پارامترهای PhoneVerifyForm:**
| پارامتر | نوع | توضیح |
|---|---|---|
| `CurrentStep` | `AuthStep` | مرحله فعلی (Phone / Verify) |
| `PhoneRequest` | `CreateNewOtpTokenRequest` | مدل فرم تلفن |
| `VerifyRequest` | `VerifyOtpTokenRequest` | مدل فرم تایید |
| `CaptchaCode` | `string?` | کد کپچا نمایش‌داده‌شده |
| `CaptchaInput` / `CaptchaInputChanged` | `string?` + `EventCallback` | ورودی کپچا (two-way) |
| `OnRefreshCaptcha` | `EventCallback` | رفرش کپچا |
| `IsBusy` | `bool` | وضعیت بارگذاری |
| `ErrorMessage` / `InfoMessage` | `string?` | پیام‌های خطا/اطلاع |
| `PhoneNumber` | `string?` | شماره تایید‌شده |
| `ResendRemaining` | `int` | ثانیه تا ارسال مجدد |
| `OnChangePhone` / `OnResendOtp` | `EventCallback` | اکشن‌های تایید |
**نکته مهم — Two-way binding کپچا:**
فیلد کپچا با `Value` + `ValueChanged` بایند شده (نه `@bind-Value`) تا مقدار تایپ‌شده از فرزند به والد برگردد:
```razor
<MudTextField Value="CaptchaInput"
ValueChanged="@((string v) => CaptchaInputChanged.InvokeAsync(v))" ... />
```
**دسترسی به فرم‌ها از والد:**
```csharp
// AuthDialog.razor.cs
var phoneForm = _phoneVerifyForm?.GetPhoneForm();
var verifyForm = _phoneVerifyForm?.GetVerifyForm();
```
---
### ۳. بهبود لایوت مدال ورود (AuthDialog)
**فایل‌های تغییریافته:**
- `Shared/AuthDialog.razor`
- `Utilities/AuthDialogService.cs`
- `wwwroot/css/site.css`
**مشکلات قبلی:**
- دیالوگ روی موبایل `FullScreen` بود → فضای خالی بزرگ بین فرم و دکمه‌ها
- `TitleContent` و `DialogActions` جدا → گپ عمودی
- ردیف کپچا با `MudStack Row="true"` → آیتم‌ها عمودی رندر می‌شدند
**تغییرات:**
1. **حذف FullScreen**: `AuthDialogService` حالا همیشه `MaxWidth.ExtraSmall, FullWidth=true, CloseButton=true`
2. **ادغام محتوا**: آواتار + عنوان + فرم + دکمه‌ها همه داخل `DialogContent` → بدون `TitleContent` و `DialogActions`
3. **کلاس `auth-dialog-wrapper`**: CSS با `.auth-dialog-wrapper .mud-dialog-title { display: none; }` عنوان پیش‌فرض دیالوگ رو مخفی می‌کنه
4. **ردیف کپچا**: از `MudStack Row` به `div.captcha-row` با CSS flex اختصاصی
5. **حالت Inline**: بدون تغییر ساختاری — فقط از `PhoneVerifyForm` استفاده می‌کنه
**CSS جدید:**
```css
.auth-dialog-wrapper .mud-dialog-title { display: none; }
.auth-dialog-wrapper .mud-dialog-content { padding-bottom: 24px !important; }
.auth-dialog-content { max-width: 400px; margin: 0 auto; }
.captcha-row { display: flex; align-items: center; gap: 10px; }
.captcha-row > .mud-input-control { flex: 1 1 0; min-width: 0; }
.captcha-row > .mud-paper { flex: 0 0 auto; }
.captcha-row > .mud-button-root { flex: 0 0 auto; }
```
---
### ۴. صفحه لندینگ — حذف /pricing و اضافه بنر بلاگ
**فایل‌های تغییریافته:**
- `Pages/Index.razor`
- `wwwroot/css/site.css`
**تغییرات:**
1. دکمه هیرو «مشاهده پکیج‌ها» → **«آخرین اخبار»** با لینک `/blog`
2. **بنر جدیدترین مطلب** بین هیرو و «سه گام تا شروع» اضافه شد
- تصویر بندانگشتی + عنوان + خلاصه + آیکون شیشه‌ای
- هاور: `translateY(-2px)` + سایه بزرگ‌تر
**CSS جدید:**
```css
.landing-blog-banner { border: 1px solid var(--mud-palette-divider); transition: ... }
.landing-blog-banner:hover { box-shadow: var(--mud-elevation-4); transform: translateY(-2px); }
.landing-blog-thumb { width: 80px; height: 80px; border-radius: 12px; }
.landing-blog-title { -webkit-line-clamp: 1; font-weight: 600; }
.landing-blog-summary { -webkit-line-clamp: 1; }
```
---
### ۵. افزایش ارتفاع تکست‌باکس‌ها (Global)
**فایل تغییریافته:**
- `wwwroot/css/site.css`
**قبل:** `padding: 10px 14px`
**بعد:** `padding: 14px 14px` + `font-size: 1rem`
```css
.mud-input-outlined .mud-input-slot {
padding: 14px 14px !important;
font-size: 1rem;
}
```
تمام فیلدهای Outlined در کل اپلیکیشن بزرگ‌تر شدند.
---
### ۶. اصلاح RTL فیلدهای ورودی
**فایل تغییریافته:**
- `wwwroot/css/site.css`
**مشکل:** فیلدهای `type="tel"` به‌صورت پیش‌فرض مرورگر `direction: ltr` می‌گیرن — لیبل سمت راست ولی placeholder/cursor سمت چپ.
**اصلاح:**
```css
.mud-input-slot input,
.mud-input-slot textarea {
direction: rtl !important;
text-align: right !important;
}
```
---
### ۷. اصلاح captcha-box CSS
**فایل تغییریافته:**
- `wwwroot/css/site.css`
**قبل:**
```css
.captcha-box {
min-width: 120px; min-height: 56px;
background: linear-gradient(135deg, rgba(123,97,255,.12), rgba(255,140,189,.12));
}
```
**بعد:**
```css
.captcha-box {
min-width: 96px; min-height: 48px;
border-radius: var(--mud-default-borderradius);
background: rgba(99,102,241,.08);
border: 1px solid var(--mud-palette-divider);
}
```
---
## 📁 نقشه فایل‌ها
```
Shared/
├── AuthDialog.razor ← بازنویسی (کامپوننت PhoneVerifyForm جایگزین RenderFragment)
├── AuthDialog.razor.cs ← بروزرسانی (استفاده از _phoneVerifyForm)
├── PhoneVerifyForm.razor ← ✨ جدید (فرم تلفن + تایید + کپچا)
├── PhoneVerifyForm.razor.cs ← ✨ جدید (پارامترها و فرم رف‌ها)
├── MainLayout.razor.cs ← Auth Guard اضافه شد
Pages/
├── Index.razor ← /pricing → /blog + بنر بلاگ
Utilities/
├── AuthDialogService.cs ← حذف FullScreen، ثابت‌سازی سایز
wwwroot/css/
├── site.css ← captcha-row, auth-dialog, RTL fix, input height
```
---
## 🔍 وضعیت بیلد
| تاریخ | خطا | هشدار | توضیح |
|---|---|---|---|
| بهمن ۱۴۰۴ | **۰** | ۱۰۵ | MUD0002 warnings (بی‌خطر — مربوط به MudBlazor analyzer) |
+542
View File
@@ -0,0 +1,542 @@
# 🎨 پلن جامع یکپارچه‌سازی UI/UX — کارا بازار سلامت
> **تاریخ**: بهمن ۱۴۰۴
> **وضعیت**: ✅ **تمام ۶ فاز + فاز ۷ (ناوبری و امنیت) تکمیل شد**
> **هدف**: یکپارچه‌سازی طراحی FrontOffice با ۲۰٪ تغییر UX و ۵۰٪ تغییر UI
> **فریمورک**: Blazor Server + MudBlazor 8.14
> **📋 تاریخچه تغییرات جزئی**: [CHANGELOG.md](CHANGELOG.md)
---
## 📊 خلاصه اجرایی
| شاخص | وضعیت قبل | وضعیت بعد |
|---|---|---|
| **صفحات کل** | ۴۱ صفحه + ۶ کامپوننت مشترک | ۴۱ صفحه + ۸ کامپوننت مشترک |
| **الگوی PageHeader** | ۱۹ صفحه از ۴۱ | ۳۲+ صفحه ✅ |
| **Inline Style سنگین** | ۱۱ صفحه 🔴 | ≤۲ صفحه (فقط gradient‌های تزئینی) ✅ |
| **Loading State** | ۲ الگوی متفاوت (Circular vs Linear) | `<LoadingState>` واحد ✅ |
| **Empty State** | ۴+ الگوی متناقض | `<EmptyState>` واحد ✅ |
| **Container Spacing** | ۶ الگوی متناقض | `py-6` استاندارد ✅ |
| **رنگ‌بندی** | رنگ‌های hardcoded در ۸+ صفحه | CSS Variable ✅ |
| **تم پالت** | Primary `#0380C0` (آبی ساده) | Indigo `#6366f1` + Full PaletteDark ✅ |
| **Elevation** | مخلوط ۲/۳/۴ | استاندارد ۰–۲ ✅ |
| **Build** | ۰ خطا | ۰ خطا ✅ |
### فازها
| فاز | عنوان | وضعیت |
|---|---|---|
| ۱ | زیرساخت دیزاین سیستم | ✅ تکمیل |
| ۲ | صفحات پروفایل | ✅ تکمیل |
| ۳ | صفحات تخصصی | ✅ تکمیل |
| ۴ | بهبود بصری فروشگاه | ✅ تکمیل |
| ۵ | صفحات عمومی | ✅ تکمیل |
| ۶ | پالیش و تست | ✅ تکمیل |
| ۷ | ناوبری، امنیت و بازسازی کامپوننت‌ها | ✅ تکمیل |
---
## 🔍 بخش ۱: تحلیل ضعف‌های جاری
### ۱.۱ ناسازگاری‌های ساختاری (Structural)
#### ❌ ۱.۱.۱ — PageHeader دوگانه
**مشکل**: نیمی از صفحات از `<PageHeader>` استفاده می‌کنند، نیم دیگر header دستی دارند.
| از `<PageHeader>` استفاده می‌کنند ✅ | Header دستی دارند ❌ |
|---|---|
| Store/* (۸ صفحه) | Profile/* (۷ صفحه) |
| DiscountStore/* (۶ صفحه) | Club/* (۲ صفحه) |
| Gateway/* (۳ صفحه) | Commission/* (۲ صفحه) |
| Package/* (۲ صفحه) | Network/* (۱ صفحه) |
| PackageDetail, Checkout | Blog/*, Index, About, Contact, FAQ |
**تأثیر**: ظاهر متفاوت دکمه بازگشت، فاصله‌بندی ناهماهنگ
#### ❌ ۱.۱.۲ — Container MaxWidth متناقض
```
MaxWidth.Large → اکثر صفحات
MaxWidth.Medium → Personal, Settings, OrderTracking, Blog/Post, Gateway/*
MaxWidth.Small → ChangePassword
ترکیبی (loading≠content) → OrderDetail, ProductDetail, PackageDetail
```
**تأثیر**: برخی صفحات پهن‌تر از حد نیاز هستند (مثلاً فرم‌های ساده با Large)
#### ❌ ۱.۱.۳ — Container Padding متناقض
```
py-6 → اکثریت (استاندارد)
py-8 → Package/Packages, Package/MyPackages
pa-2 pa-md-6 → Store/Products, DiscountStore/Products
py-6 py-md-10 → Gateway/*
py-4 py-md-6 → DiscountStore/ProductDetail
py-16 سکشنی → Index, About, Contact, FAQ
```
**قاعده پیشنهادی**: `py-6` برای صفحات داخلی، section-based برای صفحات عمومی
---
### ۱.۲ ناسازگاری‌های بصری (Visual)
#### ❌ ۱.۲.۱ — Inline Style سنگین (۱۱ صفحه)
| صفحه | نمونه مشکل‌دار |
|---|---|
| **Index.razor** | `Style="color:#fff; font-size:clamp(1.6rem,4.5vw,2.4rem);"` |
| **Profile/Index** | `Style="background:rgba(99,102,241,.12);"` |
| **Store/ProductDetail** | `style="width:100%;height:360px;background-image:url(...);"` |
| **PackageDetail** | `Style="background: radial-gradient(600px 280px..."` |
| **Checkout** | `Style="background: radial-gradient(..."` + `Elevation="4"` |
| **Blog/Index** | `Style="color:#fff; font-weight:700;"`, `Style="font-size:3.5rem;"` |
| **Blog/Post** | `Style="width:100%; height:100%;"`, `Style="font-size:4rem;"` |
| **About** | `Style="background: radial-gradient(...);"` |
| **WeeklyBalance** | `Style="background: linear-gradient(135deg, #e8f5e9..."` |
| **Gateway/**** | `Style="background:rgba(16,185,129,.12)..."`, `Style="color:#10b981;"` |
| **DiscountStore/ProductDetail** | `Style="background:rgba(16,185,129,.06)..."` |
#### ❌ ۱.۲.۲ — رنگ‌های Hardcoded
| رنگ | استفاده | باید باشد |
|---|---|---|
| `#10b981` | Gateway (سبز فروشگاه) | `var(--ds-color-store)` |
| `#ef4444` | Gateway (قرمز اعتباری) | `var(--ds-color-discount)` |
| `#6366f1`, `#818cf8`, `#a78bfa` | Hero/Banner gradients | `var(--ds-gradient-primary)` |
| `rgba(99,102,241,.12)` | Dashboard backgrounds | `var(--ds-primary-soft)` |
| `rgba(16,185,129,.06)` | Discount product highlights | `var(--ds-success-soft)` |
| `#e8f5e9`, `#c8e6c9` | WeeklyBalance stat cards | `var(--ds-success-gradient)` |
#### ❌ ۱.۲.۳ — Elevation ناهماهنگ
```
Elevation="0" → Blog cards (با border)
Elevation="1" → برخی صفحات
Elevation="2" → اکثر صفحات (استاندارد)
Elevation="3" → WeeklyBalance stat cards
Elevation="4" → Checkout.razor
```
**قاعده پیشنهادی**: `Elevation="0"` با `border` = کارت‌های اطلاعاتی، `Elevation="2"` = default
#### ❌ ۱.۲.۴ — Paper Rounding ناهماهنگ
```
rounded-lg (16px) → اکثریت
rounded-xl (20px) → Index, Profile/Index, Gateway, DiscountStore/ProductDetail
بدون class → design system !important → 12px
```
---
### ۱.۳ ناسازگاری‌های UX (تجربه کاربری)
#### ❌ ۱.۳.۱ — Loading State دوگانه
```
MudProgressCircular → Store/*, DiscountStore/*, Package/*, About, Blog, Addresses
MudProgressLinear → Club/*, Commission/*, Network/*, RegisterWizard
```
**مشکل**: کاربر دو تجربه مختلف «در حال بارگذاری» می‌بیند
#### ❌ ۱.۳.۲ — Empty State ناهماهنگ
```
MudAlert Severity.Info → Store/Orders, Categories, DiscountStore/Orders
Icon + Text + Button → Addresses, Package/MyPackages, PackageDetail
MudAlert Severity.Warning → WithdrawalRequests
Custom dashed-border paper → Blog/Index
```
#### ❌ ۱.۳.۳ — Routing Directive ناهماهنگ
```
@attribute [Route(RouteConstants...)] → ۳۹ صفحه ✅
@page "/categories" → Categories.razor ❌
@page "/blog/{Slug}" → Blog/Post.razor ❌
```
#### ❌ ۱.۳.۴ — PackageDetail Loading/Error بدون Container
Loading و Error state در `PackageDetail.razor` بدون `MudContainer` رندر می‌شوند → محتوا تمام‌عرض نمایش می‌یابد.
#### ❌ ۱.۳.۵ — تم فعلی کم‌رنگ
```csharp
// CustomMudTheme.cs فعلی
Primary = "#0380C0" // آبی ساده
// بدون Secondary، Tertiary، Info، Warning تعریف‌شده
// بدون PaletteDark
// بدون LayoutProperties
```
**مشکلات**:
- فقط Primary تعریف شده، بقیه رنگ‌ها default MudBlazor
- Dark mode بدون palette اختصاصی
- بدون `DefaultBorderRadius`، `AppbarHeight` و غیره
- تناقض بین Primary `#0380C0` و gradient‌های CSS با `#6366f1`
---
## 🎯 بخش ۲: معماری دیزاین سیستم هدف
### ۲.۱ سلسله‌مراتب صفحات
```
┌─────────────────────────────────────────────┐
│ MainLayout │
│ ├─ AppBar (fixed, transparent) │
│ ├─ MudMainContent │
│ │ ├─ [Public Pages] → Section-based │
│ │ │ (Index, About, Contact, FAQ, Blog) │
│ │ └─ [Internal Pages] → Container-based │
│ │ ├─ <PageHeader/> │
│ │ ├─ Content (MudStack/MudGrid) │
│ │ └─ </MudContainer> │
│ ├─ Footer (hidden on mobile) │
│ └─ BottomNav (mobile only) │
└─────────────────────────────────────────────┘
```
### ۲.۲ قواعد واحد (Single Source of Truth)
| قاعده | مقدار |
|---|---|
| **Container MaxWidth** | `Large` = لیست/گرید, `Medium` = فرم/جزئیات, `Small` = تک‌فرم ساده |
| **Container Spacing** | `py-6` صفحات داخلی, section-based صفحات عمومی |
| **Paper Elevation** | `0` با border = کارت اطلاعاتی, `2` = default |
| **Paper Rounding** | `rounded-lg` = default, `rounded-xl` = hero/banner |
| **Loading State** | `<LoadingState/>` component واحد |
| **Empty State** | `<EmptyState/>` component واحد |
| **Page Header** | `<PageHeader/>` در تمام صفحات داخلی |
| **Content Wrapper** | `MudStack Spacing="3"` بعد از PageHeader |
### ۲.۳ CSS Variables هدف
```css
:root {
/* ── Brand Colors ── */
--ds-brand-primary: #6366f1; /* Indigo — هویت اصلی */
--ds-brand-secondary: #8b5cf6; /* Purple */
--ds-brand-accent: #a78bfa; /* Light purple */
/* ── Semantic Colors ── */
--ds-color-store: #10b981; /* فروشگاه عادی */
--ds-color-discount: #ef4444; /* فروشگاه اعتباری */
--ds-color-success: #10b981;
--ds-color-warning: #f59e0b;
--ds-color-error: #ef4444;
--ds-color-info: #3b82f6;
/* ── Soft Backgrounds ── */
--ds-primary-soft: rgba(99,102,241,.08);
--ds-success-soft: rgba(16,185,129,.08);
--ds-error-soft: rgba(239,68,68,.08);
--ds-warning-soft: rgba(245,158,11,.08);
/* ── Gradients ── */
--ds-gradient-primary: linear-gradient(135deg, #6366f1 0%, #818cf8 50%, #a78bfa 100%);
--ds-gradient-store: linear-gradient(135deg, #10b981 0%, #34d399 100%);
--ds-gradient-discount: linear-gradient(135deg, #ef4444 0%, #f97316 50%, #f59e0b 100%);
--ds-gradient-success: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
/* ── Spacing (existing) ── */
--ds-radius-sm: 8px;
--ds-radius-md: 12px;
--ds-radius-lg: 16px;
--ds-radius-xl: 20px;
--ds-transition: 0.2s ease;
--ds-shadow-sm: 0 1px 3px rgba(0,0,0,.06);
--ds-shadow-md: 0 4px 12px rgba(0,0,0,.08);
--ds-shadow-lg: 0 8px 24px rgba(0,0,0,.10);
}
```
---
## 🚀 بخش ۳: فازبندی اجرا
### 🔷 فاز ۱ — زیرساخت دیزاین سیستم (UI ~15%)
> **اولویت**: بالا | **ریسک**: پایین | **حجم**: ۶ فایل
| # | تسک | فایل | نوع تغییر |
|---|---|---|---|
| 1.1 | ارتقاء `CustomMudTheme.cs` — اضافه کردن PaletteDark، LayoutProperties، رنگ‌های Secondary/Tertiary/Info، تغییر Primary به `#6366f1` | `CustomMudTheme.cs` | UI |
| 1.2 | توسعه CSS Variables — اضافه کردن brand colors، semantic colors، soft backgrounds، gradients | `site.css` | UI |
| 1.3 | ساخت `<LoadingState>` component واحد | `Shared/LoadingState.razor` (جدید) | UX |
| 1.4 | ساخت `<EmptyState>` component واحد | `Shared/EmptyState.razor` (جدید) | UX |
| 1.5 | بهبود `<PageHeader>` — اضافه کردن آیکون، subtitle اختیاری | `Shared/PageHeader.razor` | UI |
| 1.6 | اضافه کردن `.page-container` CSS pattern | `site.css` | UI |
---
### 🔷 فاز ۲ — یکپارچه‌سازی صفحات Profile (UI ~10%, UX ~5%)
> **اولویت**: بالا | **ریسک**: پایین | **حجم**: ۹ فایل
| # | تسک | تغییرات |
|---|---|---|
| 2.1 | `Profile/Personal` → جایگزینی header دستی با `<PageHeader>` | UX |
| 2.2 | `Profile/Addresses``<PageHeader>` + `<LoadingState>` + `<EmptyState>` | UX |
| 2.3 | `Profile/Wallet``<PageHeader>` | UX |
| 2.4 | `Profile/Settings``<PageHeader>` | UX |
| 2.5 | `Profile/Tree``<PageHeader>` | UX |
| 2.6 | `Profile/WithdrawalRequests``<PageHeader>` + `<EmptyState>` | UX |
| 2.7 | `Profile/ChangePassword``<PageHeader>` | UX |
| 2.8 | `Profile/Index` (Dashboard) → حذف inline styles، استفاده از CSS Variables | UI |
| 2.9 | `Club/MembershipPage` + `Club/FeaturesPage``<PageHeader>` + `<LoadingState>` | UX |
---
### 🔷 فاز ۳ — یکپارچه‌سازی صفحات تخصصی (UI ~5%, UX ~5%)
> **اولویت**: متوسط | **ریسک**: پایین | **حجم**: ۵ فایل
| # | تسک | تغییرات |
|---|---|---|
| 3.1 | `Commission/Dashboard``<PageHeader>` + `<LoadingState>` | UX |
| 3.2 | `Commission/WeeklyBalance``<PageHeader>` + حذف inline gradient styles | UI + UX |
| 3.3 | `Network/NetworkStatistics``<PageHeader>` + `<LoadingState>` | UX |
| 3.4 | `PackageDetail` → wrap loading/error در `MudContainer` | UX bug fix |
| 3.5 | `Checkout` → Elevation=4→2، حذف inline radial-gradient | UI |
---
### 🔷 فاز ۴ — بهبود بصری فروشگاه‌ها (UI ~10%)
> **اولویت**: متوسط | **ریسک**: پایین | **حجم**: ۶ فایل
| # | تسک | تغییرات |
|---|---|---|
| 4.1 | `Store/Products` → حذف inline hero styles، استفاده از CSS class | UI |
| 4.2 | `Store/ProductDetail` → حذف inline image styles، ساخت `.product-image-main` CSS | UI |
| 4.3 | `DiscountStore/ProductDetail` → حذف hardcoded rgba، استفاده از `--ds-success-soft` | UI |
| 4.4 | `Gateway/*` (۳ صفحه) → حذف hardcoded `#10b981`/`#ef4444`، استفاده از `--ds-color-store`/`--ds-color-discount` | UI |
| 4.5 | یکسان‌سازی Elevation → `0` با border یا `2` | UI |
| 4.6 | یکسان‌سازی Container spacing → `py-6` | UI |
---
### 🔷 فاز ۵ — بهبود صفحات عمومی و بلاگ (UI ~10%)
> **اولویت**: پایین | **ریسک**: پایین | **حجم**: ۷ فایل
| # | تسک | تغییرات |
|---|---|---|
| 5.1 | `Index.razor` → حذف inline styles از hero، استفاده از CSS class | UI |
| 5.2 | `About.razor` → حذف inline radial-gradient | UI |
| 5.3 | `Contact.razor` → cleanup minor inline styles | UI |
| 5.4 | `FAQ.razor` → cleanup minor inline styles | UI |
| 5.5 | `Blog/Index` → حذف inline hero styles، استفاده از CSS class | UI |
| 5.6 | `Blog/Post` → حذف inline styles از hero image و typography | UI |
| 5.7 | `RegisterWizard` → بهینه‌سازی wizard-section dark mode | UI |
---
### 🔷 فاز ۶ — Polish نهایی و فرآیندی (UX ~10%)
> **اولویت**: پایین | **ریسک**: بسیار پایین | **حجم**: ۴ فایل + تست
| # | تسک | تغییرات |
|---|---|---|
| 6.1 | Fix routing inconsistency — Categories + Blog/Post | UX |
| 6.2 | MudSnackbar notifications styling | UI |
| 6.3 | Dialog styling consistency (AuthDialog, AddressDialogs) | UI |
| 6.4 | Micro-interactions — button press, card hover, page transition | UI |
| 6.5 | تست کامل Dark Mode در تمام صفحات | UI + QA |
| 6.6 | تست Mobile Responsive در تمام صفحات | UX + QA |
---
## 📈 بخش ۴: جدول تأثیرگذاری
### تأثیر UI (هدف ~۵۰٪ تغییر)
| حوزه | تعداد فایل | درصد تأثیر |
|---|---|---|
| Theme + CSS Variables | ۲ | ۱۵% (تأثیر سراسری) |
| حذف Inline Styles | ۱۱ | ۱۵% |
| یکسان‌سازی Elevation/Rounding | ۲۰+ | ۱۰% |
| بهبود رنگ‌بندی (CSS Variables) | ۸ | ۵% |
| Micro-interactions | سراسری | ۵% |
| **جمع** | | **~۵۰%** |
### تأثیر UX (هدف ~۲۰٪ تغییر)
| حوزه | تعداد فایل | درصد تأثیر |
|---|---|---|
| PageHeader یکپارچه | ۱۲ صفحه | ۸% |
| LoadingState واحد | ۱۵+ صفحه | ۴% |
| EmptyState واحد | ۸+ صفحه | ۳% |
| Container fixes (PackageDetail) | ۲ | ۲% |
| Routing consistency | ۲ | ۱% |
| Flow improvements | ۲ | ۲% |
| **جمع** | | **~۲۰%** |
---
## 🔧 بخش ۵: مشخصات فنی کامپوننت‌های جدید
### ۵.۱ LoadingState Component
```razor
@* Shared/LoadingState.razor *@
<MudStack AlignItems="AlignItems.Center" Class="py-16">
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
@if (!string.IsNullOrWhiteSpace(Message))
{
<MudText Typo="Typo.body1" Class="mud-text-secondary mt-2">@Message</MudText>
}
</MudStack>
@code {
[Parameter] public string Message { get; set; } = "در حال بارگذاری...";
}
```
### ۵.۲ EmptyState Component
```razor
@* Shared/EmptyState.razor *@
<MudStack AlignItems="AlignItems.Center" Class="py-12" Spacing="3">
<MudIcon Icon="@Icon" Size="Size.Large" Color="Color.Default" Class="mud-text-disabled" />
<MudText Typo="Typo.h6" Class="mud-text-secondary">@Title</MudText>
@if (!string.IsNullOrWhiteSpace(Description))
{
<MudText Typo="Typo.body2" Class="mud-text-secondary" Style="max-width:400px; text-align:center;">
@Description
</MudText>
}
@if (!string.IsNullOrWhiteSpace(ActionText))
{
<MudButton Variant="Variant.Filled" Color="Color.Primary"
Href="@ActionHref" OnClick="@OnAction" Class="mt-2">
@ActionText
</MudButton>
}
</MudStack>
@code {
[Parameter] public string Icon { get; set; } = Icons.Material.Filled.Inbox;
[Parameter] public string Title { get; set; } = "موردی یافت نشد";
[Parameter] public string? Description { get; set; }
[Parameter] public string? ActionText { get; set; }
[Parameter] public string? ActionHref { get; set; }
[Parameter] public EventCallback OnAction { get; set; }
}
```
### ۵.۳ PageHeader ارتقاء‌یافته
```razor
@* Shared/PageHeader.razor — ارتقاء‌یافته *@
<div class="page-header">
<MudStack Spacing="0">
<MudText Typo="Typo.h5">@Title</MudText>
@if (!string.IsNullOrWhiteSpace(Subtitle))
{
<MudText Typo="Typo.body2" Class="mud-text-secondary">@Subtitle</MudText>
}
</MudStack>
@if (!string.IsNullOrWhiteSpace(BackHref))
{
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack"
Href="@BackHref">بازگشت</MudButton>
}
else
{
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack"
OnClick="GoBack">بازگشت</MudButton>
}
</div>
@code {
[Parameter] public string Title { get; set; } = "";
[Parameter] public string? Subtitle { get; set; }
[Parameter] public string? BackHref { get; set; }
[Inject] private IJSRuntime JS { get; set; } = default!;
private async Task GoBack() => await JS.InvokeVoidAsync("history.back");
}
```
---
## ✅ بخش ۶: چک‌لیست تکمیل هر فاز
### فاز ۱ چک‌لیست:
- [ ] `CustomMudTheme.cs` — Primary→`#6366f1`, PaletteDark اضافه شد
- [ ] `site.css` — CSS Variables جدید (brand, semantic, soft, gradient)
- [ ] `Shared/LoadingState.razor` — ساخته و تست شد
- [ ] `Shared/EmptyState.razor` — ساخته و تست شد
- [ ] `Shared/PageHeader.razor` — Subtitle parameter اضافه شد
- [ ] `.page-container` CSS pattern اضافه شد
- [ ] Build: 0 errors ✅
- [ ] Dark Mode: صحیح ✅
- [ ] Mobile: صحیح ✅
### فاز ۲ چک‌لیست:
- [ ] `Profile/Personal``<PageHeader>`
- [ ] `Profile/Addresses``<PageHeader>` + `<LoadingState>` + `<EmptyState>`
- [ ] `Profile/Wallet``<PageHeader>`
- [ ] `Profile/Settings``<PageHeader>`
- [ ] `Profile/Tree``<PageHeader>`
- [ ] `Profile/WithdrawalRequests``<PageHeader>` + `<EmptyState>`
- [ ] `Profile/ChangePassword``<PageHeader>`
- [ ] `Profile/Index` → inline styles → CSS
- [ ] `Club/*``<PageHeader>` + `<LoadingState>`
- [ ] Build: 0 errors ✅
### فاز ۳ چک‌لیست:
- [ ] `Commission/*``<PageHeader>` + `<LoadingState>`
- [ ] `Network/*``<PageHeader>` + `<LoadingState>`
- [ ] `PackageDetail` → MudContainer wrapper برای loading/error
- [ ] `Checkout` → Elevation fix + inline cleanup
- [ ] `WeeklyBalance` → inline gradient → CSS class
- [ ] Build: 0 errors ✅
### فاز ۴ چک‌لیست:
- [ ] `Store/Products` → hero inline → CSS
- [ ] `Store/ProductDetail` → image inline → CSS class
- [ ] `DiscountStore/ProductDetail` → rgba → variable
- [ ] `Gateway/*` → hardcoded → variable
- [ ] Elevation یکسان‌سازی
- [ ] Container spacing یکسان‌سازی
- [ ] Build: 0 errors ✅
### فاز ۵ چک‌لیست:
- [ ] `Index.razor` → hero inline cleanup
- [ ] `About.razor` → radial-gradient cleanup
- [ ] `Blog/Index` + `Blog/Post` → inline cleanup
- [ ] `Contact.razor` + `FAQ.razor` → minor cleanup
- [ ] Build: 0 errors ✅
### فاز ۶ چک‌لیست:
- [ ] Routing fix (Categories, Blog/Post)
- [ ] MudSnackbar styling
- [ ] Dialog consistency
- [ ] Dark mode full test
- [ ] Mobile responsive full test
- [ ] Build: 0 errors ✅
---
## 📋 بخش ۷: خلاصه تغییرات در یک نگاه
```
فایل‌های تغییر‌یافته:
├── CustomMudTheme.cs [فاز ۱] — ارتقاء کامل تم
├── site.css [فاز ۱-۵] — CSS Variables + class‌های جدید
├── Shared/LoadingState.razor [فاز ۱] — جدید
├── Shared/EmptyState.razor [فاز ۱] — جدید
├── Shared/PageHeader.razor [فاز ۱] — بهبود
├── Profile/* (۷ فایل) [فاز ۲] — PageHeader + LoadingState
├── Profile/Index.razor [فاز ۲] — حذف inline styles
├── Club/* (۲ فایل) [فاز ۲] — PageHeader + LoadingState
├── Commission/* (۲ فایل) [فاز ۳] — PageHeader + LoadingState + cleanup
├── Network/* (۱ فایل) [فاز ۳] — PageHeader + LoadingState
├── PackageDetail.razor [فاز ۳] — Container fix
├── Checkout.razor [فاز ۳] — Elevation + cleanup
├── Store/* (۲ فایل) [فاز ۴] — inline → CSS
├── DiscountStore/ProductDetail [فاز ۴] — rgba → variable
├── Gateway/* (۳ فایل) [فاز ۴] — hardcoded → variable
├── Index.razor [فاز ۵] — hero cleanup
├── About.razor [فاز ۵] — gradient cleanup
├── Blog/* (۲ فایل) [فاز ۵] — inline cleanup
├── Contact.razor + FAQ.razor [فاز ۵] — minor cleanup
└── Categories + Blog/Post [فاز ۶] — routing fix
```
**مجموع فایل‌های تأثیرپذیر**: ~۳۵ فایل
**فایل‌های جدید**: ۲ (LoadingState, EmptyState)
**میزان تغییر UI**: ~۵۰٪
**میزان تغییر UX**: ~۲۰٪
**ریسک شکست**: پایین (تغییرات تدریجی، build verification در هر فاز)
+425
View File
@@ -0,0 +1,425 @@
# FourSat Data Migration Tool
## نگاه کلی
این ابزار برای مهاجرت داده‌های دیتابیس از ساختار قدیمی (Production) به ساختار جدید (Stage) طراحی شده است.
**ویژگی‌ها:**
- ✅ Queue-based processing با retry logic
- ✅ Error handling - آیتم‌های ناموفق به صف retry می‌روند
- ✅ Logging کامل با Serilog (Console + File)
- ✅ قابلیت توقف/ادامه (Pause/Resume)
- ✅ Table name mapping (مثل Categorys → Categories)
- ✅ Batch processing برای کارایی بهتر
- ✅ Retry با Exponential Backoff
- ✅ Progress tracking
---
## ساختار پروژه
```
FourSat.DataMigration/
├── Program.cs # Entry point با Hosting
├── appsettings.json # تنظیمات (ConnectionStrings, Mappings)
├── Models/
│ ├── MigrationSettings.cs # تنظیمات migration
│ ├── TableMapping.cs # نگاشت table ها
│ └── MigrationQueueItem.cs # آیتم صف
├── Services/
│ ├── IMigrationService.cs # Interface
│ ├── MigrationService.cs # سرویس اصلی migration
│ ├── QueueManager.cs # مدیریت صف و retry
│ └── TableMigrator.cs # مهاجرت یک table
└── Logs/ # لاگ فایل‌ها (auto-created)
```
---
## تنظیمات (`appsettings.json`)
### 1. ConnectionStrings
```json
{
"SourceDatabase": "Server=185.252.31.42,2019;Database=Foursat;...",
"TargetDatabase": "Server=194.5.195.53,31433;Database=Foursat;..."
}
```
**⚠️ توجه**: حتماً Username و Password را وارد کنید!
### 2. MigrationSettings
- **BatchSize**: تعداد رکوردهای هر batch (پیشنهاد: 1000)
- **MaxRetryAttempts**: حداکثر تلاش مجدد (5 بار)
- **RetryDelaySeconds**: تأخیر بین retry ها (5 ثانیه)
- **MaxConcurrentTables**: تعداد table های همزمان (3 عدد)
- **EnableDetailedLogging**: لاگ جزئیات (true)
- **SkipEmptyTables**: نادیده گرفتن table های خالی (true)
### 3. TableMappings
نگاشت نام table قدیمی به جدید (33 جدول):
```json
{
"Categorys": "Categories",
"ClubFeatures": "ClubFeatures",
"ClubMembershipHistories": "ClubMembershipHistories",
"ClubMemberships": "ClubMemberships",
"CommissionPayoutHistories": "CommissionPayoutHistories",
"Contracts": "Contracts",
"FactorDetailss": "FactorDetails",
"NetworkMembershipHistories": "NetworkMembershipHistories",
"NetworkWeeklyBalances": "NetworkWeeklyBalances",
"OtpTokens": "OtpTokens",
"Packages": "Packages",
"ProductGalleryss": "ProductGalleries",
"ProductImagess": "ProductImages",
"Productss": "Products",
"PruductCategorys": "ProductCategories",
"PruductTags": "ProductTags",
"Roles": "Roles",
"SystemConfigurationHistories": "SystemConfigurationHistories",
"SystemConfigurations": "SystemConfigurations",
"Tags": "Tags",
"Transactionss": "Transactions",
"UserAddresss": "UserAddresses",
"UserCartss": "UserCarts",
"UserClubFeatures": "UserClubFeatures",
"UserCommissionPayouts": "UserCommissionPayouts",
"UserContracts": "UserContracts",
"UserOrders": "UserOrders",
"UserRoles": "UserRoles",
"Users": "Users",
"UserWalletChangeLogs": "UserWalletChangeLogs",
"UserWallets": "UserWallets",
"WeeklyCommissionPools": "WeeklyCommissionPools",
"WorkerExecutionLogs": "WorkerExecutionLogs"
}
```
**چگونه کار می‌کند:**
- اگر table در mapping باشد → از نام جدید استفاده می‌کند
- اگر در mapping نباشد → همان نام را استفاده می‌کند
- اگر table در target نباشد → Log می‌کند و skip می‌کند
---
## نحوه اجرا
### 1. ویرایش appsettings.json
```bash
cd /home/masoud/Apps/project/FourSat/DataMigration/FourSat.DataMigration
nano appsettings.json
```
**تغییرات لازم:**
-`SourceDatabase`: Username و Password را وارد کنید
-`TargetDatabase`: Username و Password را وارد کنید
-`TableMappings`: اگر mapping جدید دارید اضافه کنید
### 2. Build پروژه
```bash
dotnet build
```
### 3. اجرای Migration
```bash
dotnet run
```
### 4. مشاهده Logs
```bash
# Real-time console output
# یا
tail -f Logs/migration-20251206.txt
```
---
## جریان کار (Workflow)
```
1. خواندن تنظیمات از appsettings.json
2. اتصال به Source و Target databases
3. کشف تمام table های Source (CMS schema)
4. برای هر table:
├─ بررسی mapping (قدیمی → جدید)
├─ تعداد رکوردها را بخواند
├─ اگر خالی → skip (با log)
├─ اگر پر → افزودن به Queue
└─ Log: "Table X → Y: N records"
5. پردازش Queue:
├─ تا MaxConcurrentTables همزمان
├─ هر table در batch ها (BatchSize)
├─ اگر error → Retry (MaxRetryAttempts)
├─ اگر بعد از retry fail → Log + Skip
└─ پیشرفت را نمایش بده
6. گزارش نهایی:
├─ تعداد table های موفق
├─ تعداد table های ناموفق
├─ جمع رکوردهای migrate شده
└─ مدت زمان کل
```
---
## Retry Logic
### استراتژی:
1. **اولین تلاش**: بلافاصله
2. **تلاش 2**: بعد از 5 ثانیه
3. **تلاش 3**: بعد از 10 ثانیه (exponential backoff)
4. **تلاش 4**: بعد از 20 ثانیه
5. **تلاش 5**: بعد از 40 ثانیه
**اگر همه fail شوند:**
- Log error با جزئیات کامل
- Table را از queue حذف کن
- به table بعدی برو (متوقف نمی‌شود!)
---
## Error Handling
### خطاهای رایج:
| خطا | دلیل | راه حل |
|-----|------|--------|
| **Login failed** | Username/Password اشتباه | appsettings.json را بررسی کنید |
| **Table not found** | Table در target وجود ندارد | Migration بزنید یا از mapping صحیح استفاده کنید |
| **Timeout** | Network کند یا batch زیاد | BatchSize را کاهش دهید |
| **Deadlock** | همزمانی بالا | MaxConcurrentTables را کم کنید |
| **Permission denied** | User دسترسی ندارد | سطح دسترسی SQL را بررسی کنید |
---
## مثال خروجی
```
[12:30:15 INF] Starting migration...
[12:30:16 INF] Source: 30 tables found
[12:30:16 INF] Mapping: Categorys → Categories
[12:30:16 INF] Mapping: Productss → Products
[12:30:17 INF] Queue: 28 tables added (2 empty skipped)
[12:30:18 INF] Migrating: Categories (6 records)
[12:30:18 INF] Success: Categories (6/6) - 100%
[12:30:19 INF] Migrating: Products (150 records)
[12:30:21 INF] Success: Products (150/150) - 100%
...
[12:35:42 INF] === Migration Complete ===
[12:35:42 INF] Success: 28 tables, 45,320 records
[12:35:42 INF] Failed: 0 tables
[12:35:42 INF] Duration: 5 minutes 27 seconds
```
---
## فایل‌های باقی مانده برای پیاده‌سازی
### Models/MigrationSettings.cs
```csharp
public class MigrationSettings
{
public int BatchSize { get; set; } = 1000;
public int MaxRetryAttempts { get; set; } = 5;
public int RetryDelaySeconds { get; set; } = 5;
public int MaxConcurrentTables { get; set; } = 3;
public bool EnableDetailedLogging { get; set; } = true;
public bool SkipEmptyTables { get; set; } = true;
}
```
### Models/TableMapping.cs
```csharp
public class TableMapping
{
public string SourceTable { get; set; } = string.Empty;
public string TargetTable { get; set; } = string.Empty;
public long TotalRecords { get; set; }
public long MigratedRecords { get; set; }
public MigrationStatus Status { get; set; }
}
public enum MigrationStatus
{
Pending,
InProgress,
Completed,
Failed,
Retrying
}
```
### Models/MigrationQueueItem.cs
```csharp
public class MigrationQueueItem
{
public string SourceTable { get; set; } = string.Empty;
public string TargetTable { get; set; } = string.Empty;
public long TotalRecords { get; set; }
public int RetryCount { get; set; }
public DateTime? LastAttempt { get; set; }
public string? LastError { get; set; }
}
```
### Services/IMigrationService.cs
```csharp
public interface IMigrationService
{
Task RunAsync(CancellationToken cancellationToken);
}
```
### Services/MigrationService.cs
```csharp
public class MigrationService : IMigrationService
{
// کلاس اصلی که:
// 1. لیست table ها را از source می‌خواند
// 2. QueueManager را راه‌اندازی می‌کند
// 3. TableMigrator ها را همزمان اجرا می‌کند
// 4. Progress و statistics را نمایش می‌دهد
}
```
### Program.cs
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
var host = Host.CreateDefaultBuilder(args)
.UseSerilog((context, config) => config.ReadFrom.Configuration(context.Configuration))
.ConfigureServices((context, services) =>
{
services.Configure<MigrationSettings>(context.Configuration.GetSection("MigrationSettings"));
services.AddSingleton<IMigrationService, MigrationService>();
// Register other services...
})
.Build();
await host.Services.GetRequiredService<IMigrationService>().RunAsync(CancellationToken.None);
```
---
## توقف و ادامه (Pause/Resume)
**نحوه توقف:**
```bash
Ctrl+C # Graceful shutdown
```
**نحوه ادامه:**
- هیچ state ذخیره نمی‌شود (stateless)
- دوباره `dotnet run` کنید
- چون `INSERT` استفاده می‌شود، رکوردهای duplicate ایجاد می‌شود
- **پیشنهاد**: قبل از اجرای مجدد، Target را TRUNCATE کنید
**برای Production:**
- از `MERGE` یا `INSERT IF NOT EXISTS` استفاده کنید
- یک جدول `MigrationState` برای ذخیره پیشرفت ایجاد کنید
---
## نکات امنیتی
1. **Credentials**:
- ❌ هرگز appsettings.json را commit نکنید
- ✅ از Environment Variables یا User Secrets استفاده کنید
2. **Network**:
- ✅ از VPN برای اتصال به Production استفاده کنید
- ✅ IP شما در Firewall مجاز باشد
3. **Permissions**:
- Source: فقط `SELECT` کافی است
- Target: نیاز به `INSERT` دارد
---
## بهینه‌سازی عملکرد
### برای دیتابیس کوچک (<100K records):
```json
{
"BatchSize": 5000,
"MaxConcurrentTables": 5
}
```
### برای دیتابیس متوسط (100K-1M):
```json
{
"BatchSize": 2000,
"MaxConcurrentTables": 3
}
```
### برای دیتابیس بزرگ (>1M):
```json
{
"BatchSize": 500,
"MaxConcurrentTables": 2
}
```
---
## حذف یا خاموش کردن
### خاموش کردن موقت:
```bash
# فقط اجرا نکنید!
```
### حذف کامل:
```bash
cd /home/masoud/Apps/project/FourSat
rm -rf DataMigration/
```
---
## لایسنس
این ابزار موقت برای استفاده داخلی FourSat است. بعد از sync کامل، حذف شود.
---
## سوالات متداول (FAQ)
**Q: چرا بعضی table ها migrate نمی‌شوند؟**
A: چک کنید:
1. Table در Target وجود دارد؟
2. Schema match می‌کند؟
3. Mapping صحیح است؟
**Q: چگونه فقط یک table خاص را migrate کنم؟**
A: در کد `MigrationService.cs`، فیلتر اضافه کنید:
```csharp
var tablesToMigrate = allTables.Where(t => t == "Users").ToList();
```
**Q: چگونه از duplicate جلوگیری کنم؟**
A: قبل از اجرا، Target را خالی کنید:
```sql
TRUNCATE TABLE [CMS].[Categories];
TRUNCATE TABLE [CMS].[Products];
-- ...
```
**Q: آیا می‌توانم بدون توقف سرور اجرا کنم؟**
A: بله، فقط `SELECT` روی Source اجرا می‌شود (ReadOnly).
---
**آخرین بروزرسانی**: December 6, 2025
**نسخه**: 1.0
**وضعیت**: آماده برای پیاده‌سازی نهایی