docs: consolidate 53 files into 15 structured files in 3 folders
- business/ (5): club-commission, payment, ecommerce, membership, content - technical/ (5): cms-arch, ui, deployment, migration, api - overview/ (5): flowcharts, index, changelog, glossary, roadmap - Removed all old folders: backoffice, cms, deployment, docs, frontoffice, migration, ui-modernization, business (old) - Updated internal links with relative folder paths
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
# ⚙️ معماری CMS و زیرساخت فنی
|
||||
|
||||
> **منابع ادغامشده:** `CMS-README.md`, `ICURRENTUSERSERVICE-IMPLEMENTATION.md`, `FILE-MANAGEMENT-ARCHITECTURE.md`, `FRONTOFFICE-CMS-API-COMPATIBILITY.md`, `BFF-REMOVAL-PLAN.md`, `system-constants.md`
|
||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴
|
||||
|
||||
---
|
||||
|
||||
## ۱. Stack فنی
|
||||
|
||||
| لایه | تکنولوژی | نسخه |
|
||||
|------|----------|------|
|
||||
| **Runtime** | .NET | 9.0 |
|
||||
| **ORM** | Entity Framework Core | 9.0 |
|
||||
| **Communication** | gRPC (Protobuf) | v3 |
|
||||
| **Pattern** | CQRS + MediatR | — |
|
||||
| **Database** | SQL Server (MSSQL) | 2022-CU16 |
|
||||
| **Job Scheduler** | Hangfire | — |
|
||||
| **Auth** | JWT Bearer + Identity | — |
|
||||
| **API Gateway** | حذفشده (Direct gRPC) | — |
|
||||
|
||||
---
|
||||
|
||||
## ۲. معماری لایهای CMS
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Presentation Layer │
|
||||
│ FrontOffice (Blazor Server) ←─gRPC─→ CMS │
|
||||
│ BackOffice (Blazor WASM) ←─gRPC─→ CMS │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ Application Layer │
|
||||
│ Commands (MediatR IRequest<T>) │
|
||||
│ Queries (MediatR IRequest<T>) │
|
||||
│ Validators (FluentValidation) │
|
||||
│ Handlers (IRequestHandler<TReq, TRes>) │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ Domain Layer │
|
||||
│ Entities, Enums, Value Objects │
|
||||
│ Domain Events, Interfaces │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ Infrastructure Layer │
|
||||
│ EF Core DbContext (CMSDbContext) │
|
||||
│ Repositories, External Services │
|
||||
│ Hangfire Jobs, File Storage │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ Database │
|
||||
│ SQL Server — Schema: [CMS] │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۳. CQRS با MediatR
|
||||
|
||||
### ۳.۱ ساختار فولدرها
|
||||
|
||||
```
|
||||
CMS/src/
|
||||
├── CMSMicroservice/
|
||||
│ ├── Features/
|
||||
│ │ ├── Products/
|
||||
│ │ │ ├── Commands/
|
||||
│ │ │ │ ├── CreateProductCommand.cs
|
||||
│ │ │ │ └── CreateProductCommandHandler.cs
|
||||
│ │ │ ├── Queries/
|
||||
│ │ │ │ ├── GetProductsQuery.cs
|
||||
│ │ │ │ └── GetProductsQueryHandler.cs
|
||||
│ │ │ └── Validators/
|
||||
│ │ │ └── CreateProductCommandValidator.cs
|
||||
│ │ ├── Orders/
|
||||
│ │ ├── Users/
|
||||
│ │ ├── Club/
|
||||
│ │ ├── Payment/
|
||||
│ │ └── Blog/
|
||||
│ ├── Services/
|
||||
│ │ ├── gRPC/ ← gRPC service implementations
|
||||
│ │ ├── Background/ ← Hangfire jobs
|
||||
│ │ └── External/ ← ZarinPal, Kavenegar, Daya, Chatika
|
||||
│ ├── Infrastructure/
|
||||
│ │ ├── Persistence/ ← DbContext, Migrations
|
||||
│ │ └── Identity/ ← JWT, Claims, ICurrentUserService
|
||||
│ └── Protos/ ← .proto files
|
||||
```
|
||||
|
||||
### ۳.۲ مثال Command
|
||||
|
||||
```csharp
|
||||
// Command
|
||||
public record CreateProductCommand(
|
||||
string Name, string Description, decimal Price,
|
||||
Guid CategoryId, string ImageUrl
|
||||
) : IRequest<Guid>;
|
||||
|
||||
// Handler
|
||||
public class CreateProductCommandHandler
|
||||
: IRequestHandler<CreateProductCommand, Guid>
|
||||
{
|
||||
private readonly CMSDbContext _db;
|
||||
|
||||
public async Task<Guid> Handle(
|
||||
CreateProductCommand request, CancellationToken ct)
|
||||
{
|
||||
var product = new Product { /* map fields */ };
|
||||
_db.Products.Add(product);
|
||||
|
||||
// Auto-create inventory record
|
||||
_db.Inventories.Add(new Inventory { ProductId = product.Id });
|
||||
|
||||
await _db.SaveChangesAsync(ct);
|
||||
return product.Id;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۴. gRPC Services
|
||||
|
||||
### ۴.۱ لیست سرویسها
|
||||
|
||||
| سرویس | proto | متدهای اصلی |
|
||||
|--------|-------|-------------|
|
||||
| `ProductService` | product.proto | GetProducts, GetProduct, Create, Update, Delete |
|
||||
| `OrderService` | order.proto | CreateOrder, GetOrders, UpdateStatus |
|
||||
| `UserService` | user.proto | Register, Login, GetProfile, UpdateProfile |
|
||||
| `ClubService` | club.proto | GetNetworkTree, GetBalance, AcceptContract |
|
||||
| `PaymentService` | payment.proto | CreatePayment, VerifyPayment |
|
||||
| `BlogService` | blog.proto | GetPosts, GetPost, Create, Update |
|
||||
| `InventoryService` | inventory.proto | GetInventory, UpdateStock |
|
||||
| `FileService` | file.proto | Upload, Download, Delete |
|
||||
| `SitePageService` | sitepage.proto | GetPage, SaveSettings |
|
||||
| `CategoryService` | category.proto | GetCategories, Create, Update |
|
||||
| `SystemConfigService` | config.proto | GetConfig, UpdateConfig |
|
||||
|
||||
### ۴.۲ PaginationState (مشترک)
|
||||
|
||||
```protobuf
|
||||
message PaginationState {
|
||||
int32 skip = 1;
|
||||
int32 take = 2;
|
||||
}
|
||||
```
|
||||
|
||||
**Namespace صحیح:**
|
||||
```csharp
|
||||
using CMSMicroservice.Protobuf.Protos.PaginationState;
|
||||
// ⚠️ نه: CMSMicroservice.Protobuf.Protos.PublicMessages.PaginationState
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۵. Database
|
||||
|
||||
### ۵.۱ اتصال
|
||||
|
||||
```
|
||||
Staging: Server=194.5.195.53; Database=FourSatCMS; Schema=CMS
|
||||
Production: Server=45.149.79.127; Database=FourSatCMS; Schema=CMS
|
||||
Engine: MSSQL 2022-CU16, Collation=Arabic_CI_AS
|
||||
```
|
||||
|
||||
### ۵.۲ جداول اصلی
|
||||
|
||||
| جدول | توضیح | رکوردهای تقریبی |
|
||||
|------|--------|----------------|
|
||||
| Users | کاربران | ~5K |
|
||||
| Products | محصولات | ~200 |
|
||||
| Categories | دستهبندیها | ~30 |
|
||||
| Orders | سفارشات | ~2K |
|
||||
| Inventories | موجودی | ~200 |
|
||||
| BlogPosts | پستهای بلاگ | ~50 |
|
||||
| SitePages | صفحات سایت | ~10 |
|
||||
| UserClubMemberships | عضویت باشگاه | ~500 |
|
||||
| UserContracts | قراردادها | ~500 |
|
||||
| NetworkNodes | نودهای درخت باینری | ~500 |
|
||||
| Transactions | تراکنشها | ~5K |
|
||||
| SystemConfigurations | تنظیمات | ~30 |
|
||||
| ChatMessages | پیامهای چاتیکا | ~1K |
|
||||
|
||||
### ۵.۳ Stored Procedures
|
||||
|
||||
| SP | کاربرد |
|
||||
|----|--------|
|
||||
| `SP_GetNetworkTree` | بازگشتی — استخراج درخت باینری |
|
||||
| `sp_CalculateWeeklyBalances` | محاسبه بالانس هفتگی هر عضو |
|
||||
| `sp_CalculateWeeklyCommissionPool` | توزیع Pool هفتگی |
|
||||
|
||||
---
|
||||
|
||||
## ۶. حذف BFF / Gateway
|
||||
|
||||
### ۶.۱ قبل vs بعد
|
||||
|
||||
```
|
||||
قبل:
|
||||
FrontOffice → BFF (REST) → CMS (gRPC)
|
||||
BackOffice → BFF (REST) → CMS (gRPC)
|
||||
|
||||
بعد (فعلی):
|
||||
FrontOffice → CMS (gRPC مستقیم)
|
||||
BackOffice → CMS (gRPC مستقیم)
|
||||
|
||||
مزایا:
|
||||
✅ حذف لایه واسط → کاهش latency
|
||||
✅ حذف maintenance اضافی
|
||||
✅ Type-safe از proto تا UI
|
||||
✅ کاهش ۱ سرویس در deployment
|
||||
```
|
||||
|
||||
### ۶.۲ سازگاری API
|
||||
|
||||
```
|
||||
FrontOffice Service Layer:
|
||||
• ProductService.cs → gRPC client wrapper
|
||||
• OrderService.cs → gRPC client wrapper
|
||||
• UserService.cs → gRPC client wrapper
|
||||
|
||||
هر Service:
|
||||
• Constructor: inject GrpcChannel
|
||||
• Methods: wrap gRPC calls + map to DTOs
|
||||
• Error handling: try/catch RpcException
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۷. Hangfire Jobs
|
||||
|
||||
| Job | فرکانس | کاربرد |
|
||||
|-----|---------|--------|
|
||||
| `DayaLoanProcessorJob` | هر ۱۵ دقیقه | پردازش درخواستهای وام |
|
||||
| `WeeklyCommissionJob` | هفتگی (شنبه ۰۰:۰۰) | محاسبه و توزیع کمیسیون |
|
||||
| `ChatikaJob` | هر ۵ دقیقه | همگامسازی پیامهای AI |
|
||||
| `InventorySyncJob` | هر ساعت | ایجاد رکوردهای موجودی گمشده |
|
||||
|
||||
---
|
||||
|
||||
## ۸. پیکربندی
|
||||
|
||||
### ۸.۱ appsettings.json ساختار
|
||||
|
||||
```json
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=...;Database=FourSatCMS"
|
||||
},
|
||||
"Jwt": {
|
||||
"Secret": "***",
|
||||
"Issuer": "FourSat",
|
||||
"ExpiryMinutes": 1440
|
||||
},
|
||||
"Grpc": {
|
||||
"CmsUrl": "https://localhost:5001"
|
||||
},
|
||||
"Hangfire": {
|
||||
"DashboardPath": "/hangfire",
|
||||
"WorkerCount": 4
|
||||
},
|
||||
"Kavenegar": { "ApiKey": "***" },
|
||||
"ZarinPal": { "MerchantId": "***" },
|
||||
"DayaLoan": { "UseMock": true }
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,252 @@
|
||||
# 🖥️ BackOffice و FrontOffice — معماری UI
|
||||
|
||||
> **منابع ادغامشده:** `BACKOFFICE-ARCHITECTURE.md`, `BACKOFFICE-STORE-UNIFICATION.md`, `UI-MODERNIZATION-PLAN.md`, `UI-UNIFICATION-PLAN.md`, `PHASE-1-COMPLETE.md`, `PHASE-3-COMPLETE.md`, `PRODUCT-IMAGES-SQUARE.md`
|
||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴
|
||||
|
||||
---
|
||||
|
||||
## ۱. Stack مشترک
|
||||
|
||||
| آیتم | BackOffice | FrontOffice |
|
||||
|------|-----------|-------------|
|
||||
| **Framework** | Blazor WebAssembly | Blazor Server |
|
||||
| **UI Library** | MudBlazor v8 | MudBlazor v8 |
|
||||
| **ارتباط با CMS** | gRPC (مستقیم) | gRPC (مستقیم) |
|
||||
| **احراز هویت** | JWT Bearer | JWT Bearer |
|
||||
| **Hosting** | Static files (nginx) | Kestrel server |
|
||||
| **Target** | ادمینها | کاربران نهایی |
|
||||
|
||||
---
|
||||
|
||||
## ۲. معماری BackOffice
|
||||
|
||||
### ۲.۱ ساختار فولدرها
|
||||
|
||||
```
|
||||
BackOffice/src/BackOffice/
|
||||
├── Layout/
|
||||
│ ├── MainLayout.razor ← Sidebar + AppBar
|
||||
│ └── NavMenu.razor ← منوی ناوبری
|
||||
├── Pages/
|
||||
│ ├── Dashboard/
|
||||
│ ├── Products/
|
||||
│ │ ├── ProductList.razor
|
||||
│ │ ├── ProductList.razor.cs ← code-behind
|
||||
│ │ ├── ProductEdit.razor
|
||||
│ │ └── ProductEdit.razor.cs
|
||||
│ ├── Orders/
|
||||
│ ├── Users/
|
||||
│ ├── Club/
|
||||
│ ├── Blog/
|
||||
│ ├── Inventory/
|
||||
│ ├── SitePages/
|
||||
│ │ └── {PageType}Editor.razor ← Shopify-style typed editors
|
||||
│ └── Settings/
|
||||
├── Services/
|
||||
│ ├── ProductService.cs ← gRPC client wrapper
|
||||
│ ├── OrderService.cs
|
||||
│ ├── UserService.cs
|
||||
│ └── ...
|
||||
├── Shared/
|
||||
│ ├── AppImage.razor ← کامپوننت تصویر مشترک (1:1)
|
||||
│ ├── ConfirmDialog.razor
|
||||
│ └── LoadingIndicator.razor
|
||||
└── wwwroot/
|
||||
```
|
||||
|
||||
### ۲.۲ الگوی Code-Behind
|
||||
|
||||
```csharp
|
||||
// ProductList.razor.cs
|
||||
public partial class ProductList : ComponentBase
|
||||
{
|
||||
[Inject] private IProductService ProductService { get; set; }
|
||||
[Inject] private ISnackbar Snackbar { get; set; }
|
||||
|
||||
private List<ProductDto> _products = new();
|
||||
private bool _isLoading = true;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadProducts();
|
||||
}
|
||||
|
||||
private async Task LoadProducts()
|
||||
{
|
||||
_isLoading = true;
|
||||
try {
|
||||
_products = await ProductService.GetProductsAsync();
|
||||
} catch (RpcException ex) {
|
||||
Snackbar.Add($"خطا: {ex.Status.Detail}", Severity.Error);
|
||||
}
|
||||
_isLoading = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۳. معماری FrontOffice
|
||||
|
||||
### ۳.۱ ساختار فولدرها
|
||||
|
||||
```
|
||||
FrontOffice/src/FrontOffice/
|
||||
├── Layout/
|
||||
│ ├── MainLayout.razor ← Header + Footer
|
||||
│ └── AuthLayout.razor ← Login/Register pages
|
||||
├── Pages/
|
||||
│ ├── Home.razor
|
||||
│ ├── Landing.razor ← انیمیشندار
|
||||
│ ├── Store/
|
||||
│ │ ├── Products.razor ← Lazy loading (12 per page)
|
||||
│ │ ├── Products.razor.cs
|
||||
│ │ ├── ProductDetail.razor
|
||||
│ │ └── Cart.razor
|
||||
│ ├── DiscountStore/
|
||||
│ │ ├── Products.razor ← Lazy loading + hybrid payment
|
||||
│ │ ├── Products.razor.cs
|
||||
│ │ └── Cart.razor
|
||||
│ ├── Club/
|
||||
│ │ ├── Dashboard.razor ← داشبورد باشگاه
|
||||
│ │ ├── NetworkTree.razor ← نمای درخت
|
||||
│ │ └── Contract.razor ← امضای قرارداد
|
||||
│ ├── Blog/
|
||||
│ ├── Auth/
|
||||
│ │ ├── Login.razor
|
||||
│ │ └── Register.razor
|
||||
│ └── About.razor, Contact.razor, ...
|
||||
├── Services/
|
||||
│ ├── ProductService.cs ← با GetProductsPagedAsync
|
||||
│ ├── ClubService.cs
|
||||
│ └── ...
|
||||
└── Shared/
|
||||
├── AppImage.razor
|
||||
├── ProductCard.razor ← مشترک بین Store و DiscountStore
|
||||
└── LoadMoreButton.razor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۴. UI Modernization — فازها
|
||||
|
||||
### ۴.۱ نقشه فازها
|
||||
|
||||
| فاز | عنوان | شامل | وضعیت |
|
||||
|------|--------|-------|--------|
|
||||
| **Phase 1** | پایه MudBlazor v8 | ارتقا MudBlazor، Layout اصلی | ✅ 100% |
|
||||
| **Phase 2** | صفحات محصول | Card grid، فیلتر، جزئیات | ✅ 100% |
|
||||
| **Phase 3** | فروشگاه تخفیفی | UI DiscountStore + hybrid pay | ✅ 100% |
|
||||
| **Phase 4** | باشگاه | داشبورد، درخت، قرارداد | ✅ 100% |
|
||||
| **Phase 5** | محتوا | بلاگ، Site Pages | ✅ 100% |
|
||||
| **Phase 6** | نهاییسازی | تصاویر 1:1، lazy load، landing fix | ✅ 100% |
|
||||
| **Phase 7** | موبایل | Responsive، PWA، Bottom nav | ⬜ 0% |
|
||||
|
||||
### ۴.۲ جزئیات Phase 1-6 (تکمیلشده)
|
||||
|
||||
```
|
||||
✅ Phase 1: ارتقا MudBlazor v7→v8, AppBar, Drawer, Theme
|
||||
✅ Phase 2: ProductCard (1:1), CategoryFilter, MudGrid
|
||||
✅ Phase 3: DiscountStore pages, HybridPayment component
|
||||
✅ Phase 4: NetworkTree visualization, Contract modal
|
||||
✅ Phase 5: Blog pagination, SitePageEditors (Shopify)
|
||||
✅ Phase 6: AppImage shared, lazy load, counter animation fix
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۵. یکپارچهسازی فروشگاه (Store Unification)
|
||||
|
||||
### ۵.۱ کامپوننتهای مشترک
|
||||
|
||||
```razor
|
||||
@* AppImage.razor — مشترک بین همه پروژهها *@
|
||||
<MudImage
|
||||
Src="@ImageUrl"
|
||||
Alt="@Alt"
|
||||
ObjectFit="ObjectFit.Cover"
|
||||
Style="aspect-ratio: 1/1; width: 100%;"
|
||||
loading="lazy" />
|
||||
|
||||
@code {
|
||||
[Parameter] public string? ImageUrl { get; set; }
|
||||
[Parameter] public string Alt { get; set; } = "";
|
||||
}
|
||||
```
|
||||
|
||||
### ۵.۲ تغییرات BackOffice
|
||||
|
||||
| صفحه | قبل | بعد |
|
||||
|------|------|------|
|
||||
| Product List | `<img>` ساده | `<AppImage>` مربعی |
|
||||
| Product Edit | فرم ساده | MudForm + Validation |
|
||||
| Inventory | بدون Autocomplete | با MudAutocomplete |
|
||||
| SitePages | جدول Settings | Typed Editors |
|
||||
|
||||
---
|
||||
|
||||
## ۶. تم و استایل
|
||||
|
||||
### ۶.۱ MudBlazor Theme
|
||||
|
||||
```csharp
|
||||
var theme = new MudTheme {
|
||||
PaletteLight = new PaletteLight {
|
||||
Primary = "#1976D2",
|
||||
Secondary = "#FF9800",
|
||||
Background = "#F5F5F5",
|
||||
Surface = "#FFFFFF",
|
||||
AppbarBackground = "#1976D2"
|
||||
},
|
||||
Typography = new Typography {
|
||||
Default = new DefaultTypography {
|
||||
FontFamily = new[] { "Vazirmatn", "Roboto", "sans-serif" }
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### ۶.۲ RTL Support
|
||||
|
||||
```css
|
||||
/* wwwroot/css/app.css */
|
||||
body { direction: rtl; font-family: 'Vazirmatn', sans-serif; }
|
||||
.mud-drawer--open-responsive-lg-left { right: 0; left: auto; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۷. ناوبری Auth-Aware (FrontOffice)
|
||||
|
||||
```csharp
|
||||
// MainLayout.razor.cs
|
||||
@inject AuthenticationStateProvider AuthState
|
||||
|
||||
var authState = await AuthState.GetAuthenticationStateAsync();
|
||||
var user = authState.User;
|
||||
|
||||
if (user.Identity?.IsAuthenticated == true) {
|
||||
var isClub = user.HasClaim("IsClubMember", "true");
|
||||
// Show: Dashboard, Store, DiscountStore (if club), Profile
|
||||
} else {
|
||||
// Show: Landing, Store, Register, Login
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۸. خلاصه وضعیت
|
||||
|
||||
| ماژول | وضعیت | درصد |
|
||||
|-------|--------|------|
|
||||
| BackOffice MudBlazor v8 | ✅ | 100% |
|
||||
| FrontOffice MudBlazor v8 | ✅ | 100% |
|
||||
| Code-behind pattern | ✅ | 100% |
|
||||
| AppImage component | ✅ | 100% |
|
||||
| Lazy loading | ✅ | 100% |
|
||||
| Store Unification | ✅ | 100% |
|
||||
| SitePage Typed Editors | ✅ | 100% |
|
||||
| RTL Support | ✅ | 100% |
|
||||
| Mobile Responsive (Phase 7) | ⬜ | 0% |
|
||||
| Dark Mode | ⬜ | 0% |
|
||||
| PWA | ⬜ | 0% |
|
||||
@@ -0,0 +1,340 @@
|
||||
# 🚀 استقرار، CI/CD و زیرساخت
|
||||
|
||||
> **منابع ادغامشده:** `CICD-PIPELINE-GUIDE.md`, `DEPLOYMENT-README.md`, `INFRASTRUCTURE-GUIDE.md`, `INGRESS-NGINX-WARNING.md`, `OFFLINE-DEPLOYMENT-GUIDE.md`, `SERVER-MIRRORS-CONFIG.md`
|
||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴
|
||||
|
||||
---
|
||||
|
||||
## ۱. سرورها
|
||||
|
||||
| سرور | IP | نقش | منابع |
|
||||
|------|-----|------|--------|
|
||||
| **Staging** | 194.5.195.53 | توسعه + تست | 4 CPU, 8GB RAM |
|
||||
| **Production** | 45.149.79.127 | محیط نهایی | 4 CPU, 16GB RAM |
|
||||
| **Git** | git.se.kbs1.ir | Gitea (مخازن کد) | — |
|
||||
| **Registry** | داخلی | Docker Registry / Nexus | — |
|
||||
|
||||
---
|
||||
|
||||
## ۲. Docker و Container
|
||||
|
||||
### ۲.۱ سرویسها
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml (production)
|
||||
services:
|
||||
cms:
|
||||
image: foursat/cms:latest
|
||||
ports: ["5001:5001"] # gRPC
|
||||
environment:
|
||||
- ConnectionStrings__Default=Server=db;Database=FourSatCMS
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
depends_on: [db]
|
||||
|
||||
backoffice:
|
||||
image: foursat/backoffice:latest
|
||||
ports: ["5002:80"] # Static Blazor WASM
|
||||
|
||||
frontoffice:
|
||||
image: foursat/frontoffice:latest
|
||||
ports: ["5003:5003"] # Blazor Server
|
||||
|
||||
db:
|
||||
image: mcr.microsoft.com/mssql/server:2022-CU16-ubuntu-22.04
|
||||
ports: ["1433:1433"]
|
||||
volumes: ["sqldata:/var/opt/mssql"]
|
||||
|
||||
nexus: # NuGet + Docker registry
|
||||
image: sonatype/nexus3
|
||||
ports: ["8081:8081"]
|
||||
|
||||
volumes:
|
||||
sqldata:
|
||||
```
|
||||
|
||||
### ۲.۲ Dockerfile (CMS)
|
||||
|
||||
```dockerfile
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 5001
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["CMSMicroservice/CMSMicroservice.csproj", "CMSMicroservice/"]
|
||||
RUN dotnet restore
|
||||
COPY . .
|
||||
RUN dotnet publish -c Release -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
COPY --from=build /app/publish .
|
||||
ENTRYPOINT ["dotnet", "CMSMicroservice.dll"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۳. Kubernetes
|
||||
|
||||
### ۳.۱ Manifests ساختار
|
||||
|
||||
```
|
||||
deployment/k8s-manifests/
|
||||
├── cms-deployment.yaml
|
||||
├── cms-service.yaml
|
||||
├── backoffice-deployment.yaml
|
||||
├── backoffice-service.yaml
|
||||
├── frontoffice-deployment.yaml
|
||||
├── frontoffice-service.yaml
|
||||
├── db-statefulset.yaml
|
||||
├── db-service.yaml
|
||||
├── ingress.yaml
|
||||
├── configmap.yaml
|
||||
└── secrets.yaml
|
||||
```
|
||||
|
||||
### ۳.۲ مثال Deployment
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cms
|
||||
namespace: foursat
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cms
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: cms
|
||||
image: foursat/cms:latest
|
||||
ports:
|
||||
- containerPort: 5001
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
livenessProbe:
|
||||
grpc:
|
||||
port: 5001
|
||||
initialDelaySeconds: 15
|
||||
readinessProbe:
|
||||
grpc:
|
||||
port: 5001
|
||||
```
|
||||
|
||||
### ۳.۳ Ingress
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: foursat-ingress
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
||||
spec:
|
||||
rules:
|
||||
- host: foursat.ir
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: frontoffice
|
||||
port: { number: 5003 }
|
||||
- path: /admin
|
||||
backend:
|
||||
service:
|
||||
name: backoffice
|
||||
port: { number: 80 }
|
||||
```
|
||||
|
||||
> ⚠️ **هشدار:** Ingress-nginx نسخههای قبل از 1.9.0 مشکل امنیتی CVE-2023-5044 دارند. حتماً بروزرسانی کنید.
|
||||
|
||||
---
|
||||
|
||||
## ۴. CI/CD Pipeline
|
||||
|
||||
### ۴.۱ Gitea Actions Workflow
|
||||
|
||||
```yaml
|
||||
name: Build and Deploy
|
||||
on:
|
||||
push:
|
||||
branches: [kub-stage, production]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --no-restore -c Release
|
||||
|
||||
- name: Test
|
||||
run: dotnet test --no-build -c Release
|
||||
|
||||
- name: Docker Build & Push
|
||||
run: |
|
||||
docker build -t $REGISTRY/foursat/cms:${{ github.sha }} .
|
||||
docker push $REGISTRY/foursat/cms:${{ github.sha }}
|
||||
|
||||
- name: Deploy to K8s
|
||||
if: github.ref == 'refs/heads/production'
|
||||
run: |
|
||||
kubectl set image deployment/cms cms=$REGISTRY/foursat/cms:${{ github.sha }}
|
||||
```
|
||||
|
||||
### ۴.۲ شاخهها
|
||||
|
||||
| شاخه | محیط | Deploy |
|
||||
|------|------|--------|
|
||||
| `kub-stage` | Staging (194.5.195.53) | Auto |
|
||||
| `production` | Production (45.149.79.127) | Manual trigger |
|
||||
| `main` | — | Development only |
|
||||
|
||||
---
|
||||
|
||||
## ۵. استقرار آفلاین (Offline Deployment)
|
||||
|
||||
### ۵.۱ فلوی آمادهسازی
|
||||
|
||||
```
|
||||
سرور اینترنتدار:
|
||||
1. pull-base-images.sh → دانلود Docker images
|
||||
2. cache-nuget-packages.sh → دانلود NuGet packages
|
||||
3. save-images.sh → ذخیره تصاویر به tar
|
||||
4. بستهبندی همه فایلها
|
||||
|
||||
انتقال فیزیکی (USB/HDD):
|
||||
tar files + nuget packages + k8s manifests
|
||||
|
||||
سرور آفلاین:
|
||||
1. load-images.sh → بارگذاری تصاویر
|
||||
2. setup-nexus-complete.sh → راهاندازی Nexus (NuGet proxy)
|
||||
3. build-all-offline.sh → بیلد با Nexus محلی
|
||||
4. k8s-deploy.sh → استقرار در Kubernetes
|
||||
```
|
||||
|
||||
### ۵.۲ اسکریپتهای کلیدی
|
||||
|
||||
| اسکریپت | کاربرد |
|
||||
|----------|--------|
|
||||
| `pull-base-images.sh` | دانلود ۱۵+ Docker image پایه |
|
||||
| `save-images.sh` | Export به tar (4-8 GB) |
|
||||
| `load-images.sh` | Import از tar به Docker |
|
||||
| `cache-nuget-packages.sh` | دانلود NuGet offline |
|
||||
| `setup-nexus-complete.sh` | راهاندازی NuGet proxy |
|
||||
| `build-all-offline.sh` | بیلد بدون اینترنت |
|
||||
| `k8s-deploy.sh` | Deploy تمام سرویسها |
|
||||
| `k8s-health-check.sh` | بررسی سلامت سرویسها |
|
||||
|
||||
---
|
||||
|
||||
## ۶. Nexus Repository Manager
|
||||
|
||||
### ۶.۱ نقش
|
||||
|
||||
```
|
||||
Nexus (داخلی):
|
||||
├── NuGet proxy → cache.nuget.org packages
|
||||
├── NuGet hosted → بستههای proto داخلی
|
||||
├── Docker proxy → cache Docker Hub images
|
||||
└── Docker hosted → تصاویر داخلی FourSat
|
||||
```
|
||||
|
||||
### ۶.۲ NuGet.config
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nexus" value="http://localhost:8081/repository/nuget-group/index.json" />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۷. Mirror و Cache
|
||||
|
||||
### ۷.۱ Docker Mirror
|
||||
|
||||
```json
|
||||
// /etc/docker/daemon.json
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://mirror.gcr.io",
|
||||
"https://docker.arvancloud.ir"
|
||||
],
|
||||
"insecure-registries": [
|
||||
"localhost:8082"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### ۷.۲ NuGet Mirror
|
||||
|
||||
```
|
||||
Primary: nuget.org
|
||||
Fallback: Nexus local proxy
|
||||
Proto packages: BaGet (internal) at http://localhost:5555
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۸. Proto Packages (NuGet)
|
||||
|
||||
### ۸.۱ فلوی بستهبندی
|
||||
|
||||
```
|
||||
CMS/src/Protos/*.proto
|
||||
│
|
||||
▼
|
||||
pack-protos.sh → dotnet pack → .nupkg
|
||||
│
|
||||
▼
|
||||
push to BaGet/Nexus
|
||||
│
|
||||
▼
|
||||
BackOffice + FrontOffice → dotnet restore → مصرف proto
|
||||
```
|
||||
|
||||
### ۸.۲ نام بسته
|
||||
|
||||
```xml
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="1.0.x" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۹. مانیتورینگ و Health Check
|
||||
|
||||
```bash
|
||||
# k8s-health-check.sh
|
||||
kubectl get pods -n foursat
|
||||
kubectl top pods -n foursat
|
||||
kubectl logs deployment/cms -n foursat --tail=50
|
||||
|
||||
# تست سرویسها
|
||||
grpcurl -plaintext localhost:5001 list # لیست سرویسها
|
||||
grpcurl -plaintext localhost:5001 grpc.health.v1.Health/Check # Health
|
||||
curl http://localhost:5002/index.html # BackOffice
|
||||
curl http://localhost:5003/ # FrontOffice
|
||||
```
|
||||
@@ -0,0 +1,230 @@
|
||||
# 🔄 مهاجرت داده، BFF و Gateway
|
||||
|
||||
> **منابع ادغامشده:** `BACKOFFICE-BFF-MIGRATION.md`, `customer-facing-capabilities-codex.md`, `DATA-TABLE-MAPPINGS.md`, `DATAMIGRATION-README.md`, `FRONTOFFICE-TO-CMS-MIGRATION.md`, `GATEWAY-REMOVAL-MIGRATION-PLAN.md`, `MIGRATION-PROGRESS.md`
|
||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴
|
||||
|
||||
---
|
||||
|
||||
## ۱. تاریخچه مهاجرتها
|
||||
|
||||
```
|
||||
Timeline:
|
||||
▸ فاز ۱: FrontOffice REST → CMS gRPC (مستقیم)
|
||||
▸ فاز ۲: BackOffice REST → CMS gRPC (مستقیم)
|
||||
▸ فاز ۳: حذف BFF/Gateway
|
||||
▸ فاز ۴: حذف API Gateway (Ocelot)
|
||||
▸ فاز ۵: یکپارچهسازی Proto packages
|
||||
▸ فاز ۶: Data migration از سیستم قدیم
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۲. حذف BFF (Backend-for-Frontend)
|
||||
|
||||
### ۲.۱ قبل
|
||||
|
||||
```
|
||||
FrontOffice ──HTTP/REST──→ BFF ──gRPC──→ CMS
|
||||
BackOffice ──HTTP/REST──→ BFF ──gRPC──→ CMS
|
||||
|
||||
BFF مسئولیتها:
|
||||
• تبدیل REST↔gRPC
|
||||
• Aggregation
|
||||
• Auth proxy
|
||||
• Rate limiting
|
||||
```
|
||||
|
||||
### ۲.۲ بعد (فعلی)
|
||||
|
||||
```
|
||||
FrontOffice ──gRPC──→ CMS (مستقیم)
|
||||
BackOffice ──gRPC──→ CMS (مستقیم)
|
||||
|
||||
مزایا:
|
||||
✅ حذف ۱ سرویس از deployment
|
||||
✅ کاهش ~50ms latency per request
|
||||
✅ Type-safety از proto تا UI
|
||||
✅ سادهسازی debug و logging
|
||||
✅ کاهش maintenance cost
|
||||
```
|
||||
|
||||
### ۲.۳ مراحل مهاجرت
|
||||
|
||||
```
|
||||
مرحله ۱: ایجاد gRPC client wrappers در FrontOffice
|
||||
ProductService.cs → _client.GetProductsAsync(request)
|
||||
OrderService.cs → _client.GetOrdersAsync(request)
|
||||
...
|
||||
|
||||
مرحله ۲: جایگزینی HttpClient با GrpcChannel
|
||||
services.AddGrpcClient<ProductServiceClient>(o => {
|
||||
o.Address = new Uri(config["Grpc:CmsUrl"]);
|
||||
});
|
||||
|
||||
مرحله ۳: حذف BFF project
|
||||
- حذف BFF از solution
|
||||
- حذف BFF از docker-compose
|
||||
- حذف BFF از K8s manifests
|
||||
|
||||
مرحله ۴: تست end-to-end
|
||||
- تست هر صفحه FrontOffice
|
||||
- تست هر صفحه BackOffice
|
||||
- Performance benchmark
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۳. حذف API Gateway (Ocelot)
|
||||
|
||||
### ۳.۱ قبل
|
||||
|
||||
```
|
||||
Client → nginx → Ocelot Gateway → { CMS, BFF, FileService }
|
||||
↑
|
||||
URL routing, rate limiting, auth
|
||||
```
|
||||
|
||||
### ۳.۲ بعد
|
||||
|
||||
```
|
||||
Client → nginx → Ingress → { CMS, BackOffice, FrontOffice }
|
||||
↑
|
||||
Path-based routing in Ingress
|
||||
```
|
||||
|
||||
### ۳.۳ دلایل حذف
|
||||
|
||||
```
|
||||
✅ Ocelot maintenance burden → حذف
|
||||
✅ K8s Ingress → routing بومی
|
||||
✅ Let's Encrypt → TLS بومی
|
||||
✅ gRPC → type-safe بدون نیاز به gateway
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۴. FrontOffice to CMS Migration
|
||||
|
||||
### ۴.۱ Service Mapping
|
||||
|
||||
| FrontOffice Service | BFF Endpoint (حذفشده) | CMS gRPC Service |
|
||||
|--------------------|-----------------------|-------------------|
|
||||
| `ProductService` | `GET /api/products` | `ProductService.GetProducts` |
|
||||
| `OrderService` | `POST /api/orders` | `OrderService.CreateOrder` |
|
||||
| `UserService` | `POST /api/auth/login` | `UserService.Login` |
|
||||
| `ClubService` | `GET /api/club/tree` | `ClubService.GetNetworkTree` |
|
||||
| `BlogService` | `GET /api/blog/posts` | `BlogService.GetPosts` |
|
||||
| `PaymentService` | `POST /api/payment/create` | `PaymentService.CreatePayment` |
|
||||
| `FileService` | `POST /api/files/upload` | `FileService.Upload` |
|
||||
| `SitePageService` | `GET /api/pages/{type}` | `SitePageService.GetPage` |
|
||||
|
||||
### ۴.۲ DTO Mapping
|
||||
|
||||
```
|
||||
BFF DTOs (حذفشده) → Proto Messages (فعلی)
|
||||
ProductDto → ProductMessage
|
||||
OrderDto → OrderMessage
|
||||
UserDto → UserMessage
|
||||
|
||||
Proto-generated classes مستقیم در UI استفاده میشوند
|
||||
یا به local DTOs map میشوند (برای UI-specific fields)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۵. Data Migration (سیستم قدیم → جدید)
|
||||
|
||||
### ۵.۱ پروژه DataMigration
|
||||
|
||||
```
|
||||
DataMigration/
|
||||
├── FourSat.DataMigration/ ← Console app
|
||||
│ ├── Program.cs
|
||||
│ ├── Migrators/
|
||||
│ │ ├── UserMigrator.cs
|
||||
│ │ ├── ProductMigrator.cs
|
||||
│ │ ├── OrderMigrator.cs
|
||||
│ │ └── ClubMigrator.cs
|
||||
│ └── Mappings/
|
||||
│ └── TableMappings.cs
|
||||
└── FourSat.GeographySeeder/ ← Seed geography data
|
||||
├── Program.cs
|
||||
└── Data/
|
||||
├── provinces.json
|
||||
└── cities.json
|
||||
```
|
||||
|
||||
### ۵.۲ Data Table Mappings
|
||||
|
||||
| جدول مبدأ (قدیم) | جدول مقصد (CMS) | نکات |
|
||||
|------------------|-----------------|------|
|
||||
| `dbo.Users` | `CMS.Users` | PhoneNumber as primary identifier |
|
||||
| `dbo.Products` | `CMS.Products` | ImageUrl migration needed |
|
||||
| `dbo.Orders` | `CMS.Orders` | Status enum remapping |
|
||||
| `dbo.Categories` | `CMS.Categories` | Hierarchical → ParentId |
|
||||
| `dbo.NetworkTree` | `CMS.NetworkNodes` | Binary tree reconstruction |
|
||||
| `dbo.Wallets` | `CMS.UserWalletBalances` | ۳ wallet types split |
|
||||
| `dbo.Transactions` | `CMS.Transactions` | Type enum remapping |
|
||||
| `dbo.Memberships` | `CMS.UserClubMemberships` | + Contract creation |
|
||||
|
||||
### ۵.۳ SQL Scripts مهاجرت
|
||||
|
||||
| اسکریپت | کاربرد |
|
||||
|----------|--------|
|
||||
| `MigrateUsersToClubMembership.sql` | انتقال همه کاربران |
|
||||
| `MigrateSpecificUsersToClubMembership.sql` | انتقال انتخابی |
|
||||
| `ChargeUserWallets.sql` | شارژ اولیه کیفپولها |
|
||||
| `AddIsActiveToUserClubFeatures.sql` | افزودن فیلد IsActive |
|
||||
| `SeedSitePages.sql` | داده اولیه صفحات سایت |
|
||||
| `SystemConfigurations.sql` | مقادیر پیشفرض تنظیمات |
|
||||
| `populate-weekly-commission-pools.sql` | داده تاریخی Pool |
|
||||
| `update_products_price_10_percent.sql` | افزایش قیمت ۱۰% |
|
||||
|
||||
---
|
||||
|
||||
## ۶. Geography Seeder
|
||||
|
||||
```
|
||||
FourSat.GeographySeeder:
|
||||
• ۳۱ استان
|
||||
• ~۱۲۰۰ شهر
|
||||
• منبع: دیتای رسمی تقسیمات کشوری
|
||||
• فرمت: JSON → EF Core Seed
|
||||
|
||||
استفاده:
|
||||
dotnet run --project FourSat.GeographySeeder
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۷. Customer-Facing Capabilities Codex
|
||||
|
||||
### ۷.۱ خلاصه (بزرگترین سند — ۵,۳۰۰ خط)
|
||||
|
||||
این سند شامل مستندسازی کامل تمام قابلیتهای کاربرمحور سیستم است:
|
||||
|
||||
| بخش | محتوا |
|
||||
|------|--------|
|
||||
| **User Journey** | فلوی کامل از ثبتنام تا خرید |
|
||||
| **Store Features** | لیست محصول، فیلتر، سبد، پرداخت |
|
||||
| **Club Features** | عضویت، درخت، کمیسیون، قرارداد |
|
||||
| **Content** | بلاگ، صفحات، SEO |
|
||||
| **Admin Features** | مدیریت محصول، سفارش، کاربر |
|
||||
| **Integration** | Chatika، ZarinPal، Kavenegar، Daya |
|
||||
| **Mobile** | Responsive، PWA (planned) |
|
||||
|
||||
---
|
||||
|
||||
## ۸. وضعیت مهاجرت
|
||||
|
||||
| مهاجرت | وضعیت | درصد |
|
||||
|--------|--------|------|
|
||||
| FrontOffice BFF → gRPC | ✅ | 100% |
|
||||
| BackOffice BFF → gRPC | ✅ | 100% |
|
||||
| API Gateway حذف | ✅ | 100% |
|
||||
| Data Migration (Users) | ✅ | 100% |
|
||||
| Data Migration (Products) | ✅ | 100% |
|
||||
| Data Migration (Orders) | ✅ | 100% |
|
||||
| Data Migration (Club/Network) | ✅ | 100% |
|
||||
| Geography Seeder | ✅ | 100% |
|
||||
| Proto package unification | ✅ | 100% |
|
||||
@@ -0,0 +1,329 @@
|
||||
# 🔌 API، Protobuf و یکپارچهسازی خارجی
|
||||
|
||||
> **منابع ادغامشده:** `FRONTOFFICE-CMS-API-COMPATIBILITY.md`, `REMAINING-TASKS.md`, `chatika-integration.md`, `payment-gateway.md`, `club-feature-management-services.md`
|
||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴
|
||||
|
||||
---
|
||||
|
||||
## ۱. معماری ارتباطات
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ External Services │
|
||||
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ ZarinPal│ │ Kavenegar│ │ DayaLoan│ │ Chatika │ │
|
||||
│ │ (IPG) │ │ (SMS) │ │ (Loan) │ │ (AI) │ │
|
||||
│ └────┬────┘ └────┬─────┘ └────┬────┘ └────┬────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ ┌────▼───────────▼────────────▼────────────▼────┐ │
|
||||
│ │ CMS Microservice │ │
|
||||
│ │ (gRPC Server + Hangfire + EF Core) │ │
|
||||
│ └────────────────┬───────────────────────────────┘ │
|
||||
│ │ gRPC (Protobuf v3) │
|
||||
│ ┌───────────┼───────────┐ │
|
||||
│ ┌────▼────┐ ┌────▼────┐ │
|
||||
│ │BackOffice│ │FrontOffice│ │
|
||||
│ │(Blazor │ │(Blazor │ │
|
||||
│ │ WASM) │ │ Server) │ │
|
||||
│ └─────────┘ └──────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۲. gRPC Proto Definitions
|
||||
|
||||
### ۲.۱ لیست کامل سرویسها
|
||||
|
||||
```protobuf
|
||||
// ===== product.proto =====
|
||||
service ProductService {
|
||||
rpc GetProducts (GetProductsRequest) returns (GetProductsResponse);
|
||||
rpc GetProductById (GetProductByIdRequest) returns (ProductMessage);
|
||||
rpc CreateProduct (CreateProductRequest) returns (CreateProductResponse);
|
||||
rpc UpdateProduct (UpdateProductRequest) returns (UpdateProductResponse);
|
||||
rpc DeleteProduct (DeleteProductRequest) returns (Empty);
|
||||
rpc GetProductsPaged (GetProductsPagedRequest) returns (GetProductsPagedResponse);
|
||||
}
|
||||
|
||||
// ===== order.proto =====
|
||||
service OrderService {
|
||||
rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);
|
||||
rpc GetOrders (GetOrdersRequest) returns (GetOrdersResponse);
|
||||
rpc GetOrderById (GetOrderByIdRequest) returns (OrderMessage);
|
||||
rpc UpdateOrderStatus (UpdateOrderStatusRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== user.proto =====
|
||||
service UserService {
|
||||
rpc Register (RegisterRequest) returns (AuthResponse);
|
||||
rpc Login (LoginRequest) returns (AuthResponse);
|
||||
rpc GetProfile (GetProfileRequest) returns (UserProfileMessage);
|
||||
rpc UpdateProfile (UpdateProfileRequest) returns (Empty);
|
||||
rpc SendOtp (SendOtpRequest) returns (SendOtpResponse);
|
||||
rpc VerifyOtp (VerifyOtpRequest) returns (VerifyOtpResponse);
|
||||
}
|
||||
|
||||
// ===== club.proto =====
|
||||
service ClubService {
|
||||
rpc GetNetworkTree (GetNetworkTreeRequest) returns (NetworkTreeResponse);
|
||||
rpc GetBalance (GetBalanceRequest) returns (BalanceResponse);
|
||||
rpc ReadContract (ReadContractRequest) returns (ContractResponse);
|
||||
rpc RequestContractOtp (RequestOtpRequest) returns (OtpResponse);
|
||||
rpc VerifyContractOtp (VerifyOtpRequest) returns (VerifyOtpResponse);
|
||||
rpc AcceptContract (AcceptContractRequest) returns (AcceptContractResponse);
|
||||
rpc GetClubFeatures (GetFeaturesRequest) returns (FeaturesResponse);
|
||||
}
|
||||
|
||||
// ===== payment.proto =====
|
||||
service PaymentService {
|
||||
rpc CreatePayment (CreatePaymentRequest) returns (CreatePaymentResponse);
|
||||
rpc VerifyPayment (VerifyPaymentRequest) returns (VerifyPaymentResponse);
|
||||
rpc GetPaymentStatus (PaymentStatusRequest) returns (PaymentStatusResponse);
|
||||
}
|
||||
|
||||
// ===== blog.proto =====
|
||||
service BlogService {
|
||||
rpc GetPosts (GetPostsRequest) returns (GetPostsResponse);
|
||||
rpc GetPostBySlug (GetPostBySlugRequest) returns (BlogPostMessage);
|
||||
rpc CreatePost (CreatePostRequest) returns (CreatePostResponse);
|
||||
rpc UpdatePost (UpdatePostRequest) returns (Empty);
|
||||
rpc DeletePost (DeletePostRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== inventory.proto =====
|
||||
service InventoryService {
|
||||
rpc GetInventory (GetInventoryRequest) returns (InventoryMessage);
|
||||
rpc UpdateStock (UpdateStockRequest) returns (Empty);
|
||||
rpc GetAllInventories (GetAllRequest) returns (InventoryListResponse);
|
||||
}
|
||||
|
||||
// ===== sitepage.proto =====
|
||||
service SitePageService {
|
||||
rpc GetPage (GetPageRequest) returns (SitePageMessage);
|
||||
rpc SaveSettings (SaveSettingsRequest) returns (Empty);
|
||||
rpc GetAllPages (Empty) returns (PageListResponse);
|
||||
}
|
||||
|
||||
// ===== file.proto =====
|
||||
service FileService {
|
||||
rpc Upload (stream UploadRequest) returns (UploadResponse);
|
||||
rpc Download (DownloadRequest) returns (stream DownloadResponse);
|
||||
rpc Delete (DeleteFileRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== category.proto =====
|
||||
service CategoryService {
|
||||
rpc GetCategories (GetCategoriesRequest) returns (CategoryListResponse);
|
||||
rpc CreateCategory (CreateCategoryRequest) returns (CreateCategoryResponse);
|
||||
rpc UpdateCategory (UpdateCategoryRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== config.proto =====
|
||||
service SystemConfigService {
|
||||
rpc GetConfig (GetConfigRequest) returns (ConfigResponse);
|
||||
rpc UpdateConfig (UpdateConfigRequest) returns (Empty);
|
||||
rpc GetAllConfigs (Empty) returns (ConfigListResponse);
|
||||
}
|
||||
```
|
||||
|
||||
### ۲.۲ Shared Messages
|
||||
|
||||
```protobuf
|
||||
// ===== common.proto =====
|
||||
message PaginationState {
|
||||
int32 skip = 1;
|
||||
int32 take = 2;
|
||||
}
|
||||
|
||||
message PaginatedResponse {
|
||||
int32 totalCount = 1;
|
||||
int32 pageSize = 2;
|
||||
int32 currentPage = 3;
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۳. External Service Integration
|
||||
|
||||
### ۳.۱ ZarinPal (پرداخت)
|
||||
|
||||
```csharp
|
||||
public class ZarinPalService : IPaymentGateway
|
||||
{
|
||||
// Config
|
||||
private readonly string _merchantId;
|
||||
private readonly bool _isSandbox;
|
||||
|
||||
// Endpoints
|
||||
const string PAYMENT_URL = "https://api.zarinpal.com/pg/v4/payment/request.json";
|
||||
const string VERIFY_URL = "https://api.zarinpal.com/pg/v4/payment/verify.json";
|
||||
const string SANDBOX_URL = "https://sandbox.zarinpal.com/pg/v4/payment/request.json";
|
||||
|
||||
// Flow
|
||||
// 1. CreatePayment → Authority token
|
||||
// 2. Redirect → https://www.zarinpal.com/pg/StartPay/{Authority}
|
||||
// 3. Callback → VerifyPayment(Authority, Amount)
|
||||
// 4. Result → RefID (reference number)
|
||||
}
|
||||
```
|
||||
|
||||
### ۳.۲ Kavenegar (SMS)
|
||||
|
||||
```csharp
|
||||
public class KavenegarService : ISmsService
|
||||
{
|
||||
// Templates
|
||||
const string OTP_TEMPLATE = "verify-foursat";
|
||||
const string CONTRACT_TEMPLATE = "contract-verify";
|
||||
const string WELCOME_TEMPLATE = "club-welcome";
|
||||
|
||||
// Rate Limiting
|
||||
// ۱ SMS per phone per 60 seconds
|
||||
// ۵ SMS per phone per hour
|
||||
// ۲۰ SMS per phone per day
|
||||
|
||||
public async Task SendOtpAsync(string phone, string code)
|
||||
{
|
||||
await _api.VerifyLookup(phone, code, OTP_TEMPLATE);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ۳.۳ Daya Loan (وام)
|
||||
|
||||
```csharp
|
||||
public class DayaLoanService : ILoanService
|
||||
{
|
||||
// Hangfire job — هر ۱۵ دقیقه
|
||||
// Polly retry: 3 attempts, exponential backoff (2s, 4s, 8s)
|
||||
// Mock mode for staging (auto-approve)
|
||||
|
||||
public async Task<LoanResult> RequestLoanAsync(Guid userId, decimal amount)
|
||||
{
|
||||
if (_options.UseMock)
|
||||
return LoanResult.Approved(amount);
|
||||
|
||||
var response = await _httpClient.PostAsync(
|
||||
$"{_baseUrl}/api/loans/request",
|
||||
new { UserId = userId, Amount = amount });
|
||||
|
||||
return MapResponse(response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ۳.۴ Chatika (AI)
|
||||
|
||||
```csharp
|
||||
public class ChatikaService : IAiChatService
|
||||
{
|
||||
// Hangfire job — هر ۵ دقیقه
|
||||
// Polly retry: 3 attempts
|
||||
// Only for active club members
|
||||
|
||||
public async Task<string> GetResponseAsync(string userMessage)
|
||||
{
|
||||
var response = await _httpClient.PostAsync(
|
||||
$"{_baseUrl}/api/chat",
|
||||
new { Message = userMessage });
|
||||
|
||||
return response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۴. API Compatibility Layer
|
||||
|
||||
### ۴.۱ FrontOffice Service Pattern
|
||||
|
||||
```csharp
|
||||
// هر سرویس در FrontOffice یک wrapper بر gRPC client است
|
||||
public class ProductService : IProductService
|
||||
{
|
||||
private readonly ProductServiceClient _client;
|
||||
|
||||
public ProductService(ProductServiceClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public async Task<ProductListResult> GetProductsPagedAsync(
|
||||
int skip, int take, Guid? categoryId = null, string? search = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new GetProductsPagedRequest {
|
||||
Pagination = new PaginationState { Skip = skip, Take = take },
|
||||
CategoryId = categoryId?.ToString() ?? "",
|
||||
SearchTerm = search ?? ""
|
||||
};
|
||||
|
||||
var response = await _client.GetProductsPagedAsync(request);
|
||||
|
||||
return new ProductListResult(
|
||||
response.Products.Select(MapToDto).ToList(),
|
||||
response.TotalCount);
|
||||
}
|
||||
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unavailable)
|
||||
{
|
||||
// CMS is down — show cached data or error
|
||||
throw new ServiceUnavailableException("CMS service unavailable");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ۴.۲ Error Handling
|
||||
|
||||
| gRPC Status | HTTP Equivalent | Handling |
|
||||
|------------|-----------------|----------|
|
||||
| `OK` | 200 | Return data |
|
||||
| `NotFound` | 404 | Show "not found" message |
|
||||
| `InvalidArgument` | 400 | Show validation errors |
|
||||
| `Unauthenticated` | 401 | Redirect to login |
|
||||
| `PermissionDenied` | 403 | Show "access denied" |
|
||||
| `Unavailable` | 503 | Show "service down" |
|
||||
| `Internal` | 500 | Show generic error |
|
||||
|
||||
---
|
||||
|
||||
## ۵. Proto Package Distribution
|
||||
|
||||
```
|
||||
CMS/src/Protos/*.proto
|
||||
│
|
||||
▼
|
||||
pack-protos.sh
|
||||
│
|
||||
▼
|
||||
Foursat.CMSMicroservice.Protobuf.nupkg (v1.0.x)
|
||||
│
|
||||
▼
|
||||
Push to BaGet (http://localhost:5555) or Nexus
|
||||
│
|
||||
▼
|
||||
BackOffice: <PackageReference Include="Foursat.CMSMicroservice.Protobuf" />
|
||||
FrontOffice: <PackageReference Include="Foursat.CMSMicroservice.Protobuf" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۶. Remaining Tasks / Integration Gaps
|
||||
|
||||
| آیتم | اولویت | وضعیت |
|
||||
|------|---------|--------|
|
||||
| Product Bundle API | Medium | ⬜ Proto + Handler needed |
|
||||
| Manual Payment API | Low | ⬜ Design only |
|
||||
| SignalR for Chatika | Low | ⬜ Replace polling |
|
||||
| File upload streaming | Done | ✅ |
|
||||
| Blog search | Done | ✅ |
|
||||
| Inventory autocomplete | Done | ✅ |
|
||||
| Lazy load pagination | Done | ✅ |
|
||||
| Rate limiting (API level) | Medium | ⬜ |
|
||||
| API versioning | Low | ⬜ |
|
||||
Reference in New Issue
Block a user