154 lines
6.9 KiB
Markdown
154 lines
6.9 KiB
Markdown
# 🔀 جداسازی سرویسهای Admin و Customer
|
|
|
|
> آخرین بروزرسانی: February 10, 2026
|
|
> مرتبط با: [ICURRENTUSERSERVICE-IMPLEMENTATION.md](ICURRENTUSERSERVICE-IMPLEMENTATION.md)
|
|
|
|
---
|
|
|
|
## 🐛 مشکل
|
|
|
|
پنل ادمین BackOffice بجای نمایش اطلاعات **همه کاربران**، فقط اطلاعات **خود ادمین** رو نشان میداد.
|
|
|
|
### علت ریشهای:
|
|
Query Handler ها وقتی `UserId = 0` دریافت میکردند، بجای اینکه "همه کاربران" رو برگردانند، به JWT fallback میکردند و UserId ادمین رو از توکن استخراج میکردند:
|
|
|
|
```csharp
|
|
// ❌ الگوی قدیمی (مشکلدار)
|
|
var userId = request.UserId == 0
|
|
? (long.TryParse(_currentUser.UserId, out var uid) ? uid : 0) // ← fallback به JWT
|
|
: request.UserId;
|
|
```
|
|
|
|
### مشکل:
|
|
- **BackOffice (Admin)** → `UserId = 0` ارسال میکنه → Handler از JWT ادمین میخونه → فقط اطلاعات ادمین برمیگرده
|
|
- **FrontOffice (Customer)** → `UserId = 0` ارسال میکنه → Handler از JWT مشتری میخونه → اتفاقاً درسته، ولی دلیلش اشتباهه
|
|
|
|
---
|
|
|
|
## ✅ الگوی جدید
|
|
|
|
### اصل طراحی:
|
|
> **Handler ها بیخبر از JWT هستند.** وظیفه resolve کردن کاربر، به عهده **Service Layer (gRPC endpoint)** است.
|
|
|
|
### الگوی Handler:
|
|
```csharp
|
|
// ✅ الگوی جدید
|
|
// UserId = 0 → بدون فیلتر (نمایش همه) — مناسب Admin
|
|
// UserId > 0 → فیلتر بر اساس کاربر خاص — مناسب Customer یا Admin
|
|
|
|
public async Task<Result> Handle(SomeQuery request, CancellationToken ct)
|
|
{
|
|
var userId = request.UserId;
|
|
|
|
var query = _context.SomeEntity.AsNoTracking();
|
|
|
|
if (userId > 0)
|
|
query = query.Where(x => x.UserId == userId);
|
|
|
|
// userId == 0 → no filter → return all
|
|
return await query.ToListAsync(ct);
|
|
}
|
|
```
|
|
|
|
### الگوی Customer Service (JWT رو خودش resolve میکنه):
|
|
```csharp
|
|
// ✅ Customer endpoint → حتماً JWT resolve میکنه
|
|
public override async Task<Response> GetMyData(Request request, ServerCallContext context)
|
|
{
|
|
if (!long.TryParse(_currentUserService.UserId, out var userId) || userId == 0)
|
|
throw new RpcException(new Status(StatusCode.Unauthenticated, "User not authenticated"));
|
|
|
|
var query = new GetDataQuery { UserId = userId }; // ← userId صریح
|
|
var result = await _sender.Send(query, context.CancellationToken);
|
|
return MapToResponse(result);
|
|
}
|
|
```
|
|
|
|
### الگوی Admin Service (UserId رو از request میگیره):
|
|
```csharp
|
|
// ✅ Admin endpoint → UserId از request (0 = همه)
|
|
public override async Task<Response> GetAllData(Request request, ServerCallContext context)
|
|
{
|
|
// request.UserId = 0 → handler همه رو برمیگردونه
|
|
// request.UserId > 0 → handler فیلتر میکنه
|
|
var result = await _dispatcher.Send(request, context);
|
|
return result;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 لیست تغییرات
|
|
|
|
### 🔧 ۸ Query Handler اصلاحشده:
|
|
|
|
| # | Handler | تغییر | رفتار `UserId = 0` |
|
|
|---|---------|-------|---------------------|
|
|
| 1 | `GetCustomerOrdersQueryHandler` | حذف `ICurrentUserService` + JWT fallback | بدون فیلتر → همه سفارشات |
|
|
| 2 | `GetCustomerOrderQueryHandler` | حذف `ICurrentUserService` + JWT fallback | هر سفارشی با OrderId |
|
|
| 3 | `GetUserWeeklyBalancesQueryHandler` | حذف `ICurrentUserService` + JWT fallback | بدون فیلتر → همه تعادلها |
|
|
| 4 | `GetUserCommissionPayoutsQueryHandler` | حذف `ICurrentUserService` + JWT fallback | بدون فیلتر → همه پرداختها |
|
|
| 5 | `GetNetworkStatisticsQueryHandler` | حذف `ICurrentUserService` + JWT fallback | آمار root user (کل شبکه) |
|
|
| 6 | `GetNetworkTreeQueryHandler` | حذف JWT fallback + خطا اگر UserId نباشد | `ArgumentException` (الزامی) |
|
|
| 7 | `GetUserQueryHandler` | حذف JWT fallback + خطا اگر UserId نباشد | `ArgumentException` (الزامی) |
|
|
| 8 | `GetUserWalletQueryHandler` | حذف JWT fallback + خطا اگر UserId نباشد | `ArgumentException` (الزامی) |
|
|
|
|
### 🌐 ۴ Customer Service Endpoint اصلاحشده:
|
|
|
|
| # | Service / Method | تغییر |
|
|
|---|-----------------|-------|
|
|
| 1 | `UserOrderService.GetCustomerOrders` | JWT resolve → ارسال `customerUserId` به handler |
|
|
| 2 | `UserOrderService.GetCustomerOrder` | JWT resolve → ارسال `customerUserId` به handler |
|
|
| 3 | `NetworkMembershipService.GetMyNetworkStatistics` | افزودن `ICurrentUserService` + JWT resolve |
|
|
| 4 | `UserWalletService.GetCustomerWallet` | تغییر از `Id = 0` به `Id = userId` (از JWT) |
|
|
|
|
---
|
|
|
|
## 📐 دیاگرام جریان
|
|
|
|
### درخواست Admin (BackOffice):
|
|
```
|
|
BackOffice Panel → gRPC (UserId=0) → Admin Service → Handler (UserId=0 → no filter → ALL users) ✅
|
|
BackOffice Panel → gRPC (UserId=42) → Admin Service → Handler (UserId=42 → filter → one user) ✅
|
|
```
|
|
|
|
### درخواست Customer (FrontOffice):
|
|
```
|
|
FrontOffice App → gRPC → Customer Service → JWT resolve (UserId=42) → Handler (UserId=42 → filter) ✅
|
|
```
|
|
|
|
---
|
|
|
|
## ⚠️ نکات مهم
|
|
|
|
1. **Handler ها هرگز `ICurrentUserService` رو inject نمیکنند** (بعد از این فیکس)
|
|
2. فقط **Customer Service endpoints** مسئول JWT resolve هستند
|
|
3. **Admin endpoints** از `IDispatchRequestToCQRS` استفاده میکنند و UserId مستقیم از proto request میاد
|
|
4. Handler هایی که UserId **الزامی** دارند (مثل GetUser, GetUserWallet, GetNetworkTree) → `ArgumentException` پرتاب میکنند
|
|
5. Handler هایی که لیست برمیگردونند (مثل GetCustomerOrders, GetWeeklyBalances) → `UserId = 0` یعنی "بدون فیلتر"
|
|
|
|
---
|
|
|
|
## 🔗 فایلهای تغییریافته
|
|
|
|
### Application Layer:
|
|
```
|
|
CMS/src/CMSMicroservice.Application/
|
|
├── OrdersCQ/Queries/GetCustomerOrders/GetCustomerOrdersQueryHandler.cs
|
|
├── OrdersCQ/Queries/GetCustomerOrder/GetCustomerOrderQueryHandler.cs
|
|
├── UserWeeklyBalanceCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs
|
|
├── CommissionPayoutCQ/Queries/GetUserCommissionPayouts/GetUserCommissionPayoutsQueryHandler.cs
|
|
├── NetworkStatisticsCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs
|
|
├── NetworkTreeCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs
|
|
├── UserCQ/Queries/GetUser/GetUserQueryHandler.cs
|
|
└── UserWalletCQ/Queries/GetUserWallet/GetUserWalletQueryHandler.cs
|
|
```
|
|
|
|
### WebApi Layer:
|
|
```
|
|
CMS/src/CMSMicroservice.WebApi/Services/
|
|
├── UserOrderService.cs (GetCustomerOrders + GetCustomerOrder)
|
|
├── NetworkMembershipService.cs (GetMyNetworkStatistics)
|
|
└── UserWalletService.cs (GetCustomerWallet)
|
|
```
|