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:
@@ -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
@@ -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) |
|
||||
@@ -0,0 +1,445 @@
|
||||
# CMS Microservice - Network & Club Commission + Inventory Management System
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
## 📊 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
|
||||
@@ -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** 🛰️
|
||||
@@ -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) |
|
||||
@@ -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 در هر فاز)
|
||||
@@ -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
|
||||
**وضعیت**: آماده برای پیادهسازی نهایی
|
||||
Reference in New Issue
Block a user