Files
CMS/ICURRENTUSERSERVICE-IMPLEMENTATION.md
T
masoodafar-web b2d676b555 feat: Implement customer profile and referral queries
- Add GetCustomerProfileResponseDto for retrieving customer profile information.
- Create GetCustomerReferralsQuery and GetCustomerReferralsQueryHandler to fetch customer referrals with pagination and filtering options.
- Introduce GetCustomerReferralsResponseDto to structure the response for customer referrals.
- Implement GetCustomerSettingsQuery and GetCustomerSettingsQueryHandler to retrieve user settings.
- Add GetCustomerOrder and GetCustomerOrderQueryHandler for fetching specific customer orders.
- Create GetCustomerOrderHistoryQuery and GetCustomerOrderHistoryQueryHandler to retrieve order history with filtering options.
- Implement GetCustomerOrdersQuery and GetCustomerOrdersQueryHandler for fetching multiple customer orders with filters.
- Add GetCustomerWalletChangeLogQuery and GetCustomerWalletChangeLogQueryHandler for retrieving wallet change logs.
- Implement GetCustomerWithdrawalSettingsQuery and GetCustomerWithdrawalSettingsQueryHandler for fetching withdrawal settings.
- Create GetCustomerWithdrawalsQuery and GetCustomerWithdrawalsQueryHandler to retrieve customer withdrawal requests.
2026-02-05 23:01:50 +03:30

22 KiB

پیاده‌سازی ICurrentUserService در سرویس‌های Customer

خلاصه تغییرات

این سند تمام تغییرات انجام شده برای پیاده‌سازی احراز هویت مبتنی بر JWT در endpoint‌های Customer را مستند می‌کند. هدف اصلی حذف نیاز به ارسال صریح UserId از سمت کلاینت و استخراج خودکار آن از JWT Claims است.

الگوی پیاده‌سازی

الگوی Query Handler (با ICurrentUserService)

public class SomeQueryHandler : IRequestHandler<SomeQuery, SomeResponseDto>
{
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUser;

    public SomeQueryHandler(IApplicationDbContext context, ICurrentUserService currentUser)
    {
        _context = context;
        _currentUser = currentUser;
    }

    public async Task<SomeResponseDto> Handle(SomeQuery request, CancellationToken cancellationToken)
    {
        // رزولو کردن UserId از JWT اگر در request مشخص نشده باشد
        var userId = request.UserId == 0
            ? (long.TryParse(_currentUser.UserId, out var currentUserId) ? currentUserId : 0)
            : request.UserId;

        if (userId == 0)
            throw new UnauthorizedAccessException("User ID not found");

        var query = _context.SomeEntity
            .Where(x => x.UserId == userId)
            .AsNoTracking();

        // ... ادامه پیاده‌سازی
    }
}

الگوی Service (استفاده از ISender)

public class SomeService : SomeContract.SomeContractBase
{
    private readonly ISender _sender;

    public SomeService(ISender sender)
    {
        _sender = sender;
    }

    public override async Task<Response> CustomerEndpoint(Request request, ServerCallContext context)
    {
        var query = new SomeQuery { UserId = 0 }; // 0 = استفاده از ICurrentUserService
        var result = await _sender.Send(query, context.CancellationToken);
        return MapToProtoResponse(result);
    }
}

تصمیمات معماری

1. ISender vs IDispatchRequestToCQRS

  • IDispatchRequestToCQRS: برای endpoint‌های Admin که ساختار Proto به‌طور مستقیم به CQRS نگاشت می‌شود
  • ISender: برای endpoint‌های Customer که نیاز به ساخت دستی Query و ساختار متفاوت دارند

2. قرارداد UserId = 0

  • 0 یا مقدار مشخص نشده = استفاده از ICurrentUserService برای دریافت کاربر فعلی از JWT
  • مقدار غیر صفر = کاربر صریح (برای عملیات admin/support)

