feat: Complete overhaul of FourSat documentation structure and content

- Added FINAL-STATUS.md detailing project completion and key metrics
- Created QUICK-REFERENCE.md for quick access to essential documents
- Updated README.md with project overview and quick start guide
- Established STRUCTURE.md outlining the final documentation structure
- Organized and archived old files, ensuring a clean and efficient directory
- Enhanced documentation quality with comprehensive metrics and checklists
This commit is contained in:
masoodafar-web
2025-12-04 17:32:31 +03:30
commit 119e870a26
67 changed files with 210873 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# BackOffice.BFF
BackOffice BFF
@@ -0,0 +1,593 @@
# BackOffice.BFF - CMS Integration Documentation
**Date**: 2025-11-30
**Status**: ✅ Integrated
**CMS Package Version**: 0.0.140
---
## 📋 Overview
BackOffice.BFF به CMS Microservice متصل شد و حالا می‌تواند به سرویس‌های Network-Club-Commission دسترسی داشته باشد.
این Integration به BackOffice امکان می‌دهد:
- مدیریت کامیسیون‌های کاربران
- مشاهده ساختار شبکه Binary Tree
- فعال/غیرفعال کردن عضویت باشگاه
- مشاهده گزارشات هفتگی کمیسیون
---
## 🏗️ Architecture
```
┌─────────────────────────────────────────────────────────┐
│ BackOffice.BFF (API Gateway) │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ IApplicationContractContext │ │
│ │ - Users (existing) │ │
│ │ - Products (existing) │ │
│ │ - Orders (existing) │ │
│ │ ✨ Commissions (NEW) │ │
│ │ ✨ NetworkMemberships (NEW) │ │
│ │ ✨ ClubMemberships (NEW) │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ gRPC │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ CMS Microservice │
│ https://cms.kbs1.ir │
│ │
│ ┌─────────────────────┐ ┌─────────────────────────┐ │
│ │ CommissionContract │ │ NetworkMembershipContract│ │
│ │ - GetWeeklyPool │ │ - GetUserNetworkInfo │ │
│ │ - GetUserPayouts │ │ - GetNetworkTree │ │
│ │ - ProcessWithdrawal │ │ - CalculateLegBalances │ │
│ └─────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────┐ │
│ │ ClubMembershipContract│ │
│ │ - ActivateClub │ │
│ │ - DeactivateClub │ │
│ │ - GetClubStatus │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
---
## 📦 Integration Details
### 1️⃣ NuGet Package
**Package**: `Foursat.CMSMicroservice.Protobuf`
**Version**: `0.0.140` (Updated from 0.0.137)
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Domain/BackOffice.BFF.Domain.csproj`
```xml
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.140" />
```
**What's New in 0.0.140**:
-`commission.proto` - Commission system contracts
-`networkmembership.proto` - Binary tree network contracts
-`clubmembership.proto` - Club membership contracts
-`configuration.proto` - System configuration contracts
---
### 2️⃣ Interface Definition
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs`
```csharp
public interface IApplicationContractContext
{
// ... existing services ...
// Network & Commission System (NEW)
CommissionContract.CommissionContractClient Commissions { get; }
NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships { get; }
ClubMembershipContract.ClubMembershipContractClient ClubMemberships { get; }
}
```
---
### 3️⃣ Implementation
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs`
```csharp
public class ApplicationContractContext : IApplicationContractContext
{
// ... existing implementations ...
// Network & Commission System
public CommissionContract.CommissionContractClient Commissions
=> GetService<CommissionContract.CommissionContractClient>();
public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships
=> GetService<NetworkMembershipContract.NetworkMembershipContractClient>();
public ClubMembershipContract.ClubMembershipContractClient ClubMemberships
=> GetService<ClubMembershipContract.ClubMembershipContractClient>();
}
```
---
### 4️⃣ gRPC Configuration
**File**: `/BackOffice.BFF/src/BackOffice.BFF.WebApi/appsettings.json`
```json
{
"GrpcChannelOptions": {
"FMSMSAddress": "https://dl.afrino.co",
"CMSMSAddress": "https://cms.kbs1.ir"
}
}
```
**Auto-Registration**:
- gRPC clients به صورت خودکار توسط `ConfigureGrpcServices.BatchRegisterGrpcClients()` ثبت می‌شوند
- بر اساس نام Assembly (`CMSMicroservice.Protobuf`)
- با Address مشخص شده در `appsettings.json`
---
## 🔌 Available Services
### 1️⃣ CommissionContract
**Namespace**: `CMSMicroservice.Protobuf.Protos.Commission`
#### Commands:
```csharp
// محاسبه بالانس های هفتگی
await _context.Commissions.CalculateWeeklyBalancesAsync(
new CalculateWeeklyBalancesRequest { WeekNumber = "2025-W48" });
// محاسبه Pool هفتگی
await _context.Commissions.CalculateWeeklyCommissionPoolAsync(
new CalculateWeeklyCommissionPoolRequest { WeekNumber = "2025-W48" });
// پردازش Payout های کاربران
await _context.Commissions.ProcessUserPayoutsAsync(
new ProcessUserPayoutsRequest { WeekNumber = "2025-W48" });
// درخواست برداشت توسط کاربر
await _context.Commissions.RequestWithdrawalAsync(
new RequestWithdrawalRequest
{
UserId = 123,
Amount = 500000
});
// پردازش برداشت (توسط Admin)
await _context.Commissions.ProcessWithdrawalAsync(
new ProcessWithdrawalRequest
{
PayoutId = 456,
Status = WithdrawalStatus.Approved
});
```
#### Queries:
```csharp
// دریافت Pool هفتگی
var pool = await _context.Commissions.GetWeeklyCommissionPoolAsync(
new GetWeeklyCommissionPoolRequest { WeekNumber = "2025-W48" });
// دریافت Payout های یک کاربر
var payouts = await _context.Commissions.GetUserPayoutsAsync(
new GetUserPayoutsRequest
{
UserId = 123,
PageNumber = 1,
PageSize = 10
});
// دریافت تاریخچه Withdrawal ها
var withdrawals = await _context.Commissions.GetWithdrawalHistoryAsync(
new GetWithdrawalHistoryRequest
{
UserId = 123,
Status = WithdrawalStatus.Pending
});
```
---
### 2️⃣ NetworkMembershipContract
**Namespace**: `CMSMicroservice.Protobuf.Protos.NetworkMembership`
#### Queries:
```csharp
// دریافت اطلاعات شبکه یک کاربر
var networkInfo = await _context.NetworkMemberships.GetUserNetworkInfoAsync(
new GetUserNetworkInfoRequest { UserId = 123 });
// دریافت درخت شبکه (Binary Tree)
var tree = await _context.NetworkMemberships.GetNetworkTreeAsync(
new GetNetworkTreeRequest
{
RootUserId = 123,
MaxDepth = 5
});
// دریافت بالانس های هفتگی
var balances = await _context.NetworkMemberships.GetUserWeeklyBalancesAsync(
new GetUserWeeklyBalancesRequest
{
UserId = 123,
WeekNumber = "2025-W48"
});
// محاسبه بالانس Leg های یک کاربر
var legBalances = await _context.NetworkMemberships.CalculateLegBalancesAsync(
new CalculateLegBalancesRequest { UserId = 123 });
```
---
### 3️⃣ ClubMembershipContract
**Namespace**: `CMSMicroservice.Protobuf.Protos.ClubMembership`
#### Commands:
```csharp
// فعال کردن عضویت باشگاه
await _context.ClubMemberships.ActivateClubMembershipAsync(
new ActivateClubMembershipRequest
{
UserId = 123,
ActivationDate = Timestamp.FromDateTime(DateTime.UtcNow)
});
// غیرفعال کردن عضویت باشگاه
await _context.ClubMemberships.DeactivateClubMembershipAsync(
new DeactivateClubMembershipRequest
{
UserId = 123,
Reason = "User request"
});
```
#### Queries:
```csharp
// دریافت وضعیت عضویت باشگاه
var status = await _context.ClubMemberships.GetClubMembershipStatusAsync(
new GetClubMembershipStatusRequest { UserId = 123 });
// لیست تمام اعضای باشگاه
var members = await _context.ClubMemberships.GetAllClubMembersAsync(
new GetAllClubMembersRequest
{
IsActive = true,
PageNumber = 1,
PageSize = 20
});
```
---
## 📝 Usage Example in BFF
### Example 1: Create Commission Query Handler
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserPayouts/GetUserPayoutsQuery.cs`
```csharp
public record GetUserPayoutsQuery : IRequest<GetUserPayoutsResponseDto>
{
public long UserId { get; init; }
public int PageNumber { get; init; } = 1;
public int PageSize { get; init; } = 10;
}
public class GetUserPayoutsQueryHandler
: IRequestHandler<GetUserPayoutsQuery, GetUserPayoutsResponseDto>
{
private readonly IApplicationContractContext _context;
public GetUserPayoutsQueryHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<GetUserPayoutsResponseDto> Handle(
GetUserPayoutsQuery request,
CancellationToken cancellationToken)
{
var response = await _context.Commissions.GetUserPayoutsAsync(
request.Adapt<GetUserPayoutsRequest>(),
cancellationToken: cancellationToken);
return response.Adapt<GetUserPayoutsResponseDto>();
}
}
```
---
### Example 2: Create Network Tree Query Handler
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/NetworkCQ/Queries/GetNetworkTree/GetNetworkTreeQuery.cs`
```csharp
public record GetNetworkTreeQuery : IRequest<GetNetworkTreeResponseDto>
{
public long RootUserId { get; init; }
public int MaxDepth { get; init; } = 5;
}
public class GetNetworkTreeQueryHandler
: IRequestHandler<GetNetworkTreeQuery, GetNetworkTreeResponseDto>
{
private readonly IApplicationContractContext _context;
public GetNetworkTreeQueryHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<GetNetworkTreeResponseDto> Handle(
GetNetworkTreeQuery request,
CancellationToken cancellationToken)
{
var response = await _context.NetworkMemberships.GetNetworkTreeAsync(
request.Adapt<GetNetworkTreeRequest>(),
cancellationToken: cancellationToken);
return response.Adapt<GetNetworkTreeResponseDto>();
}
}
```
---
### Example 3: Create Club Activation Command Handler
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/ClubCQ/Commands/ActivateClub/ActivateClubCommand.cs`
```csharp
public record ActivateClubCommand : IRequest<Unit>
{
public long UserId { get; init; }
public DateTimeOffset? ActivationDate { get; init; }
}
public class ActivateClubCommandHandler
: IRequestHandler<ActivateClubCommand, Unit>
{
private readonly IApplicationContractContext _context;
public ActivateClubCommandHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<Unit> Handle(
ActivateClubCommand request,
CancellationToken cancellationToken)
{
await _context.ClubMemberships.ActivateClubMembershipAsync(
request.Adapt<ActivateClubMembershipRequest>(),
cancellationToken: cancellationToken);
return Unit.Value;
}
}
```
---
## 🔐 Authentication & Authorization
**JWT Token**:
- BackOffice.BFF به CMS با JWT Token متصل می‌شود
- Token از `ITokenProvider` گرفته می‌شود
- در Header با کلید `Authorization: Bearer {token}` ارسال می‌شود
**Implementation در `ConfigureGrpcServices.cs`**:
```csharp
private static async Task CallCredentials(
AuthInterceptorContext context,
Metadata metadata,
IServiceProvider serviceProvider)
{
var provider = serviceProvider.GetRequiredService<ITokenProvider>();
var token = await provider.GetTokenAsync();
metadata.Add("Authorization", $"Bearer {token}");
}
```
---
## 🧪 Testing
### Test Connection:
```csharp
// در یک Controller یا Handler:
var pool = await _context.Commissions.GetWeeklyCommissionPoolAsync(
new GetWeeklyCommissionPoolRequest { WeekNumber = "2025-W48" });
Console.WriteLine($"Total Pool Value: {pool.TotalPoolValue}");
Console.WriteLine($"Active Members: {pool.ActiveMembersCount}");
```
**Expected Output**:
```
Total Pool Value: 50000000
Active Members: 120
```
---
### Test with Postman/Swagger:
1. Start BackOffice.BFF:
```bash
cd /home/masoud/Apps/project/FourSat/BackOffice.BFF/src
dotnet run --project BackOffice.BFF.WebApi
```
2. Call API endpoint (example):
```http
GET /api/commission/weekly-pool?weekNumber=2025-W48
Authorization: Bearer {your-token}
```
3. Expected Response:
```json
{
"weekNumber": "2025-W48",
"totalPoolValue": 50000000,
"activeMembersCount": 120,
"isCalculated": true
}
```
---
## 📊 Status & Metrics
| Component | Status | Version | Notes |
|-----------|--------|---------|-------|
| **CMS Protobuf Package** | ✅ Active | 0.0.140 | With Network-Club-Commission |
| **gRPC Connection** | ✅ Configured | - | https://cms.kbs1.ir |
| **Auto-Registration** | ✅ Active | - | Via BatchRegisterGrpcClients |
| **Commission Client** | ✅ Ready | - | All commands & queries available |
| **Network Client** | ✅ Ready | - | Binary tree queries available |
| **Club Client** | ✅ Ready | - | Activation/Deactivation available |
| **Authentication** | ✅ Configured | JWT | Via ITokenProvider |
---
## 🔗 Related Documentation
### CMS Side:
- **Implementation Progress**: `/CMS/docs/implementation-progress.md`
- **Network System Design**: `/CMS/docs/network-club-commission-system.md`
- **Monitoring Setup**: `/CMS/docs/monitoring-alerts-consolidated-report.md`
- **Migration Guide**: `/CMS/docs/migration-network-parent-guide.md`
- **Binary Tree Registration**: `/CMS/docs/binary-tree-registration-guide.md`
### BFF Side:
- **This Document**: `/BackOffice.BFF/docs/cms-integration.md`
- **README**: `/BackOffice.BFF/README.md`
---
## 🚀 Next Steps
### Immediate:
1. ✅ Integration completed
2. ⏳ Create Commission/Network/Club CQRS handlers in BFF
3. ⏳ Add API endpoints in BackOffice.BFF.WebApi
4. ⏳ Test integration with real data
### Short-term:
5. ⏳ Add Swagger documentation for new endpoints
6. ⏳ Implement error handling for gRPC calls
7. ⏳ Add logging for commission operations
8. ⏳ Create admin dashboard for network visualization
### Long-term:
9. ⏳ Add real-time notifications (SignalR) for commission updates
10. ⏳ Implement caching for frequently accessed data
11. ⏳ Add reporting/analytics endpoints
12. ⏳ Performance optimization for large network trees
---
## 📞 Troubleshooting
### Issue 1: gRPC Connection Failed
**Error**: `Status(StatusCode="Unavailable", Detail="...")`
**Solutions**:
1. Check CMS service is running: `https://cms.kbs1.ir`
2. Verify network connectivity
3. Check firewall settings
4. Verify SSL certificate is valid
---
### Issue 2: Authentication Failed
**Error**: `Status(StatusCode="Unauthenticated", Detail="...")`
**Solutions**:
1. Verify `ITokenProvider` is registered in DI
2. Check JWT token is valid and not expired
3. Verify token has correct claims/permissions
4. Check Authorization header is being sent
---
### Issue 3: Package Version Mismatch
**Error**: `The type or namespace 'CommissionContract' could not be found`
**Solutions**:
1. Update package version in `.csproj`:
```xml
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.140" />
```
2. Run `dotnet restore`
3. Clean and rebuild solution
---
### Issue 4: Method Not Found
**Error**: `Method 'GetWeeklyPool' not found on service 'CommissionContract'`
**Solutions**:
1. Verify CMS service has the latest code deployed
2. Check Protobuf contract matches between CMS and BFF
3. Update both CMS and BFF to latest versions
4. Restart both services
---
## 🎯 Summary
### ✅ Completed:
- CMS Protobuf package updated to 0.0.140
- 3 new gRPC clients added to BFF:
* CommissionContract (8+ methods)
* NetworkMembershipContract (6+ methods)
* ClubMembershipContract (4+ methods)
- Auto-registration configured
- Authentication via JWT configured
- Build successful (0 errors)
### ⏳ Pending:
- Create CQRS handlers for Commission operations
- Create CQRS handlers for Network operations
- Create CQRS handlers for Club operations
- Add API Controllers/Endpoints
- Add Swagger documentation
- Integration testing
### 🔑 Key Points:
- **No manual registration needed**: gRPC clients auto-register via `BatchRegisterGrpcClients()`
- **Authentication handled**: JWT token automatically added to all requests
- **Type-safe**: All Protobuf contracts are strongly typed
- **Easy to use**: Simple interface via `IApplicationContractContext`
---
**Last Updated**: 2025-11-30
**Build Status**: ✅ Success
**Ready for**: Handler implementation & API endpoint creation
@@ -0,0 +1,600 @@
# BackOffice.BFF - Discount Shop Integration Plan
**تاریخ ایجاد**: 1403/09/13 (2024-12-04)
**وضعیت**: 📋 برنامه‌ریزی
**اولویت**: 🔴 بالا
---
## 📊 خلاصه وضعیت
### ✅ تکمیل شده در CMS
- **Phase 9: Club Discount Shop System** - 100% ✅
- 6 Entities (Category, Product, Cart, Order)
- 13 Commands + 6 Queries + 9 Validators
- 4 Proto Files (19 gRPC RPCs)
- 4 gRPC Services
- Migration: AddDiscountShopSystem
### ⏳ نیاز به پیاده‌سازی در BackOffice.BFF
- **19 Handler** برای 4 سرویس جدید
- **4 Client Interface** در IApplicationContractContext
- **Test و Validation**
---
## 🎯 امکانات جدید برای Admin Panel
### 1️⃣ مدیریت محصولات فروشگاه تخفیفی (5 API)
**سرویس**: `DiscountProductContract`
#### الف. ایجاد محصول جدید
- **Handler**: `CreateDiscountProductHandler`
- **Request**:
```csharp
- Title (عنوان محصول)
- ShortInfomation (توضیحات کوتاه)
- FullInformation (توضیحات کامل)
- Price (قیمت به تومان)
- MaxDiscountPercent (حداکثر درصد تخفیف قابل استفاده از کیف پول تخفیف - 0 تا 100)
- ImagePath (مسیر تصویر اصلی)
- ThumbnailPath (مسیر تصویر کوچک)
- InitialCount (تعداد اولیه موجودی)
- SortOrder (ترتیب نمایش)
- IsActive (فعال/غیرفعال)
- CategoryIds (لیست شناسه دسته‌بندی‌ها)
```
- **Response**: ProductId (شناسه محصول ایجاد شده)
- **کاربرد Admin**: ایجاد محصول جدید در فروشگاه تخفیفی
#### ب. ویرایش محصول
- **Handler**: `UpdateDiscountProductHandler`
- **Request**: همان فیلدهای بالا + ProductId
- **کاربرد Admin**: ویرایش اطلاعات محصول موجود
#### ج. حذف محصول
- **Handler**: `DeleteDiscountProductHandler`
- **Request**: ProductId
- **کاربرد Admin**: حذف محصول از فروشگاه
#### د. دریافت جزئیات محصول
- **Handler**: `GetDiscountProductByIdHandler`
- **Request**: ProductId
- **Response**: تمام اطلاعات محصول + لیست دسته‌بندی‌ها + موجودی باقی‌مانده
- **کاربرد Admin**: مشاهده جزئیات کامل یک محصول
#### ه. لیست محصولات با فیلتر
- **Handler**: `GetDiscountProductsHandler`
- **Request**:
```csharp
- CategoryId (nullable - فیلتر بر اساس دسته‌بندی)
- SearchQuery (nullable - جستجو در عنوان و توضیحات)
- MinPrice (nullable - حداقل قیمت)
- MaxPrice (nullable - حداکثر قیمت)
- IsActive (nullable - فیلتر فعال/غیرفعال)
- InStock (nullable - فقط موجود در انبار)
- PageNumber (شماره صفحه)
- PageSize (تعداد آیتم در صفحه)
```
- **Response**:
```csharp
- MetaData (اطلاعات صفحه‌بندی)
- Models (لیست محصولات)
```
- **کاربرد Admin**: مدیریت و جستجوی محصولات
---
### 2️⃣ مدیریت دسته‌بندی محصولات (4 API)
**سرویس**: `DiscountCategoryContract`
#### الف. ایجاد دسته‌بندی جدید
- **Handler**: `CreateDiscountCategoryHandler`
- **Request**:
```csharp
- Name (نام لاتین برای URL)
- Title (عنوان فارسی)
- Description (nullable - توضیحات)
- ImagePath (nullable - تصویر دسته‌بندی)
- ParentCategoryId (nullable - دسته‌بندی والد برای ساختار درختی)
- SortOrder (ترتیب نمایش)
- IsActive (فعال/غیرفعال)
```
- **Response**: CategoryId
- **کاربرد Admin**: ایجاد دسته‌بندی جدید (با قابلیت ساختار چند سطحی)
#### ب. ویرایش دسته‌بندی
- **Handler**: `UpdateDiscountCategoryHandler`
- **Request**: همان فیلدهای بالا + CategoryId
- **کاربرد Admin**: ویرایش دسته‌بندی موجود
#### ج. حذف دسته‌بندی
- **Handler**: `DeleteDiscountCategoryHandler`
- **Request**: CategoryId
- **Response**: Success/Failure
- **Logic**:
- چک می‌کند اگر این دسته‌بندی زیرمجموعه دارد → خطا
- چک می‌کند اگر محصولی به این دسته‌بندی متصل است → خطا
- در غیر این صورت حذف می‌شود
- **کاربرد Admin**: حذف ایمن دسته‌بندی
#### د. دریافت درخت دسته‌بندی‌ها
- **Handler**: `GetDiscountCategoriesHandler`
- **Request**:
```csharp
- ParentCategoryId (nullable)
* اگر null باشد: دسته‌بندی‌های ریشه (Root) برگردانده می‌شود
* اگر مقدار داشته باشد: زیرمجموعه‌های آن دسته‌بندی برگردانده می‌شود
- IsActive (nullable - فیلتر فعال/غیرفعال)
```
- **Response**:
```csharp
- List<DiscountCategoryDto> (ساختار recursive با Children)
```
- **کاربرد Admin**: مشاهده ساختار درختی دسته‌بندی‌ها
---
### 3️⃣ مدیریت سبد خرید کاربران (5 API)
**سرویس**: `DiscountShoppingCartContract`
> **توجه**: این APIها در Admin Panel کمتر استفاده می‌شوند، اما برای Support و Troubleshooting مفید هستند.
#### الف. افزودن به سبد خرید (Support)
- **Handler**: `AddToCartHandler`
- **Request**: UserId, ProductId, Count
- **کاربرد Admin**: کمک به کاربر در افزودن محصول به سبد (Support)
#### ب. حذف از سبد خرید (Support)
- **Handler**: `RemoveFromCartHandler`
- **Request**: UserId, ProductId
- **کاربرد Admin**: کمک به کاربر در حذف آیتم از سبد
#### ج. تغییر تعداد آیتم (Support)
- **Handler**: `UpdateCartItemCountHandler`
- **Request**: UserId, ProductId, NewCount
- **کاربرد Admin**: اصلاح تعداد آیتم در سبد کاربر
#### د. مشاهده سبد خرید کاربر
- **Handler**: `GetUserCartHandler`
- **Request**: UserId
- **Response**:
```csharp
- List<CartItemDto>
* ProductId
* ProductTitle
* ProductImagePath
* UnitPrice (قیمت واحد)
* MaxDiscountPercent
* Count (تعداد)
* TotalPrice (قیمت کل = UnitPrice × Count)
* DiscountAmount (مقدار تخفیف قابل استفاده)
* FinalPrice (قیمت نهایی بعد از تخفیف)
* ProductRemainingCount (موجودی باقی‌مانده)
- TotalPrice (مجموع قیمت کل سبد)
- TotalDiscountAmount (مجموع تخفیف قابل استفاده)
- FinalPrice (مجموع قیمت نهایی)
```
- **کاربرد Admin**: بررسی سبد خرید کاربر برای Support
#### ه. پاک کردن سبد خرید
- **Handler**: `ClearCartHandler`
- **Request**: UserId
- **کاربرد Admin**: پاک کردن کامل سبد خرید کاربر (در صورت نیاز)
---
### 4️⃣ مدیریت سفارشات فروشگاه تخفیفی (5 API)
**سرویس**: `DiscountOrderContract`
#### الف. ثبت سفارش (کمتر استفاده می‌شود در Admin)
- **Handler**: `PlaceOrderHandler`
- **Request**:
```csharp
- UserId
- UserAddressId
- DiscountBalanceToUse (مقدار کیف پول تخفیف برای استفاده)
- Notes (nullable - یادداشت)
```
- **Response**:
```csharp
- Success
- Message
- OrderId
- GatewayAmount (مبلغ باقی‌مانده برای پرداخت از طریق درگاه)
- PaymentUrl (nullable - لینک پرداخت)
```
- **کاربرد Admin**: ثبت سفارش دستی برای کاربر (نادر)
#### ب. تکمیل پرداخت سفارش (کمتر استفاده می‌شود)
- **Handler**: `CompleteOrderPaymentHandler`
- **Request**: OrderId, TransactionId, PaymentSuccess
- **کاربرد Admin**: تایید دستی پرداخت (در صورت مشکل)
#### ج. تغییر وضعیت ارسال سفارش ⭐ **مهم**
- **Handler**: `UpdateOrderStatusHandler`
- **Request**:
```csharp
- OrderId
- NewStatus (enum: Pending, Processing, Shipped, Delivered, Cancelled)
- TrackingCode (nullable - کد رهگیری پست)
- AdminNotes (nullable - یادداشت ادمین)
```
- **Response**: Success, Message
- **کاربرد Admin**:
- تغییر وضعیت سفارش به "در حال پردازش"
- ثبت کد رهگیری پست
- تغییر وضعیت به "ارسال شده"
- تایید تحویل
- لغو سفارش
#### د. مشاهده جزئیات سفارش ⭐ **مهم**
- **Handler**: `GetOrderByIdHandler`
- **Request**: OrderId
- **Response**:
```csharp
- OrderId
- UserId
- UserName (nullable)
- Address (AddressInfo)
* Title
* Address
* PostalCode
- OrderItems (List)
* ProductId
* ProductTitle
* ProductPrice (قیمت اسنپ‌شات در زمان خرید)
* MaxDiscountPercent
* Count
* TotalPrice
* DiscountAmount
* FinalPrice
- TotalPrice (مجموع قیمت)
- DiscountBalanceUsed (مقدار استفاده شده از کیف پول تخفیف)
- GatewayAmount (مبلغ پرداخت شده از درگاه)
- PaymentTransactionId (nullable)
- DeliveryStatus (enum)
- TrackingCode (nullable)
- AdminNotes (nullable)
- Notes (یادداشت کاربر)
- OrderDate
- PaymentDate (nullable)
```
- **کاربرد Admin**: بررسی کامل سفارش
#### ه. لیست سفارشات کاربر ⭐ **مهم**
- **Handler**: `GetUserOrdersHandler`
- **Request**:
```csharp
- UserId
- PageNumber
- PageSize
```
- **Response**:
```csharp
- MetaData (صفحه‌بندی)
- Models (List<OrderSummaryDto>)
* OrderId
* TotalPrice
* DiscountBalanceUsed
* GatewayAmount
* DeliveryStatus
* ItemsCount (تعداد آیتم‌های سفارش)
* OrderDate
```
- **کاربرد Admin**: مشاهده تاریخچه سفارشات کاربر
---
## 📋 لیست کامل Handlerهای مورد نیاز
### ✅ موجود در BackOffice.BFF (35 Handler)
1. User Management (7)
2. Product Management (5)
3. Order Management (5)
4. Category/Tag (4)
5. Role & Permission (3)
6. Commission System (4)
7. Network Membership (3)
8. Club Membership (4)
### ⏳ نیاز به ایجاد (19 Handler)
#### گروه 1: Discount Product (5 Handlers)
1. ✅ `CreateDiscountProductHandler`
2. ✅ `UpdateDiscountProductHandler`
3. ✅ `DeleteDiscountProductHandler`
4. ✅ `GetDiscountProductByIdHandler`
5. ✅ `GetDiscountProductsHandler`
#### گروه 2: Discount Category (4 Handlers)
6. ✅ `CreateDiscountCategoryHandler`
7. ✅ `UpdateDiscountCategoryHandler`
8. ✅ `DeleteDiscountCategoryHandler`
9. ✅ `GetDiscountCategoriesHandler`
#### گروه 3: Discount Shopping Cart (5 Handlers)
10. ✅ `AddToCartHandler` (برای Support)
11. ✅ `RemoveFromCartHandler` (برای Support)
12. ✅ `UpdateCartItemCountHandler` (برای Support)
13. ✅ `GetUserCartHandler` ⭐
14. ✅ `ClearCartHandler`
#### گروه 4: Discount Order (5 Handlers)
15. ✅ `PlaceOrderHandler` (کمتر استفاده می‌شود)
16. ✅ `CompleteOrderPaymentHandler` (کمتر استفاده می‌شود)
17. ✅ `UpdateOrderStatusHandler` ⭐⭐⭐ **خیلی مهم**
18. ✅ `GetOrderByIdHandler` ⭐⭐⭐ **خیلی مهم**
19. ✅ `GetUserOrdersHandler` ⭐⭐ **مهم**
---
## 🏗️ تغییرات مورد نیاز در BackOffice.BFF
### 1️⃣ آپدیت IApplicationContractContext
**فایل**: `/BackOffice.BFF/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs`
```csharp
public interface IApplicationContractContext
{
// ... existing services ...
// Discount Shop System (NEW - Phase 9)
DiscountProductContract.DiscountProductContractClient DiscountProducts { get; }
DiscountCategoryContract.DiscountCategoryContractClient DiscountCategories { get; }
DiscountShoppingCartContract.DiscountShoppingCartContractClient DiscountShoppingCarts { get; }
DiscountOrderContract.DiscountOrderContractClient DiscountOrders { get; }
}
```
### 2️⃣ پیاده‌سازی در ApplicationContractContext
**فایل**: `/BackOffice.BFF/src/BackOffice.BFF.Infrastructure/Persistence/ApplicationContractContext.cs`
```csharp
public class ApplicationContractContext : IApplicationContractContext
{
// ... existing implementations ...
// Discount Shop System (NEW)
public DiscountProductContract.DiscountProductContractClient DiscountProducts { get; }
public DiscountCategoryContract.DiscountCategoryContractClient DiscountCategories { get; }
public DiscountShoppingCartContract.DiscountShoppingCartContractClient DiscountShoppingCarts { get; }
public DiscountOrderContract.DiscountOrderContractClient DiscountOrders { get; }
public ApplicationContractContext(GrpcChannel channel)
{
// ... existing initializations ...
// Discount Shop System
DiscountProducts = new DiscountProductContract.DiscountProductContractClient(channel);
DiscountCategories = new DiscountCategoryContract.DiscountCategoryContractClient(channel);
DiscountShoppingCarts = new DiscountShoppingCartContract.DiscountShoppingCartContractClient(channel);
DiscountOrders = new DiscountOrderContract.DiscountOrderContractClient(channel);
}
}
```
### 3️⃣ ایجاد Handlerها
**ساختار فولدر**:
```
BackOffice.BFF.Application/
└── DiscountShop/
├── Products/
│ ├── CreateDiscountProduct/
│ │ ├── CreateDiscountProductCommand.cs
│ │ └── CreateDiscountProductHandler.cs
│ ├── UpdateDiscountProduct/
│ ├── DeleteDiscountProduct/
│ ├── GetDiscountProductById/
│ └── GetDiscountProducts/
├── Categories/
│ ├── CreateDiscountCategory/
│ ├── UpdateDiscountCategory/
│ ├── DeleteDiscountCategory/
│ └── GetDiscountCategories/
├── Cart/
│ ├── AddToCart/
│ ├── RemoveFromCart/
│ ├── UpdateCartItemCount/
│ ├── GetUserCart/
│ └── ClearCart/
└── Orders/
├── PlaceOrder/
├── CompleteOrderPayment/
├── UpdateOrderStatus/
├── GetOrderById/
└── GetUserOrders/
```
---
## 🎨 UI Pages مورد نیاز در BackOffice
### صفحات جدید (6 صفحه)
1. **صفحه لیست محصولات فروشگاه تخفیفی** (2 روز)
- `Pages/DiscountShop/Products/ProductsList.razor`
- DataGrid با فیلترها
- دکمه‌های Create, Edit, Delete
- نمایش موجودی و MaxDiscountPercent
2. **صفحه ایجاد/ویرایش محصول** (1 روز)
- `Pages/DiscountShop/Products/ProductForm.razor`
- فرم کامل با تمام فیلدها
- انتخاب چندتایی دسته‌بندی
- آپلود تصویر
3. **صفحه مدیریت دسته‌بندی‌ها** (1.5 روز)
- `Pages/DiscountShop/Categories/CategoriesList.razor`
- نمایش درختی (Tree View)
- قابلیت Drag & Drop برای تغییر Parent
- Dialog ایجاد/ویرایش
4. **صفحه لیست سفارشات فروشگاه** (2 روز)
- `Pages/DiscountShop/Orders/OrdersList.razor`
- DataGrid با فیلترها (Status, Date Range, User)
- نمایش خلاصه: TotalPrice, DiscountUsed, GatewayAmount
- دکمه View Details
5. **صفحه جزئیات سفارش** (1.5 روز)
- `Pages/DiscountShop/Orders/OrderDetails.razor`
- نمایش کامل اطلاعات سفارش
- لیست آیتم‌های سفارش
- **تغییر وضعیت ارسال** (Dropdown)
- ثبت کد رهگیری
- یادداشت ادمین
6. **صفحه مشاهده سبد خرید کاربر** (1 روز)
- `Pages/DiscountShop/Support/UserCart.razor`
- برای Support و Troubleshooting
- نمایش محاسبات تخفیف
- قابلیت اصلاح (Add/Remove/Update)
**جمع زمان UI**: **9 روز**
---
## 📊 گزارشات مالی جدید (اختیاری - اولویت متوسط)
### گزارشات پیشنهادی:
1. **گزارش فروش فروشگاه تخفیفی**
- مجموع فروش (TotalPrice)
- مجموع تخفیف استفاده شده (DiscountBalanceUsed)
- مجموع پرداخت از درگاه (GatewayAmount)
- تفکیک بر اساس تاریخ، محصول، دسته‌بندی
2. **گزارش محبوب‌ترین محصولات**
- تعداد فروش هر محصول
- مجموع درآمد
- میانگین استفاده از تخفیف
3. **گزارش وضعیت موجودی**
- محصولات کم موجودی (RemainingCount < حد آستانه)
- هشدار اتمام موجودی
4. **گزارش استفاده از کیف پول تخفیف**
- کاربران برتر در استفاده از تخفیف
- میانگین درصد استفاده از تخفیف
- مقایسه با فروش کل
---
## ⏱️ تخمین زمان پیاده‌سازی
### BackOffice.BFF (Backend)
| مرحله | زمان | توضیحات |
|-------|------|---------|
| آپدیت Interface & Context | 30 دقیقه | اضافه کردن 4 Client |
| ایجاد 19 Handler | 3 روز | ~20 دقیقه هر Handler |
| Test & Debug | 1 روز | تست تمام Handlerها |
| **جمع** | **4 روز** | |
### BackOffice UI (Frontend)
| مرحله | زمان | توضیحات |
|-------|------|---------|
| صفحات محصولات (2 صفحه) | 3 روز | List + Form |
| صفحات دسته‌بندی (1 صفحه) | 1.5 روز | Tree View |
| صفحات سفارشات (2 صفحه) | 3.5 روز | List + Details |
| صفحه Support (سبد خرید) | 1 روز | |
| **جمع** | **9 روز** | |
### **جمع کل**: **13 روز کاری** (~2.5 هفته)
---
## 🚀 اولویت‌بندی پیاده‌سازی
### فاز 1: حداقل قابل استفاده (MVP) - 5 روز
✅ **اولویت بالا**
1. Handlerهای مدیریت محصولات (5)
2. Handlerهای مدیریت دسته‌بندی (4)
3. Handler مشاهده جزئیات سفارش (1)
4. Handler تغییر وضعیت سفارش (1)
5. صفحه لیست محصولات + فرم
6. صفحه لیست سفارشات + جزئیات
### فاز 2: قابلیت‌های Support - 3 روز
🟡 **اولویت متوسط**
1. Handlerهای سبد خرید (5)
2. Handler لیست سفارشات کاربر (1)
3. صفحه مدیریت دسته‌بندی
4. صفحه Support سبد خرید
### فاز 3: گزارشات و آمار - 5 روز
🟢 **اولویت پایین** (می‌تواند بعداً اضافه شود)
1. گزارشات مالی
2. داشبورد فروش فروشگاه
3. چارت‌های تحلیلی
---
## 📝 نکات مهم
### ⚠️ نکته 1: MaxDiscountPercent
این فیلد بسیار مهم است:
- مشخص می‌کند کاربر حداکثر چند درصد از قیمت محصول را می‌تواند با کیف پول تخفیف پرداخت کند
- مثال: قیمت = 1,000,000 تومان، MaxDiscountPercent = 70%
- حداکثر تخفیف: 700,000 تومان
- مبلغ باقی‌مانده (300,000 تومان) باید از درگاه پرداخت شود
### ⚠️ نکته 2: Snapshot محصول
وقتی سفارش ثبت می‌شود، اطلاعات محصول (عنوان، قیمت، MaxDiscountPercent) در جدول `DiscountOrderItem` ذخیره می‌شود:
- این اطلاعات Snapshot هستند و حتی اگر محصول بعداً ویرایش شود، سفارش تغییر نمی‌کند
- برای گزارش‌گیری دقیق مالی ضروری است
### ⚠️ نکته 3: Hybrid Payment Flow
جریان پرداخت ترکیبی:
1. کاربر سفارش ثبت می‌کند → `PlaceOrder`
2. CMS محاسبه می‌کند چقدر از کیف پول تخفیف استفاده شود
3. مبلغ باقی‌مانده (GatewayAmount) به کاربر نمایش داده می‌شود
4. کاربر به درگاه پرداخت می‌رود
5. بعد از بازگشت از درگاه → `CompleteOrderPayment`
6. CMS تراکنش را Verify می‌کند و DiscountBalance را کم می‌کند
### ⚠️ نکته 4: Stock Management
- هنگام `PlaceOrder`: RemainingCount کم می‌شود (Reserve)
- اگر پرداخت ناموفق باشد: باید موجودی برگردانده شود (در CompleteOrderPayment)
- Admin باید بتواند موجودی را دستی تغییر دهد
---
## 📚 مستندات مرتبط
- [CMS Implementation Progress](../CMS/implementation-progress.md) - Phase 9 Details
- [REMAINING-TASKS-CONSOLIDATED](../REMAINING-TASKS-CONSOLIDATED.md) - Overall Project Status
- [BackOffice.BFF CMS Integration](./cms-integration.md) - Existing Integration Guide
---
## ✅ Checklist پیاده‌سازی
### Backend (BackOffice.BFF)
- [ ] آپدیت IApplicationContractContext (4 Client)
- [ ] آپدیت ApplicationContractContext (Implementation)
- [ ] ایجاد 5 Handler محصولات
- [ ] ایجاد 4 Handler دسته‌بندی
- [ ] ایجاد 5 Handler سبد خرید
- [ ] ایجاد 5 Handler سفارشات
- [ ] تست تمام Handlerها
- [ ] آپدیت مستندات cms-integration.md
### Frontend (BackOffice UI)
- [ ] صفحه لیست محصولات
- [ ] صفحه فرم محصول (Create/Edit)
- [ ] صفحه مدیریت دسته‌بندی‌ها
- [ ] صفحه لیست سفارشات
- [ ] صفحه جزئیات سفارش
- [ ] صفحه Support سبد خرید
- [ ] تست UI با داده واقعی
---
**آماده شروع پیاده‌سازی؟** 🚀
+32
View File
@@ -0,0 +1,32 @@
# 📁 BackOffice.BFF - Design Files
این پوشه شامل فایل‌های طراحی BackOffice.BFF است.
---
## 📊 فایل‌ها
### Database Models:
- **`model.ndm2`** - طراحی دیتابیس BackOffice.BFF
- ابزار: Navicat Data Modeler
- محتوا: ساختار Entity ها و روابط
---
## 🔧 نحوه استفاده
```bash
# باز کردن با Navicat Data Modeler
navicat-data-modeler model.ndm2
```
---
## 🔗 مراجع
- **Handlers Status**: [`../handlers-status.md`](../handlers-status.md)
- **CMS Integration**: [`../cms-integration.md`](../cms-integration.md)
---
**تاریخ ایجاد**: ۱ دسامبر ۲۰۲۵
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff