# 🔀 جداسازی سرویس‌های 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 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 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 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) ```