3. مسئولیت Query Handler

  • Query Handler باید پس از رزولو کردن userId، وجود آن را validate کند
  • در صورت عدم موفقیت در تعیین userId، UnauthorizedAccessException پرتاب شود

سرویس‌های پیاده‌سازی شده

1. UserWallet Service (5 endpoints)

1.1 GetUserWalletQueryHandler

فایل: CMSMicroservice.Application/UserWalletCQ/Queries/GetUserWallet/GetUserWalletQueryHandler.cs

تغییرات:

  • افزودن ICurrentUserService به constructor
  • اضافه شدن فیلد DiscountBalance به DTO
  • پشتیبانی از Id = 0 برای استفاده از کاربر فعلی
var userId = request.Id == 0 
    ? (long.TryParse(_currentUser.UserId, out var currentUserId) ? currentUserId : 0)
    : request.Id;

1.2 GetCustomerWalletChangeLogQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/

پیاده‌سازی:

  • Query/Handler جدید برای دریافت تاریخچه تغییرات کیف پول
  • استفاده از entity UserWalletChangeLog
  • پشتیبانی از Pagination
  • فیلتر بر اساس userId از ICurrentUserService

1.3 GetCustomerWithdrawalsQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWithdrawals/

پیاده‌سازی:

  • Query/Handler جدید برای دریافت درخواست‌های برداشت
  • استفاده از entity UserCommissionPayout
  • فیلتر بر اساس WithdrawalRequestDate و status = PayoutRequested
  • پشتیبانی از Pagination

1.4 GetCustomerWithdrawalSettingsQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWithdrawalSettings/

پیاده‌سازی:

  • Query/Handler جدید برای دریافت تنظیمات برداشت
  • مقدار ثابت MIN_WITHDRAWAL_AMOUNT = 50000
  • برگرداندن موجودی کیف پول کاربر فعلی

1.5 UserWalletService

فایل: CMSMicroservice.WebApi/Services/UserWalletService.cs

تغییرات:

  • افزودن ISender به constructor
  • پیاده‌سازی 4 متد Customer با استفاده از Query Handler‌های واقعی:
    • GetCustomerWallet
    • GetCustomerWalletChangeLog
    • GetCustomerWithdrawals
    • GetCustomerWithdrawalSettings

2. Commission Service (2 endpoints)

2.1 GetUserCommissionPayoutsQueryHandler

فایل: CMSMicroservice.Application/CommissionCQ/Queries/GetUserCommissionPayouts/GetUserCommissionPayoutsQueryHandler.cs

تغییرات:

  • افزودن ICurrentUserService به constructor
  • پشتیبانی از UserId = null یا 0 برای استفاده از کاربر فعلی
  • کوئری از UserCommissionPayouts با Include کردن WeekDefinition

2.2 GetUserWeeklyBalancesQueryHandler

فایل: CMSMicroservice.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs

تغییرات:

  • افزودن ICurrentUserService به constructor
  • همان الگوی رزولو UserId
  • کوئری از UserWeeklyBalances با Include کردن WeekDefinition

3. NetworkMembership Service (3 endpoints)

3.1 GetNetworkTreeQueryHandler

فایل: CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs

تغییرات:

  • افزودن ICurrentUserService به constructor
  • پشتیبانی از UserId = 0 برای استفاده از کاربر فعلی
  • اجرای Stored Procedure [CMS].[GetNetworkTree]
  • تبدیل نتایج flat SP به ساختار درختی hierarchical

3.2 GetNetworkStatisticsQueryHandler

فایل: CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs

تغییرات:

  • افزودن پارامتر UserId به Query
  • افزودن ICurrentUserService به constructor
  • تغییر منطق از آمار کل سیستم به آمار شبکه زیرمجموعه کاربر
  • فیلتر: x.NetworkParentId == userId (نه x.NetworkParentId != null)

3.3 NetworkMembershipService

فایل: CMSMicroservice.WebApi/Services/NetworkMembershipService.cs

تغییرات:

  • افزودن ISender به constructor
  • پیاده‌سازی 3 متد Customer:
    • GetMyNetworkTree: درخت شبکه کاربر فعلی با UserId=0
    • GetSubordinateTree: درخت زیرمجموعه خاص (برای admin)
    • GetMyNetworkStatistics: آمار شبکه کاربر فعلی
  • متدهای helper:
    • ConvertToNodeModel(): تبدیل بازگشتی DTO به Proto Model
    • CountNodes(): شمارش بازگشتی node‌های درخت

رفع باگ:

  • حذف فیلدهای IsClubActive و ActivationWeekDefinitionId که در Proto request وجود نداشتند

4. Package Service (3 query endpoints)

4.1 GetCustomerPackagesQueryHandler (جدید)

فایل: CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/

پیاده‌سازی:

  • Query/Handler جدید برای دریافت لیست پکیج‌ها
  • کوئری از entity Package
  • نگاشت فیلدهای اضافی:
    • Name = Title
    • ImageUrl = ImagePath
    • Currency = "IRR"
    • ValidityDays = 365
  • پشتیبانی از فیلتر PackageType (در صورت وجود در entity)

4.2 GetCustomerPackageDetailsQueryHandler (جدید)

فایل: CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackageDetails/

پیاده‌سازی:

  • Query/Handler جدید برای دریافت جزئیات یک پکیج
  • کوئری بر اساس PackageId
  • افزودن Features (کمیسیون، پشتیبانی، آموزش)
  • افزودن Requirements (عضویت، موجودی کیف پول، محدودیت‌ها)

4.3 GetCustomerPurchaseHistoryQueryHandler (جدید)

فایل: CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPurchaseHistory/

پیاده‌سازی:

  • Query/Handler جدید با ICurrentUserService
  • کوئری از UserOrders با فیلتر PackageId != null
  • Include کردن navigation property Package
  • پشتیبانی از:
    • Pagination
    • فیلتر تاریخ (FromDate, ToDate)
    • فیلتر نوع پکیج
  • نگاشت PaymentStatus صحیح (Success/Reject/Pending)
  • دریافت RefId از Transaction (نه ReferenceId)

4.4 PackageService

فایل: CMSMicroservice.WebApi/Services/PackageService.cs

تغییرات:

  • افزودن ISender به constructor
  • افزودن namespace alias: using AppModels = CMSMicroservice.Application.Common.Models;
  • جایگزینی 3 متد MOCK با Query Handler واقعی:
    • GetCustomerPackages
    • GetCustomerPackageDetails
    • GetCustomerPurchaseHistory
  • رفع ابهام در type‌های PaginationState و MetaData با استفاده از alias
  • متدهای Command (Purchase, Verify) همچنان MOCK باقی ماندند

مشکلات رفع شده

1. خطای Type Inference با IDispatchRequestToCQRS

خطا: CS1061: 'Empty' does not contain definition for 'Balance'

علت: استفاده از overload نادرست Handle<TCommand, TResponse> که compiler نوع‌ها را اشتباه استنباط می‌کرد

راه حل: استفاده از ISender.Send() به‌جای IDispatchRequestToCQRS برای endpoint‌های Customer

2. عدم تطابق فیلدهای Proto

خطا: CS1061: GetSubordinateTreeRequest doesn't have ActivationWeekDefinitionId

علت: کد سرویس فیلدهایی را فرض می‌کرد که در Proto تعریف نشده بودند

راه حل: حذف فیلدهای غیرموجود از نگاشت request

3. خطای Nullable Protobuf Wrapper

خطا: CS1061: 'long' doesn't contain 'Value' property

علت: تلاش برای فراخوانی .Value روی type‌های non-nullable

راه حل: حذف فراخوانی .Value و انتساب مستقیم

4. خطای Transaction.ReferenceId

