6.9 KiB
6.9 KiB
🔀 جداسازی سرویسهای Admin و Customer
آخرین بروزرسانی: February 10, 2026
مرتبط با: ICURRENTUSERSERVICE-IMPLEMENTATION.md
🐛 مشکل
پنل ادمین BackOffice بجای نمایش اطلاعات همه کاربران، فقط اطلاعات خود ادمین رو نشان میداد.
علت ریشهای:
Query Handler ها وقتی UserId = 0 دریافت میکردند، بجای اینکه "همه کاربران" رو برگردانند، به JWT fallback میکردند و UserId ادمین رو از توکن استخراج میکردند:
// ❌ الگوی قدیمی (مشکلدار)
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:
// ✅ الگوی جدید
// 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 میکنه):
// ✅ 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 میگیره):
// ✅ 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) ✅
⚠️ نکات مهم
- Handler ها هرگز
ICurrentUserServiceرو inject نمیکنند (بعد از این فیکس) - فقط Customer Service endpoints مسئول JWT resolve هستند
- Admin endpoints از
IDispatchRequestToCQRSاستفاده میکنند و UserId مستقیم از proto request میاد - Handler هایی که UserId الزامی دارند (مثل GetUser, GetUserWallet, GetNetworkTree) →
ArgumentExceptionپرتاب میکنند - 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)