خطا: CS1061: 'Transaction' does not contain a definition for 'ReferenceId'

علت: نام صحیح فیلد RefId است نه ReferenceId

راه حل: تغییر به Transaction.RefId

5. خطای PaymentStatus Enum Values

خطا: CS0117: 'PaymentStatus' does not contain a definition for 'Failed'/'Refunded'

علت: enum فقط دارای مقادیر Success, Reject, Pending است

راه حل: تصحیح switch statement به مقادیر صحیح

6. خطای Ambiguous Reference

خطا: CS0104: 'PaginationState'/'MetaData' is ambiguous

علت: type‌ها هم در CMSMicroservice.Application.Common.Models و هم در CMSMicroservice.Protobuf.Protos وجود دارند

راه حل: افزودن namespace alias: using AppModels = CMSMicroservice.Application.Common.Models;

7. خطای MetaData Constructor

خطا: CS1729: 'MetaData' does not contain a constructor that takes 3 arguments

علت: MetaData class در Application layer بدون constructor است

راه حل: استفاده از object initializer به‌جای constructor:

var metaData = new MetaData
{
    TotalCount = totalCount,
    CurrentPage = pageNumber,
    PageSize = pageSize,
    TotalPage = (int)Math.Ceiling((double)totalCount / pageSize),
    HasPrevious = pageNumber > 1,
    HasNext = pageNumber < totalPages
};

8. خطای CategoryIds در Proto

خطا: CS1061: 'GetAllProductsByFilterFilter' does not contain 'CategoryIds'

علت: Proto فقط category_id (singular) دارد نه category_ids

راه حل: تبدیل single value به List:

CategoryIds = request.Filter?.CategoryId != null 
    ? new List<long> { request.Filter.CategoryId.Value } 
    : null

9. خطای OrderVAT و DeliveryStatus

خطا: CS1061: 'OrderVAT' does not contain 'VATPercentage'

علت:

  • فیلد صحیح VATRate است (decimal)
  • enum‌های Processing و Shipped وجود ندارند

راه حل:

  • استفاده از VATRate * 100 برای درصد
  • تصحیح enum values: Pending, InTransit, Delivered, Cancelled, Returned

10. خطای Transaction/UserWalletChangeLog بدون UserId

خطا: CS1061: 'Transaction/UserWalletChangeLog' does not contain 'UserId'

علت: این entity‌ها direct UserId ندارند

راه حل: query از طریق navigation properties:

// Transaction
.Include(x => x.UserOrders)
.Where(x => x.UserOrders.Any(o => o.UserId == userId))

// UserWalletChangeLog
.Include(x => x.Wallet)
.Where(x => x.Wallet.UserId == userId)

5. UserOrder Service (3 endpoints)

5.1 GetCustomerOrdersQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserOrderCQ/Queries/GetCustomerOrders/

پیاده‌سازی:

  • Query/Handler جدید با ICurrentUserService
  • کوئری از UserOrders با Include:
    • Package, Transaction, UserAddress, User, FactorDetails, OrderVAT
  • پشتیبانی از Pagination
  • محاسبه TotalAmount با احتساب مالیات (VATRate * 100)

رفع باگ:

  • OrderVAT.VATPercentage وجود ندارد → استفاده از VATRate * 100
  • DeliveryStatus.Processing/Shipped وجود ندارد → Pending/InTransit

5.2 GetCustomerOrderQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserOrderCQ/Queries/GetCustomerOrder/

پیاده‌سازی:

  • Query/Handler برای دریافت یک سفارش با OrderId
  • Validation: بررسی تعلق Order به UserId فعلی
  • Include همان navigation properties

5.3 GetCustomerOrderHistoryQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserOrderCQ/Queries/GetCustomerOrderHistory/

پیاده‌سازی:

  • Query/Handler با Pagination و فیلترها
  • فیلترهای پشتیبانی شده:
    • FromDate, ToDate
    • PaymentStatus, DeliveryStatus
  • محاسبه CanCancelOrder بر اساس شرایط:
    • PaymentStatus = Pending
    • DeliveryStatus = None یا Pending

5.4 UserOrderService

فایل: CMSMicroservice.WebApi/Services/UserOrderService.cs

تغییرات:

  • افزودن ISender به constructor
  • پیاده‌سازی 3 متد Customer با Query Handler واقعی
  • استفاده از namespace alias برای حل ambiguity

6. Transaction Service (2 endpoints)

6.1 GetCustomerTransactionQueryHandler (جدید)

فایل: CMSMicroservice.Application/TransactionsCQ/Queries/GetCustomerTransaction/

پیاده‌سازی:

  • Query/Handler با ICurrentUserService
  • چالش: Transaction entity بدون UserId
  • راه حل: query از طریق UserOrders navigation:
    .Include(x => x.UserOrders)
    .Where(x => x.UserOrders.Any(o => o.UserId == userId))
    
  • فیلتر بر اساس Id یا Authority

6.2 GetCustomerTransactionsByFilterQueryHandler (جدید)

فایل: CMSMicroservice.Application/TransactionsCQ/Queries/GetCustomerTransactionsByFilter/

پیاده‌سازی:

  • Query/Handler با Pagination
  • فیلترهای پشتیبانی شده:
    • Id, Amount, Description
    • PaymentStatus (bool), RefId, Type
  • همان الگوی query از طریق UserOrders

6.3 TransactionsService

فایل: CMSMicroservice.WebApi/Services/TransactionsService.cs

تغییرات:

  • افزودن ISender و Query imports
  • جایگزینی MOCK با Query Handler واقعی
  • mapping صحیح Proto enums

7. Products Service (2 endpoints)

7.1 GetCustomerProductsQueryHandler (جدید)

فایل: CMSMicroservice.Application/ProductsCQ/Queries/GetCustomerProducts/

پیاده‌سازی:

  • Query/Handler بدون ICurrentUserService (محصولات عمومی)
  • کوئری از Products با Include:
    • ProductGalleries.ProductImage
    • ProductCategories.Category
  • ساخت درختی Category Path با متد BuildCategoryPath()
  • بازگشت بازگشتی به parent categories

7.2 GetCustomerProductsByFilterQueryHandler (جدید)

فایل: CMSMicroservice.Application/ProductsCQ/Queries/GetCustomerProductsByFilter/

پیاده‌سازی:

  • Query/Handler با Pagination
  • فیلترهای کامل:
    • Id, Title, Description, ShortInfomation, FullInformation
    • Price, Discount, Rate
    • SaleCount, ViewCount, RemainingCount
    • CategoryIds (لیست شناسه دسته‌بندی‌ها)
  • Sorting پویا با ApplyOrder()

7.3 ProductsService

فایل: CMSMicroservice.WebApi/Services/ProductsService.cs

تغییرات:

  • افزودن ISender به constructor
  • پیاده‌سازی 2 متد Customer
  • mapping دستی Gallery و Categories به Proto structures
  • رفع باگ: Proto فقط category_id دارد نه category_ids
    • تبدیل single value به List

8. User Service (3 endpoints)

8.1 GetCustomerProfileQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserCQ/Queries/GetCustomerProfile/

پیاده‌سازی:

  • Query/Handler با ICurrentUserService
  • دریافت پروفایل کامل کاربر فعلی
  • محاسبه ProfileCompletionPercentage بر اساس 10 فیلد:
    • FirstName, LastName, Mobile, Email, NationalCode
    • AvatarPath, BirthDate, IsMobileVerified
    • NetworkParentId, ReferralCode
  • محاسبه FullName از FirstName + LastName

8.2 GetCustomerReferralsQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserCQ/Queries/GetCustomerReferrals/

پیاده‌سازی:

  • Query/Handler با ICurrentUserService و Pagination
  • کوئری کاربران با NetworkParentId == userId
  • فیلتر بر اساس StatusFilter (ACTIVE/INACTIVE/ALL)
  • محاسبه آمار:
    • TotalReferrals, ActiveReferrals
    • TotalCommissionEarned از UserWallet.NetworkBalance
    • ThisMonthCommission از UserWalletChangeLog
  • رفع باگ: UserWalletChangeLog بدون UserId
    • راه حل: .Include(x => x.Wallet).Where(x => x.Wallet.UserId == userId)

8.3 GetCustomerSettingsQueryHandler (جدید)

فایل: CMSMicroservice.Application/UserCQ/Queries/GetCustomerSettings/

پیاده‌سازی:

  • Query/Handler ساده برای دریافت تنظیمات کاربر
  • فیلدهای موجود در User entity:
    • EmailNotifications, SmsNotifications, PushNotifications
  • مقادیر پیش‌فرض برای فیلدهای ناموجود:
    • MarketingNotifications = false
    • PreferredLanguage = "fa"
    • TimeZone = "Asia/Tehran"
    • TwoFactorAuthEnabled = false

8.4 UserService

فایل: CMSMicroservice.WebApi/Services/UserService.cs

تغییرات:

  • افزودن ISender و Query imports
  • پیاده‌سازی 3 متد Customer با Query Handler واقعی
  • تبدیل DateTime به Timestamp با SpecifyKind(DateTimeKind.Utc)
  • رفع ambiguity: fully qualified names برای CustomerReferralStats و CustomerReferralModel

آمار پیشرفت

سرویس‌های تکمیل شده (8/8): 100%

UserWallet (5 endpoints) Commission (2 endpoints)
NetworkMembership (3 endpoints) Package (3 endpoints) UserOrder (3 endpoints) Transaction (2 endpoints) Products (2 endpoints) User (3 endpoints)

جمع کل: 25 endpoint با الگوی ICurrentUserService پیاده‌سازی شد


نکات فنی

Entity Navigation Properties

همیشه از .Include() برای load کردن navigation property‌های مورد نیاز استفاده شود:

query = query.Include(x => x.Package)
           .Include(x => x.Transaction);

Pagination

از extension method‌های GetMetaData و PaginatedListAsync استفاده شود:

var metaData = await query.GetMetaData(request.PaginationState, cancellationToken);
var items = await query.PaginatedListAsync(request.PaginationState).ToListAsync(cancellationToken);

DateTime Mapping

برای تبدیل به Protobuf Timestamp، DateTime باید UTC باشد:

Timestamp.FromDateTime(DateTime.SpecifyKind(dateTime, DateTimeKind.Utc))

Enum Casting

برای نگاشت enum‌ها بین Application و Proto:

Status = (PaymentStatusEnum)order.PaymentStatus

Build Status

آخرین Build موفق: 0 Error(s), 66 Warning(s) - Time Elapsed 00:00:03.55


تاریخ آخرین به‌روزرسانی

5 فوریه 2026


نتیجه‌گیری

پیاده‌سازی ICurrentUserService در 25 endpoint مربوط به 8 سرویس با موفقیت کامل شد.

دستاوردها:

  • 100% Coverage: تمام endpoint‌های Customer پیاده‌سازی شدند
  • الگوی Consistent: pattern مشخص برای تمام سرویس‌ها
  • امنیت بالا: استخراج خودکار UserId از JWT
  • قابلیت نگهداری: کد تمیز و قابل فهم
  • Build موفق: بدون هیچ خطا

چالش‌های حل شده:

  • Entity‌های بدون UserId (Transaction, UserWalletChangeLog)
  • Proto/Application type ambiguity
  • MetaData بدون constructor
  • Category path building
  • Proto enum mapping
  • DateTime UTC conversion

تمام تغییرات compile می‌شوند و آماده تست و deployment هستند.