diff --git a/src/FrontOffice.BFF.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesResponseDto.cs b/src/FrontOffice.BFF.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesResponseDto.cs
index f28fe2f..f8ba6f0 100644
--- a/src/FrontOffice.BFF.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesResponseDto.cs
+++ b/src/FrontOffice.BFF.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesResponseDto.cs
@@ -79,4 +79,9 @@ public class WeeklyBalanceItemDto
/// آیا منقضی شده؟
///
public bool IsExpired { get; set; }
+
+ ///
+ /// تاریخ شمسی
+ ///
+ public string DatePersian { get; set; } = string.Empty;
}
diff --git a/src/FrontOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs b/src/FrontOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs
index 89c0c14..45fea88 100644
--- a/src/FrontOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs
+++ b/src/FrontOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs
@@ -16,6 +16,10 @@ using CMSMicroservice.Protobuf.Protos.UserWallet;
using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog;
using CMSMicroservice.Protobuf.Protos.ClubMembership;
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
+using CMSMicroservice.Protobuf.Protos.DiscountProduct;
+using CMSMicroservice.Protobuf.Protos.DiscountCategory;
+using CMSMicroservice.Protobuf.Protos.DiscountShoppingCart;
+using CMSMicroservice.Protobuf.Protos.DiscountOrder;
using PYMSMicroservice.Protobuf.Protos.Transaction;
namespace FrontOffice.BFF.Application.Common.Interfaces;
@@ -49,6 +53,12 @@ public interface IApplicationContractContext
// Network & Club System
ClubMembershipContract.ClubMembershipContractClient ClubMemberships { get; }
NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships { get; }
+
+ // Discount Shop System
+ DiscountProductContract.DiscountProductContractClient DiscountProducts { get; }
+ DiscountCategoryContract.DiscountCategoryContractClient DiscountCategories { get; }
+ DiscountShoppingCartContract.DiscountShoppingCartContractClient DiscountCart { get; }
+ DiscountOrderContract.DiscountOrderContractClient DiscountOrders { get; }
#endregion
#region PYMS
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/AddToDiscountCart/AddToDiscountCartCommand.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/AddToDiscountCart/AddToDiscountCartCommand.cs
new file mode 100644
index 0000000..bba6fc1
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/AddToDiscountCart/AddToDiscountCartCommand.cs
@@ -0,0 +1,23 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Commands.AddToDiscountCart;
+
+///
+/// افزودن محصول به سبد خرید تخفیفی
+///
+public record AddToDiscountCartCommand : IRequest
+{
+ ///
+ /// شناسه محصول
+ ///
+ public long ProductId { get; init; }
+
+ ///
+ /// تعداد
+ ///
+ public int Count { get; init; } = 1;
+}
+
+public class AddToDiscountCartResponseDto
+{
+ public bool Success { get; set; }
+ public string Message { get; set; } = string.Empty;
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/AddToDiscountCart/AddToDiscountCartCommandHandler.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/AddToDiscountCart/AddToDiscountCartCommandHandler.cs
new file mode 100644
index 0000000..65309fd
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/AddToDiscountCart/AddToDiscountCartCommandHandler.cs
@@ -0,0 +1,33 @@
+using CMSMicroservice.Protobuf.Protos.DiscountShoppingCart;
+
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Commands.AddToDiscountCart;
+
+public class AddToDiscountCartCommandHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+ private readonly ICurrentUserService _currentUserService;
+
+ public AddToDiscountCartCommandHandler(IApplicationContractContext context, ICurrentUserService currentUserService)
+ {
+ _context = context;
+ _currentUserService = currentUserService;
+ }
+
+ public async Task Handle(AddToDiscountCartCommand request, CancellationToken cancellationToken)
+ {
+ var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
+
+ var response = await _context.DiscountCart.AddToCartAsync(new AddToCartRequest
+ {
+ UserId = userId,
+ ProductId = request.ProductId,
+ Count = request.Count
+ }, cancellationToken: cancellationToken);
+
+ return new AddToDiscountCartResponseDto
+ {
+ Success = response.Success,
+ Message = response.Message
+ };
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/PlaceDiscountOrder/PlaceDiscountOrderCommand.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/PlaceDiscountOrder/PlaceDiscountOrderCommand.cs
new file mode 100644
index 0000000..a96458d
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/PlaceDiscountOrder/PlaceDiscountOrderCommand.cs
@@ -0,0 +1,39 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Commands.PlaceDiscountOrder;
+
+///
+/// ثبت سفارش از سبد خرید تخفیفی
+///
+public record PlaceDiscountOrderCommand : IRequest
+{
+ ///
+ /// شناسه آدرس کاربر
+ ///
+ public long UserAddressId { get; init; }
+
+ ///
+ /// مقدار استفاده از موجودی تخفیف (اختیاری)
+ ///
+ public long DiscountBalanceToUse { get; init; }
+
+ ///
+ /// توضیحات
+ ///
+ public string? Notes { get; init; }
+}
+
+public class PlaceDiscountOrderResponseDto
+{
+ public bool Success { get; set; }
+ public string Message { get; set; } = string.Empty;
+ public long OrderId { get; set; }
+
+ ///
+ /// مبلغ قابل پرداخت از درگاه
+ ///
+ public long GatewayAmount { get; set; }
+
+ ///
+ /// لینک پرداخت (اگر نیاز باشد)
+ ///
+ public string? PaymentUrl { get; set; }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/PlaceDiscountOrder/PlaceDiscountOrderCommandHandler.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/PlaceDiscountOrder/PlaceDiscountOrderCommandHandler.cs
new file mode 100644
index 0000000..eacdff0
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/PlaceDiscountOrder/PlaceDiscountOrderCommandHandler.cs
@@ -0,0 +1,41 @@
+using CMSMicroservice.Protobuf.Protos.DiscountOrder;
+
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Commands.PlaceDiscountOrder;
+
+public class PlaceDiscountOrderCommandHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+ private readonly ICurrentUserService _currentUserService;
+
+ public PlaceDiscountOrderCommandHandler(IApplicationContractContext context, ICurrentUserService currentUserService)
+ {
+ _context = context;
+ _currentUserService = currentUserService;
+ }
+
+ public async Task Handle(PlaceDiscountOrderCommand request, CancellationToken cancellationToken)
+ {
+ var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
+
+ var cmsRequest = new PlaceOrderRequest
+ {
+ UserId = userId,
+ UserAddressId = request.UserAddressId,
+ DiscountBalanceToUse = request.DiscountBalanceToUse
+ };
+
+ if (!string.IsNullOrEmpty(request.Notes))
+ cmsRequest.Notes = request.Notes;
+
+ var response = await _context.DiscountOrders.PlaceOrderAsync(cmsRequest, cancellationToken: cancellationToken);
+
+ return new PlaceDiscountOrderResponseDto
+ {
+ Success = response.Success,
+ Message = response.Message,
+ OrderId = response.OrderId,
+ GatewayAmount = response.GatewayAmount,
+ PaymentUrl = response.PaymentUrl
+ };
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/RemoveFromDiscountCart/RemoveFromDiscountCartCommand.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/RemoveFromDiscountCart/RemoveFromDiscountCartCommand.cs
new file mode 100644
index 0000000..e7db756
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/RemoveFromDiscountCart/RemoveFromDiscountCartCommand.cs
@@ -0,0 +1,18 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Commands.RemoveFromDiscountCart;
+
+///
+/// حذف محصول از سبد خرید تخفیفی
+///
+public record RemoveFromDiscountCartCommand : IRequest
+{
+ ///
+ /// شناسه محصول
+ ///
+ public long ProductId { get; init; }
+}
+
+public class RemoveFromDiscountCartResponseDto
+{
+ public bool Success { get; set; }
+ public string Message { get; set; } = string.Empty;
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/RemoveFromDiscountCart/RemoveFromDiscountCartCommandHandler.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/RemoveFromDiscountCart/RemoveFromDiscountCartCommandHandler.cs
new file mode 100644
index 0000000..88cf3c5
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Commands/RemoveFromDiscountCart/RemoveFromDiscountCartCommandHandler.cs
@@ -0,0 +1,32 @@
+using CMSMicroservice.Protobuf.Protos.DiscountShoppingCart;
+
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Commands.RemoveFromDiscountCart;
+
+public class RemoveFromDiscountCartCommandHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+ private readonly ICurrentUserService _currentUserService;
+
+ public RemoveFromDiscountCartCommandHandler(IApplicationContractContext context, ICurrentUserService currentUserService)
+ {
+ _context = context;
+ _currentUserService = currentUserService;
+ }
+
+ public async Task Handle(RemoveFromDiscountCartCommand request, CancellationToken cancellationToken)
+ {
+ var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
+
+ var response = await _context.DiscountCart.RemoveFromCartAsync(new RemoveFromCartRequest
+ {
+ UserId = userId,
+ ProductId = request.ProductId
+ }, cancellationToken: cancellationToken);
+
+ return new RemoveFromDiscountCartResponseDto
+ {
+ Success = response.Success,
+ Message = response.Message
+ };
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesQuery.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesQuery.cs
new file mode 100644
index 0000000..b0945c0
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesQuery.cs
@@ -0,0 +1,17 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountCategories;
+
+///
+/// دریافت لیست دستهبندیهای فروشگاه تخفیفی
+///
+public record GetDiscountCategoriesQuery : IRequest
+{
+ ///
+ /// شناسه دستهبندی والد (اختیاری)
+ ///
+ public long? ParentCategoryId { get; init; }
+
+ ///
+ /// فقط فعالها
+ ///
+ public bool OnlyActive { get; init; } = true;
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesQueryHandler.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesQueryHandler.cs
new file mode 100644
index 0000000..802b927
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesQueryHandler.cs
@@ -0,0 +1,51 @@
+using CMSMicroservice.Protobuf.Protos.DiscountCategory;
+using CMSProto = CMSMicroservice.Protobuf.Protos.DiscountCategory;
+
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountCategories;
+
+public class GetDiscountCategoriesQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetDiscountCategoriesQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetDiscountCategoriesQuery request, CancellationToken cancellationToken)
+ {
+ var cmsRequest = new GetDiscountCategoriesRequest();
+
+ if (request.ParentCategoryId.HasValue)
+ cmsRequest.ParentCategoryId = request.ParentCategoryId.Value;
+
+ if (request.OnlyActive)
+ cmsRequest.IsActive = true;
+
+ var response = await _context.DiscountCategories.GetDiscountCategoriesAsync(cmsRequest, cancellationToken: cancellationToken);
+
+ // Proto already returns tree structure with children
+ var categories = response.Categories.Select(MapToDto).ToList();
+
+ return new GetDiscountCategoriesResponseDto
+ {
+ Categories = categories
+ };
+ }
+
+ private static DiscountCategoryDto MapToDto(CMSProto.DiscountCategoryDto model)
+ {
+ return new DiscountCategoryDto
+ {
+ Id = model.Id,
+ Name = model.Name,
+ Title = model.Title,
+ Description = model.Description,
+ ImagePath = model.ImagePath,
+ ParentCategoryId = model.ParentCategoryId > 0 ? model.ParentCategoryId : null,
+ SortOrder = model.SortOrder,
+ IsActive = model.IsActive,
+ Children = model.Children.Select(MapToDto).ToList()
+ };
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesResponseDto.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesResponseDto.cs
new file mode 100644
index 0000000..72e9c1c
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountCategories/GetDiscountCategoriesResponseDto.cs
@@ -0,0 +1,54 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountCategories;
+
+public class GetDiscountCategoriesResponseDto
+{
+ public List Categories { get; set; } = new();
+}
+
+public class DiscountCategoryDto
+{
+ ///
+ /// شناسه
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// نام
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// عنوان
+ ///
+ public string Title { get; set; } = string.Empty;
+
+ ///
+ /// توضیحات
+ ///
+ public string? Description { get; set; }
+
+ ///
+ /// مسیر تصویر
+ ///
+ public string? ImagePath { get; set; }
+
+ ///
+ /// شناسه والد
+ ///
+ public long? ParentCategoryId { get; set; }
+
+ ///
+ /// ترتیب نمایش
+ ///
+ public int SortOrder { get; set; }
+
+ ///
+ /// فعال بودن
+ ///
+ public bool IsActive { get; set; }
+
+ ///
+ /// زیرمجموعهها
+ ///
+ public List Children { get; set; } = new();
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsQuery.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsQuery.cs
new file mode 100644
index 0000000..2ebf22a
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsQuery.cs
@@ -0,0 +1,27 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountProducts;
+
+///
+/// دریافت لیست محصولات فروشگاه تخفیفی
+///
+public record GetDiscountProductsQuery : IRequest
+{
+ ///
+ /// شناسه دستهبندی (اختیاری)
+ ///
+ public long? CategoryId { get; init; }
+
+ ///
+ /// شماره صفحه
+ ///
+ public int PageNumber { get; init; } = 1;
+
+ ///
+ /// تعداد در هر صفحه
+ ///
+ public int PageSize { get; init; } = 20;
+
+ ///
+ /// عبارت جستجو (اختیاری)
+ ///
+ public string? SearchTerm { get; init; }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsQueryHandler.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsQueryHandler.cs
new file mode 100644
index 0000000..759cc8c
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsQueryHandler.cs
@@ -0,0 +1,50 @@
+using CMSMicroservice.Protobuf.Protos.DiscountProduct;
+
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountProducts;
+
+public class GetDiscountProductsQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetDiscountProductsQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetDiscountProductsQuery request, CancellationToken cancellationToken)
+ {
+ var cmsRequest = new GetDiscountProductsRequest
+ {
+ PageNumber = request.PageNumber,
+ PageSize = request.PageSize
+ };
+
+ if (request.CategoryId.HasValue)
+ cmsRequest.CategoryId = request.CategoryId.Value;
+
+ if (!string.IsNullOrEmpty(request.SearchTerm))
+ cmsRequest.SearchQuery = request.SearchTerm;
+
+ var response = await _context.DiscountProducts.GetDiscountProductsAsync(cmsRequest, cancellationToken: cancellationToken);
+
+ return new GetDiscountProductsResponseDto
+ {
+ MetaData = new MetaData
+ {
+ TotalCount = response.MetaData?.TotalCount ?? 0
+ },
+ Products = response.Models.Select(p => new DiscountProductDto
+ {
+ Id = p.Id,
+ Title = p.Title,
+ ShortInformation = p.ShortInfomation,
+ Price = p.Price,
+ MaxDiscountPercent = p.MaxDiscountPercent,
+ ImagePath = p.ImagePath,
+ ThumbnailPath = p.ThumbnailPath,
+ AvailableCount = p.RemainingCount,
+ IsActive = p.IsActive
+ }).ToList()
+ };
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsResponseDto.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsResponseDto.cs
new file mode 100644
index 0000000..cf8c09a
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetDiscountProducts/GetDiscountProductsResponseDto.cs
@@ -0,0 +1,65 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountProducts;
+
+public class GetDiscountProductsResponseDto
+{
+ public MetaData MetaData { get; set; } = new();
+ public List Products { get; set; } = new();
+}
+
+public class DiscountProductDto
+{
+ ///
+ /// شناسه محصول
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// عنوان
+ ///
+ public string Title { get; set; } = string.Empty;
+
+ ///
+ /// توضیحات کوتاه
+ ///
+ public string ShortInformation { get; set; } = string.Empty;
+
+ ///
+ /// قیمت اصلی
+ ///
+ public long Price { get; set; }
+
+ ///
+ /// حداکثر درصد تخفیف
+ ///
+ public int MaxDiscountPercent { get; set; }
+
+ ///
+ /// قیمت با تخفیف
+ ///
+ public long DiscountedPrice => Price - (Price * MaxDiscountPercent / 100);
+
+ ///
+ /// مسیر تصویر
+ ///
+ public string ImagePath { get; set; } = string.Empty;
+
+ ///
+ /// مسیر تصویر کوچک
+ ///
+ public string ThumbnailPath { get; set; } = string.Empty;
+
+ ///
+ /// موجودی
+ ///
+ public int AvailableCount { get; set; }
+
+ ///
+ /// فعال بودن
+ ///
+ public bool IsActive { get; set; }
+
+ ///
+ /// دستهبندیها
+ ///
+ public List CategoryIds { get; set; } = new();
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartQuery.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartQuery.cs
new file mode 100644
index 0000000..fa7eb1a
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartQuery.cs
@@ -0,0 +1,6 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountCart;
+
+///
+/// دریافت سبد خرید فروشگاه تخفیفی کاربر جاری
+///
+public record GetMyDiscountCartQuery : IRequest;
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartQueryHandler.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartQueryHandler.cs
new file mode 100644
index 0000000..7aa1f81
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartQueryHandler.cs
@@ -0,0 +1,42 @@
+using CMSMicroservice.Protobuf.Protos.DiscountShoppingCart;
+
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountCart;
+
+public class GetMyDiscountCartQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+ private readonly ICurrentUserService _currentUserService;
+
+ public GetMyDiscountCartQueryHandler(IApplicationContractContext context, ICurrentUserService currentUserService)
+ {
+ _context = context;
+ _currentUserService = currentUserService;
+ }
+
+ public async Task Handle(GetMyDiscountCartQuery request, CancellationToken cancellationToken)
+ {
+ var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
+
+ var response = await _context.DiscountCart.GetUserCartAsync(new GetUserCartRequest
+ {
+ UserId = userId
+ }, cancellationToken: cancellationToken);
+
+ return new GetMyDiscountCartResponseDto
+ {
+ Items = response.Items.Select(i => new DiscountCartItemDto
+ {
+ Id = i.ProductId, // Use ProductId as unique identifier
+ ProductId = i.ProductId,
+ ProductTitle = i.ProductTitle,
+ UnitPrice = i.UnitPrice,
+ Count = i.Count,
+ MaxDiscountPercent = i.MaxDiscountPercent,
+ DiscountAmount = i.DiscountAmount,
+ ThumbnailPath = i.ProductImagePath
+ }).ToList(),
+ TotalPrice = response.TotalPrice,
+ TotalDiscountAmount = response.TotalDiscountAmount
+ };
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartResponseDto.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartResponseDto.cs
new file mode 100644
index 0000000..67c94cd
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountCart/GetMyDiscountCartResponseDto.cs
@@ -0,0 +1,77 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountCart;
+
+public class GetMyDiscountCartResponseDto
+{
+ ///
+ /// آیتمهای سبد خرید
+ ///
+ public List Items { get; set; } = new();
+
+ ///
+ /// جمع کل قیمت (بدون تخفیف)
+ ///
+ public long TotalPrice { get; set; }
+
+ ///
+ /// جمع کل تخفیف
+ ///
+ public long TotalDiscountAmount { get; set; }
+
+ ///
+ /// مبلغ قابل پرداخت
+ ///
+ public long PayableAmount => TotalPrice - TotalDiscountAmount;
+
+ ///
+ /// تعداد کل آیتمها
+ ///
+ public int TotalItemCount => Items.Sum(i => i.Count);
+}
+
+public class DiscountCartItemDto
+{
+ ///
+ /// شناسه آیتم سبد
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// شناسه محصول
+ ///
+ public long ProductId { get; set; }
+
+ ///
+ /// عنوان محصول
+ ///
+ public string ProductTitle { get; set; } = string.Empty;
+
+ ///
+ /// قیمت واحد
+ ///
+ public long UnitPrice { get; set; }
+
+ ///
+ /// تعداد
+ ///
+ public int Count { get; set; }
+
+ ///
+ /// درصد تخفیف
+ ///
+ public int MaxDiscountPercent { get; set; }
+
+ ///
+ /// مبلغ تخفیف
+ ///
+ public long DiscountAmount { get; set; }
+
+ ///
+ /// قیمت کل ردیف
+ ///
+ public long TotalPrice => UnitPrice * Count;
+
+ ///
+ /// مسیر تصویر
+ ///
+ public string ThumbnailPath { get; set; } = string.Empty;
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersQuery.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersQuery.cs
new file mode 100644
index 0000000..0b11b2d
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersQuery.cs
@@ -0,0 +1,10 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountOrders;
+
+///
+/// دریافت سفارشات فروشگاه تخفیفی کاربر
+///
+public record GetMyDiscountOrdersQuery : IRequest
+{
+ public int PageNumber { get; init; } = 1;
+ public int PageSize { get; init; } = 20;
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersQueryHandler.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersQueryHandler.cs
new file mode 100644
index 0000000..f0c589b
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersQueryHandler.cs
@@ -0,0 +1,80 @@
+using CMSMicroservice.Protobuf.Protos.DiscountOrder;
+
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountOrders;
+
+public class GetMyDiscountOrdersQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+ private readonly ICurrentUserService _currentUserService;
+
+ public GetMyDiscountOrdersQueryHandler(IApplicationContractContext context, ICurrentUserService currentUserService)
+ {
+ _context = context;
+ _currentUserService = currentUserService;
+ }
+
+ public async Task Handle(GetMyDiscountOrdersQuery request, CancellationToken cancellationToken)
+ {
+ var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
+
+ var response = await _context.DiscountOrders.GetUserOrdersAsync(new GetUserOrdersRequest
+ {
+ UserId = userId,
+ PageNumber = request.PageNumber,
+ PageSize = request.PageSize
+ }, cancellationToken: cancellationToken);
+
+ return new GetMyDiscountOrdersResponseDto
+ {
+ MetaData = new MetaData
+ {
+ TotalCount = response.MetaData?.TotalCount ?? 0
+ },
+ Orders = response.Models.Select(o => new DiscountOrderDto
+ {
+ Id = o.Id,
+ OrderNumber = o.OrderNumber,
+ Status = MapStatus((int)o.DeliveryStatus),
+ StatusColor = GetStatusColor((int)o.DeliveryStatus),
+ TotalAmount = o.TotalPrice,
+ DiscountUsed = o.DiscountBalanceUsed,
+ GatewayPaid = o.GatewayAmount,
+ CreatedAt = o.Created?.ToDateTime() ?? DateTime.UtcNow,
+ CreatedAtPersian = FormatPersianDate(o.Created?.ToDateTime())
+ }).ToList()
+ };
+ }
+
+ private static string MapStatus(int status)
+ {
+ return status switch
+ {
+ 0 => "در انتظار پرداخت",
+ 1 => "در حال پردازش",
+ 2 => "ارسال شده",
+ 3 => "تحویل داده شده",
+ 4 => "لغو شده",
+ _ => "نامشخص"
+ };
+ }
+
+ private static string GetStatusColor(int status)
+ {
+ return status switch
+ {
+ 0 => "warning",
+ 1 => "primary",
+ 2 => "info",
+ 3 => "success",
+ 4 => "danger",
+ _ => "secondary"
+ };
+ }
+
+ private static string FormatPersianDate(DateTime? date)
+ {
+ if (!date.HasValue) return string.Empty;
+ // TODO: استفاده از PersianCalendar
+ return date.Value.ToString("yyyy/MM/dd HH:mm");
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersResponseDto.cs b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersResponseDto.cs
new file mode 100644
index 0000000..56c3ee1
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/DiscountShopCQ/Queries/GetMyDiscountOrders/GetMyDiscountOrdersResponseDto.cs
@@ -0,0 +1,55 @@
+namespace FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountOrders;
+
+public class GetMyDiscountOrdersResponseDto
+{
+ public MetaData MetaData { get; set; } = new();
+ public List Orders { get; set; } = new();
+}
+
+public class DiscountOrderDto
+{
+ ///
+ /// شناسه سفارش
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// شماره سفارش
+ ///
+ public string OrderNumber { get; set; } = string.Empty;
+
+ ///
+ /// وضعیت
+ ///
+ public string Status { get; set; } = string.Empty;
+
+ ///
+ /// رنگ وضعیت
+ ///
+ public string StatusColor { get; set; } = string.Empty;
+
+ ///
+ /// جمع کل
+ ///
+ public long TotalAmount { get; set; }
+
+ ///
+ /// تخفیف استفاده شده
+ ///
+ public long DiscountUsed { get; set; }
+
+ ///
+ /// مبلغ پرداخت شده از درگاه
+ ///
+ public long GatewayPaid { get; set; }
+
+ ///
+ /// تاریخ ثبت
+ ///
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// تاریخ شمسی
+ ///
+ public string CreatedAtPersian { get; set; } = string.Empty;
+}
diff --git a/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionQuery.cs b/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionQuery.cs
new file mode 100644
index 0000000..c1b55ca
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionQuery.cs
@@ -0,0 +1,3 @@
+namespace FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkPosition;
+
+public record GetMyNetworkPositionQuery : IRequest;
diff --git a/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionQueryHandler.cs b/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionQueryHandler.cs
new file mode 100644
index 0000000..8c1e6c0
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionQueryHandler.cs
@@ -0,0 +1,44 @@
+using CMSMicroservice.Protobuf.Protos.NetworkMembership;
+
+namespace FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkPosition;
+
+public class GetMyNetworkPositionQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+ private readonly ICurrentUserService _currentUserService;
+
+ public GetMyNetworkPositionQueryHandler(
+ IApplicationContractContext context,
+ ICurrentUserService currentUserService)
+ {
+ _context = context;
+ _currentUserService = currentUserService;
+ }
+
+ public async Task Handle(GetMyNetworkPositionQuery request, CancellationToken cancellationToken)
+ {
+ var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
+
+ var response = await _context.NetworkMemberships.GetUserNetworkAsync(
+ new GetUserNetworkRequest { UserId = userId },
+ cancellationToken: cancellationToken);
+
+ return new GetMyNetworkPositionResponseDto
+ {
+ UserId = response.UserId,
+ ParentId = response.ParentId ?? 0,
+ ParentName = response.ParentName ?? string.Empty,
+ Position = response.NetworkLeg switch
+ {
+ 0 => "Left",
+ 1 => "Right",
+ _ => "Root"
+ },
+ Level = response.NetworkLevel,
+ ReferralCode = response.ReferralCode ?? string.Empty,
+ JoinedAt = response.JoinedAt?.ToDateTime() ?? DateTime.UtcNow,
+ HasLeftChild = response.LeftChildId.HasValue,
+ HasRightChild = response.RightChildId.HasValue
+ };
+ }
+}
diff --git a/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionResponseDto.cs b/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionResponseDto.cs
new file mode 100644
index 0000000..5482c73
--- /dev/null
+++ b/src/FrontOffice.BFF.Application/NetworkMembershipCQ/Queries/GetMyNetworkPosition/GetMyNetworkPositionResponseDto.cs
@@ -0,0 +1,49 @@
+namespace FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkPosition;
+
+public class GetMyNetworkPositionResponseDto
+{
+ ///
+ /// شناسه کاربر
+ ///
+ public long UserId { get; set; }
+
+ ///
+ /// شناسه والد
+ ///
+ public long ParentId { get; set; }
+
+ ///
+ /// نام والد
+ ///
+ public string ParentName { get; set; } = string.Empty;
+
+ ///
+ /// موقعیت در درخت (Left/Right)
+ ///
+ public string Position { get; set; } = string.Empty;
+
+ ///
+ /// سطح در درخت
+ ///
+ public int Level { get; set; }
+
+ ///
+ /// کد معرف
+ ///
+ public string ReferralCode { get; set; } = string.Empty;
+
+ ///
+ /// تاریخ عضویت
+ ///
+ public DateTime JoinedAt { get; set; }
+
+ ///
+ /// آیا فرزند چپ دارد؟
+ ///
+ public bool HasLeftChild { get; set; }
+
+ ///
+ /// آیا فرزند راست دارد؟
+ ///
+ public bool HasRightChild { get; set; }
+}
diff --git a/src/FrontOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs b/src/FrontOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs
index e56686f..3bd2566 100644
--- a/src/FrontOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs
+++ b/src/FrontOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs
@@ -16,6 +16,10 @@ using CMSMicroservice.Protobuf.Protos.UserWallet;
using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog;
using CMSMicroservice.Protobuf.Protos.ClubMembership;
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
+using CMSMicroservice.Protobuf.Protos.DiscountProduct;
+using CMSMicroservice.Protobuf.Protos.DiscountCategory;
+using CMSMicroservice.Protobuf.Protos.DiscountShoppingCart;
+using CMSMicroservice.Protobuf.Protos.DiscountOrder;
using FrontOffice.BFF.Application.Common.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using PYMSMicroservice.Protobuf.Protos.Transaction;
@@ -76,6 +80,12 @@ public class ApplicationContractContext : IApplicationContractContext
// Network & Club System
public ClubMembershipContract.ClubMembershipContractClient ClubMemberships => GetService();
public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships => GetService();
+
+ // Discount Shop System
+ public DiscountProductContract.DiscountProductContractClient DiscountProducts => GetService();
+ public DiscountCategoryContract.DiscountCategoryContractClient DiscountCategories => GetService();
+ public DiscountShoppingCartContract.DiscountShoppingCartContractClient DiscountCart => GetService();
+ public DiscountOrderContract.DiscountOrderContractClient DiscountOrders => GetService();
#endregion
#region PYMS
diff --git a/src/FrontOffice.BFF.WebApi/Common/Mappings/ClubMembershipProfile.cs b/src/FrontOffice.BFF.WebApi/Common/Mappings/ClubMembershipProfile.cs
new file mode 100644
index 0000000..f8f24dd
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Common/Mappings/ClubMembershipProfile.cs
@@ -0,0 +1,42 @@
+using FrontOffice.BFF.Application.ClubMembershipCQ.Queries.GetMyClubMembership;
+using FrontOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateMyClubMembership;
+using Google.Protobuf.WellKnownTypes;
+using ProtoDto = FrontOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership;
+
+namespace FrontOffice.BFF.WebApi.Common.Mappings;
+
+public class ClubMembershipProfile : IRegister
+{
+ void IRegister.Register(TypeAdapterConfig config)
+ {
+ // Request -> Command mappings
+ config.NewConfig()
+ .Map(dest => dest.PackageId, src => src.PackageId)
+ .Map(dest => dest.ActivationCode, src => src.ActivationCode)
+ .Map(dest => dest.DurationMonths, src => src.DurationMonths > 0 ? src.DurationMonths : 12);
+
+ // Response mappings
+ config.NewConfig()
+ .Map(dest => dest.UserId, src => src.UserId)
+ .Map(dest => dest.PackageId, src => src.PackageId)
+ .Map(dest => dest.PackageName, src => src.PackageName)
+ .Map(dest => dest.ActivationCode, src => src.ActivationCode)
+ .Map(dest => dest.IsActive, src => src.IsActive)
+ .Map(dest => dest.ActivationDate, src => src.ActivationDate.HasValue
+ ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.ActivationDate.Value, DateTimeKind.Utc))
+ : null)
+ .Map(dest => dest.ExpirationDate, src => src.ExpirationDate.HasValue
+ ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.ExpirationDate.Value, DateTimeKind.Utc))
+ : null)
+ .Map(dest => dest.Status, src => src.Status)
+ .Map(dest => dest.DaysRemaining, src => src.DaysRemaining)
+ .Map(dest => dest.IsTrialPeriod, src => src.IsTrialPeriod);
+
+ config.NewConfig()
+ .Map(dest => dest.Success, src => src.Success)
+ .Map(dest => dest.Message, src => src.Message)
+ .Map(dest => dest.ActivationDate, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.ActivationDate, DateTimeKind.Utc)))
+ .Map(dest => dest.ExpirationDate, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.ExpirationDate, DateTimeKind.Utc)))
+ .Map(dest => dest.AmountPaid, src => src.AmountPaid);
+ }
+}
diff --git a/src/FrontOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs b/src/FrontOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs
new file mode 100644
index 0000000..46cd8c1
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs
@@ -0,0 +1,59 @@
+using FrontOffice.BFF.Application.CommissionCQ.Queries.GetMyCommissionPayouts;
+using FrontOffice.BFF.Application.CommissionCQ.Queries.GetMyWeeklyBalances;
+using Google.Protobuf.WellKnownTypes;
+using ProtoDto = FrontOffice.BFF.Commission.Protobuf.Protos.Commission;
+
+namespace FrontOffice.BFF.WebApi.Common.Mappings;
+
+public class CommissionProfile : IRegister
+{
+ void IRegister.Register(TypeAdapterConfig config)
+ {
+ // Request -> Query mappings
+ config.NewConfig()
+ .Map(dest => dest.PageNumber, src => src.PageNumber)
+ .Map(dest => dest.PageSize, src => src.PageSize)
+ .Map(dest => dest.WeekNumber, src => src.WeekNumber)
+ .Map(dest => dest.Status, src => src.Status);
+
+ config.NewConfig()
+ .Map(dest => dest.PageNumber, src => src.PageNumber)
+ .Map(dest => dest.PageSize, src => src.PageSize)
+ .Map(dest => dest.WeekNumber, src => src.WeekNumber)
+ .Map(dest => dest.OnlyActive, src => src.OnlyActive);
+
+ // Response mappings
+ config.NewConfig()
+ .Map(dest => dest.MetaData, src => new ProtoDto.MetaData { TotalCount = src.TotalCount })
+ .Map(dest => dest.Payouts, src => src.Payouts);
+
+ config.NewConfig()
+ .Map(dest => dest.Id, src => src.Id)
+ .Map(dest => dest.WeekNumber, src => src.WeekNumber)
+ .Map(dest => dest.WeekLabel, src => src.WeekLabel)
+ .Map(dest => dest.BalancesEarned, src => src.BalancesEarned)
+ .Map(dest => dest.TotalAmount, src => src.TotalAmount)
+ .Map(dest => dest.AmountFormatted, src => src.AmountFormatted)
+ .Map(dest => dest.Status, src => src.Status)
+ .Map(dest => dest.StatusBadgeColor, src => src.StatusBadgeColor)
+ .Map(dest => dest.CalculatedDate, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.CalculatedDate, DateTimeKind.Utc)))
+ .Map(dest => dest.DatePersian, src => src.DatePersian);
+
+ config.NewConfig()
+ .Map(dest => dest.MetaData, src => new ProtoDto.MetaData { TotalCount = src.TotalCount })
+ .Map(dest => dest.Balances, src => src.Balances)
+ .Map(dest => dest.TotalLeftBalances, src => src.TotalLeftBalances)
+ .Map(dest => dest.TotalRightBalances, src => src.TotalRightBalances)
+ .Map(dest => dest.WeakerLeg, src => src.WeakerLeg);
+
+ config.NewConfig()
+ .Map(dest => dest.Id, src => src.Id)
+ .Map(dest => dest.WeekNumber, src => src.WeekNumber)
+ .Map(dest => dest.LeftLegBalances, src => src.LeftLegBalances)
+ .Map(dest => dest.RightLegBalances, src => src.RightLegBalances)
+ .Map(dest => dest.TotalBalances, src => src.TotalBalances)
+ .Map(dest => dest.WeeklyPoolContribution, src => src.WeeklyPoolContribution)
+ .Map(dest => dest.IsExpired, src => src.IsExpired)
+ .Map(dest => dest.DatePersian, src => src.DatePersian);
+ }
+}
diff --git a/src/FrontOffice.BFF.WebApi/Common/Mappings/DiscountShopProfile.cs b/src/FrontOffice.BFF.WebApi/Common/Mappings/DiscountShopProfile.cs
new file mode 100644
index 0000000..f94ff14
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Common/Mappings/DiscountShopProfile.cs
@@ -0,0 +1,104 @@
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountProducts;
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountCategories;
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountCart;
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountOrders;
+using FrontOffice.BFF.Application.DiscountShopCQ.Commands.AddToDiscountCart;
+using FrontOffice.BFF.Application.DiscountShopCQ.Commands.RemoveFromDiscountCart;
+using FrontOffice.BFF.Application.DiscountShopCQ.Commands.PlaceDiscountOrder;
+using ProtoDto = FrontOffice.BFF.DiscountShop.Protobuf.Protos.DiscountShop;
+
+namespace FrontOffice.BFF.WebApi.Common.Mappings;
+
+public class DiscountShopProfile : IRegister
+{
+ void IRegister.Register(TypeAdapterConfig config)
+ {
+ // Request -> Query/Command mappings
+ config.NewConfig()
+ .Map(dest => dest.PageNumber, src => src.PageNumber)
+ .Map(dest => dest.PageSize, src => src.PageSize)
+ .Map(dest => dest.SearchTerm, src => src.SearchTerm)
+ .Map(dest => dest.CategoryId, src => src.CategoryId);
+
+ config.NewConfig()
+ .Map(dest => dest.ParentCategoryId, src => src.ParentCategoryId)
+ .Map(dest => dest.OnlyActive, src => src.OnlyActive);
+
+ config.NewConfig()
+ .Map(dest => dest.PageNumber, src => src.PageNumber)
+ .Map(dest => dest.PageSize, src => src.PageSize);
+
+ config.NewConfig()
+ .Map(dest => dest.ProductId, src => src.ProductId)
+ .Map(dest => dest.Count, src => src.Count);
+
+ config.NewConfig()
+ .Map(dest => dest.ProductId, src => src.ProductId);
+
+ config.NewConfig()
+ .Map(dest => dest.UserAddressId, src => src.UserAddressId)
+ .Map(dest => dest.DiscountBalanceToUse, src => src.DiscountBalanceToUse)
+ .Map(dest => dest.Notes, src => src.Notes);
+
+ // Response mappings
+ config.NewConfig()
+ .Map(dest => dest.MetaData, src => new ProtoDto.MetaData { TotalCount = src.MetaData.TotalCount })
+ .Map(dest => dest.Products, src => src.Products);
+
+ config.NewConfig()
+ .Map(dest => dest.Id, src => src.Id)
+ .Map(dest => dest.Title, src => src.Title)
+ .Map(dest => dest.Description, src => src.ShortInformation)
+ .Map(dest => dest.ImagePath, src => src.ImagePath)
+ .Map(dest => dest.OriginalPrice, src => src.Price)
+ .Map(dest => dest.DiscountedPrice, src => src.DiscountedPrice)
+ .Map(dest => dest.DiscountPercent, src => src.MaxDiscountPercent)
+ .Map(dest => dest.RemainingCount, src => src.AvailableCount)
+ .Map(dest => dest.IsAvailable, src => src.IsActive);
+
+ config.NewConfig()
+ .Map(dest => dest.Categories, src => src.Categories);
+
+ config.NewConfig()
+ .Map(dest => dest.Id, src => src.Id)
+ .Map(dest => dest.Title, src => src.Title)
+ .Map(dest => dest.ParentId, src => src.ParentCategoryId)
+ .Map(dest => dest.IconPath, src => src.ImagePath)
+ .Map(dest => dest.SortOrder, src => src.SortOrder)
+ .Map(dest => dest.Children, src => src.Children);
+
+ config.NewConfig()
+ .Map(dest => dest.Items, src => src.Items)
+ .Map(dest => dest.TotalPrice, src => src.TotalPrice)
+ .Map(dest => dest.TotalDiscountAmount, src => src.TotalDiscountAmount);
+
+ config.NewConfig()
+ .Map(dest => dest.ProductId, src => src.ProductId)
+ .Map(dest => dest.ProductTitle, src => src.ProductTitle)
+ .Map(dest => dest.ProductImagePath, src => src.ThumbnailPath)
+ .Map(dest => dest.UnitPrice, src => src.UnitPrice)
+ .Map(dest => dest.Count, src => src.Count)
+ .Map(dest => dest.TotalPrice, src => src.TotalPrice);
+
+ config.NewConfig()
+ .Map(dest => dest.MetaData, src => new ProtoDto.MetaData { TotalCount = src.MetaData.TotalCount })
+ .Map(dest => dest.Orders, src => src.Orders);
+
+ config.NewConfig()
+ .Map(dest => dest.Id, src => src.Id)
+ .Map(dest => dest.OrderNumber, src => src.OrderNumber)
+ .Map(dest => dest.Status, src => src.Status)
+ .Map(dest => dest.StatusColor, src => src.StatusColor)
+ .Map(dest => dest.TotalAmount, src => src.TotalAmount)
+ .Map(dest => dest.DiscountUsed, src => src.DiscountUsed)
+ .Map(dest => dest.GatewayPaid, src => src.GatewayPaid)
+ .Map(dest => dest.CreatedAtPersian, src => src.CreatedAtPersian);
+
+ config.NewConfig()
+ .Map(dest => dest.Success, src => src.Success)
+ .Map(dest => dest.Message, src => src.Message)
+ .Map(dest => dest.OrderId, src => src.OrderId)
+ .Map(dest => dest.OrderNumber, src => src.OrderId.ToString())
+ .Map(dest => dest.PaymentUrl, src => src.PaymentUrl);
+ }
+}
diff --git a/src/FrontOffice.BFF.WebApi/Common/Mappings/NetworkMembershipProfile.cs b/src/FrontOffice.BFF.WebApi/Common/Mappings/NetworkMembershipProfile.cs
new file mode 100644
index 0000000..eb98596
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Common/Mappings/NetworkMembershipProfile.cs
@@ -0,0 +1,68 @@
+using FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkTree;
+using FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkStatistics;
+using FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkPosition;
+using Google.Protobuf.WellKnownTypes;
+using ProtoDto = FrontOffice.BFF.NetworkMembership.Protobuf.Protos.NetworkMembership;
+
+namespace FrontOffice.BFF.WebApi.Common.Mappings;
+
+public class NetworkMembershipProfile : IRegister
+{
+ void IRegister.Register(TypeAdapterConfig config)
+ {
+ // Request -> Query mappings
+ config.NewConfig()
+ .Map(dest => dest.MaxDepth, src => src.MaxDepth > 0 ? src.MaxDepth : 3);
+
+ // Response mappings - Tree
+ config.NewConfig()
+ .Map(dest => dest.RootNode, src => src.RootNode)
+ .Map(dest => dest.TotalMembers, src => src.TotalMembers)
+ .Map(dest => dest.CurrentDepth, src => src.CurrentDepth);
+
+ config.NewConfig()
+ .Map(dest => dest.UserId, src => src.UserId)
+ .Map(dest => dest.FullName, src => src.FullName)
+ .Map(dest => dest.Mobile, src => src.Mobile)
+ .Map(dest => dest.Avatar, src => src.Avatar)
+ .Map(dest => dest.Position, src => src.Position)
+ .Map(dest => dest.LeftChild, src => src.LeftChild)
+ .Map(dest => dest.RightChild, src => src.RightChild)
+ .Map(dest => dest.Level, src => src.Level)
+ .Map(dest => dest.HasChildren, src => src.HasChildren);
+
+ // Response mappings - Statistics
+ config.NewConfig()
+ .Map(dest => dest.TotalMembers, src => src.TotalMembers)
+ .Map(dest => dest.ActiveMembers, src => src.ActiveMembers)
+ .Map(dest => dest.LeftLegCount, src => src.LeftLegCount)
+ .Map(dest => dest.RightLegCount, src => src.RightLegCount)
+ .Map(dest => dest.LeftPercentage, src => src.LeftPercentage)
+ .Map(dest => dest.RightPercentage, src => src.RightPercentage)
+ .Map(dest => dest.AverageDepth, src => src.AverageDepth)
+ .Map(dest => dest.MaxDepth, src => src.MaxDepth)
+ .Map(dest => dest.WeakerLeg, src => src.WeakerLeg)
+ .Map(dest => dest.MyNetworkLevel, src => src.MyNetworkLevel)
+ .Map(dest => dest.MyNetworkLeg, src => src.MyNetworkLeg)
+ .Map(dest => dest.MyReferralCode, src => src.MyReferralCode)
+ .Map(dest => dest.LastMember, src => src.LastMember);
+
+ config.NewConfig()
+ .Map(dest => dest.UserId, src => src.UserId)
+ .Map(dest => dest.FullName, src => src.FullName)
+ .Map(dest => dest.Position, src => src.Position)
+ .Map(dest => dest.TotalChildren, src => src.TotalChildren);
+
+ // Response mappings - Position
+ config.NewConfig()
+ .Map(dest => dest.UserId, src => src.UserId)
+ .Map(dest => dest.ParentId, src => src.ParentId)
+ .Map(dest => dest.ParentName, src => src.ParentName)
+ .Map(dest => dest.Position, src => src.Position)
+ .Map(dest => dest.Level, src => src.Level)
+ .Map(dest => dest.ReferralCode, src => src.ReferralCode)
+ .Map(dest => dest.JoinedAt, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.JoinedAt, DateTimeKind.Utc)))
+ .Map(dest => dest.HasLeftChild, src => src.HasLeftChild)
+ .Map(dest => dest.HasRightChild, src => src.HasRightChild);
+ }
+}
diff --git a/src/FrontOffice.BFF.WebApi/FrontOffice.BFF.WebApi.csproj b/src/FrontOffice.BFF.WebApi/FrontOffice.BFF.WebApi.csproj
index 4b15dc2..d117c8c 100644
--- a/src/FrontOffice.BFF.WebApi/FrontOffice.BFF.WebApi.csproj
+++ b/src/FrontOffice.BFF.WebApi/FrontOffice.BFF.WebApi.csproj
@@ -31,5 +31,9 @@
+
+
+
+
diff --git a/src/FrontOffice.BFF.WebApi/Services/ClubMembershipGrpcService.cs b/src/FrontOffice.BFF.WebApi/Services/ClubMembershipGrpcService.cs
new file mode 100644
index 0000000..9457ade
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Services/ClubMembershipGrpcService.cs
@@ -0,0 +1,35 @@
+using FrontOffice.BFF.WebApi.Common.Services;
+using FrontOffice.BFF.Application.ClubMembershipCQ.Queries.GetMyClubMembership;
+using FrontOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateMyClubMembership;
+using FrontOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership;
+
+namespace FrontOffice.BFF.WebApi.Services;
+
+///
+/// سرویس عضویت باشگاه - برای کاربران FrontOffice
+///
+public class ClubMembershipGrpcService : ClubMembershipContract.ClubMembershipContractBase
+{
+ private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
+
+ public ClubMembershipGrpcService(IDispatchRequestToCQRS dispatchRequestToCQRS)
+ {
+ _dispatchRequestToCQRS = dispatchRequestToCQRS;
+ }
+
+ ///
+ /// دریافت وضعیت عضویت باشگاه کاربر جاری
+ ///
+ public override async Task GetMyClubMembership(Empty request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(context);
+ }
+
+ ///
+ /// فعالسازی عضویت باشگاه (پرداخت 56M)
+ ///
+ public override async Task ActivateMyClubMembership(ActivateMyClubMembershipRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+}
diff --git a/src/FrontOffice.BFF.WebApi/Services/ClubMembershipService.cs b/src/FrontOffice.BFF.WebApi/Services/ClubMembershipService.cs
index dd9bd39..bfa078d 100644
--- a/src/FrontOffice.BFF.WebApi/Services/ClubMembershipService.cs
+++ b/src/FrontOffice.BFF.WebApi/Services/ClubMembershipService.cs
@@ -1,6 +1,8 @@
using FrontOffice.BFF.WebApi.Common.Services;
using FrontOffice.BFF.Application.ClubMembershipCQ.Queries.GetMyClubMembership;
using FrontOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateMyClubMembership;
+using MediatR;
+using System.Threading;
namespace FrontOffice.BFF.WebApi.Services;
@@ -11,19 +13,19 @@ namespace FrontOffice.BFF.WebApi.Services;
///
public class ClubMembershipService
{
- private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
+ private readonly ISender _sender;
- public ClubMembershipService(IDispatchRequestToCQRS dispatchRequestToCQRS)
+ public ClubMembershipService(ISender sender)
{
- _dispatchRequestToCQRS = dispatchRequestToCQRS;
+ _sender = sender;
}
///
/// دریافت وضعیت عضویت باشگاه کاربر جاری
///
- public async Task GetMyClubMembership(Empty request, ServerCallContext context)
+ public async Task GetMyClubMembership(CancellationToken cancellationToken = default)
{
- return await _dispatchRequestToCQRS.Handle(context);
+ return await _sender.Send(new GetMyClubMembershipQuery(), cancellationToken);
}
///
@@ -31,8 +33,8 @@ public class ClubMembershipService
///
public async Task ActivateMyClubMembership(
ActivateMyClubMembershipCommand request,
- ServerCallContext context)
+ CancellationToken cancellationToken = default)
{
- return await _dispatchRequestToCQRS.Handle(request, context);
+ return await _sender.Send(request, cancellationToken);
}
}
diff --git a/src/FrontOffice.BFF.WebApi/Services/CommissionService.cs b/src/FrontOffice.BFF.WebApi/Services/CommissionService.cs
new file mode 100644
index 0000000..4a19c5d
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Services/CommissionService.cs
@@ -0,0 +1,35 @@
+using FrontOffice.BFF.WebApi.Common.Services;
+using FrontOffice.BFF.Application.CommissionCQ.Queries.GetMyCommissionPayouts;
+using FrontOffice.BFF.Application.CommissionCQ.Queries.GetMyWeeklyBalances;
+using FrontOffice.BFF.Commission.Protobuf.Protos.Commission;
+
+namespace FrontOffice.BFF.WebApi.Services;
+
+///
+/// سرویس کمیسیون - برای کاربران FrontOffice
+///
+public class CommissionService : CommissionContract.CommissionContractBase
+{
+ private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
+
+ public CommissionService(IDispatchRequestToCQRS dispatchRequestToCQRS)
+ {
+ _dispatchRequestToCQRS = dispatchRequestToCQRS;
+ }
+
+ ///
+ /// دریافت لیست پرداختهای کمیسیون کاربر
+ ///
+ public override async Task GetMyCommissionPayouts(GetMyCommissionPayoutsRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+
+ ///
+ /// دریافت لیست بالانسهای هفتگی کاربر
+ ///
+ public override async Task GetMyWeeklyBalances(GetMyWeeklyBalancesRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+}
diff --git a/src/FrontOffice.BFF.WebApi/Services/DiscountShopService.cs b/src/FrontOffice.BFF.WebApi/Services/DiscountShopService.cs
new file mode 100644
index 0000000..f6dfd98
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Services/DiscountShopService.cs
@@ -0,0 +1,80 @@
+using FrontOffice.BFF.WebApi.Common.Services;
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountProducts;
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetDiscountCategories;
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountCart;
+using FrontOffice.BFF.Application.DiscountShopCQ.Queries.GetMyDiscountOrders;
+using FrontOffice.BFF.Application.DiscountShopCQ.Commands.AddToDiscountCart;
+using FrontOffice.BFF.Application.DiscountShopCQ.Commands.RemoveFromDiscountCart;
+using FrontOffice.BFF.Application.DiscountShopCQ.Commands.PlaceDiscountOrder;
+using FrontOffice.BFF.DiscountShop.Protobuf.Protos.DiscountShop;
+
+namespace FrontOffice.BFF.WebApi.Services;
+
+///
+/// سرویس فروشگاه تخفیفی - برای کاربران عضو باشگاه
+///
+public class DiscountShopService : DiscountShopContract.DiscountShopContractBase
+{
+ private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
+
+ public DiscountShopService(IDispatchRequestToCQRS dispatchRequestToCQRS)
+ {
+ _dispatchRequestToCQRS = dispatchRequestToCQRS;
+ }
+
+ ///
+ /// دریافت لیست محصولات تخفیفی
+ ///
+ public override async Task GetDiscountProducts(GetDiscountProductsRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+
+ ///
+ /// دریافت دستهبندیهای فروشگاه تخفیفی
+ ///
+ public override async Task GetDiscountCategories(GetDiscountCategoriesRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+
+ ///
+ /// دریافت سبد خرید تخفیفی کاربر جاری
+ ///
+ public override async Task GetMyDiscountCart(Empty request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(context);
+ }
+
+ ///
+ /// افزودن محصول به سبد خرید تخفیفی
+ ///
+ public override async Task AddToDiscountCart(AddToDiscountCartRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+
+ ///
+ /// حذف محصول از سبد خرید تخفیفی
+ ///
+ public override async Task RemoveFromDiscountCart(RemoveFromDiscountCartRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+
+ ///
+ /// دریافت لیست سفارشات تخفیفی کاربر جاری
+ ///
+ public override async Task GetMyDiscountOrders(GetMyDiscountOrdersRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+
+ ///
+ /// ثبت سفارش تخفیفی
+ ///
+ public override async Task PlaceDiscountOrder(PlaceDiscountOrderRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+}
diff --git a/src/FrontOffice.BFF.WebApi/Services/NetworkMembershipService.cs b/src/FrontOffice.BFF.WebApi/Services/NetworkMembershipService.cs
new file mode 100644
index 0000000..4d025a8
--- /dev/null
+++ b/src/FrontOffice.BFF.WebApi/Services/NetworkMembershipService.cs
@@ -0,0 +1,44 @@
+using FrontOffice.BFF.WebApi.Common.Services;
+using FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkTree;
+using FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkStatistics;
+using FrontOffice.BFF.Application.NetworkMembershipCQ.Queries.GetMyNetworkPosition;
+using FrontOffice.BFF.NetworkMembership.Protobuf.Protos.NetworkMembership;
+
+namespace FrontOffice.BFF.WebApi.Services;
+
+///
+/// سرویس عضویت شبکهای - برای کاربران FrontOffice
+///
+public class NetworkMembershipService : NetworkMembershipContract.NetworkMembershipContractBase
+{
+ private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
+
+ public NetworkMembershipService(IDispatchRequestToCQRS dispatchRequestToCQRS)
+ {
+ _dispatchRequestToCQRS = dispatchRequestToCQRS;
+ }
+
+ ///
+ /// دریافت درخت شبکه کاربر جاری
+ ///
+ public override async Task GetMyNetworkTree(GetMyNetworkTreeRequest request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(request, context);
+ }
+
+ ///
+ /// دریافت آمار شبکه کاربر جاری
+ ///
+ public override async Task GetMyNetworkStatistics(Empty request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(context);
+ }
+
+ ///
+ /// دریافت موقعیت کاربر در شبکه
+ ///
+ public override async Task GetMyNetworkPosition(Empty request, ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(context);
+ }
+}
diff --git a/src/FrontOffice.BFF.sln b/src/FrontOffice.BFF.sln
index 493e8ed..6fdb68c 100644
--- a/src/FrontOffice.BFF.sln
+++ b/src/FrontOffice.BFF.sln
@@ -1,3 +1,4 @@
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
@@ -30,64 +31,228 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontOffice.BFF.ShopingCart
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontOffice.BFF.UserWallet.Protobuf", "Protobufs\FrontOffice.BFF.UserWallet.Protobuf\FrontOffice.BFF.UserWallet.Protobuf.csproj", "{03F99CE9-F952-47B0-B71A-1F4865E52443}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontOffice.BFF.DiscountShop.Protobuf", "Protobufs\FrontOffice.BFF.DiscountShop.Protobuf\FrontOffice.BFF.DiscountShop.Protobuf.csproj", "{5547FB9B-7AEF-49C7-AA11-119D37DD9528}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontOffice.BFF.Commission.Protobuf", "Protobufs\FrontOffice.BFF.Commission.Protobuf\FrontOffice.BFF.Commission.Protobuf.csproj", "{B1380466-18E7-4CAD-88F8-E1419D2B6300}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontOffice.BFF.ClubMembership.Protobuf", "Protobufs\FrontOffice.BFF.ClubMembership.Protobuf\FrontOffice.BFF.ClubMembership.Protobuf.csproj", "{B6EAE0A3-3427-4D86-B2BA-B185F476B74F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontOffice.BFF.NetworkMembership.Protobuf", "Protobufs\FrontOffice.BFF.NetworkMembership.Protobuf\FrontOffice.BFF.NetworkMembership.Protobuf.csproj", "{CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7E733B83-275C-4639-AA10-4A59B681B904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E733B83-275C-4639-AA10-4A59B681B904}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Debug|x64.Build.0 = Debug|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Debug|x86.Build.0 = Debug|Any CPU
{7E733B83-275C-4639-AA10-4A59B681B904}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E733B83-275C-4639-AA10-4A59B681B904}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Release|x64.ActiveCfg = Release|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Release|x64.Build.0 = Release|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Release|x86.ActiveCfg = Release|Any CPU
+ {7E733B83-275C-4639-AA10-4A59B681B904}.Release|x86.Build.0 = Release|Any CPU
{56107B61-262D-413A-A9B6-4F3730220415}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{56107B61-262D-413A-A9B6-4F3730220415}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Debug|x64.Build.0 = Debug|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Debug|x86.Build.0 = Debug|Any CPU
{56107B61-262D-413A-A9B6-4F3730220415}.Release|Any CPU.ActiveCfg = Release|Any CPU
{56107B61-262D-413A-A9B6-4F3730220415}.Release|Any CPU.Build.0 = Release|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Release|x64.ActiveCfg = Release|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Release|x64.Build.0 = Release|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Release|x86.ActiveCfg = Release|Any CPU
+ {56107B61-262D-413A-A9B6-4F3730220415}.Release|x86.Build.0 = Release|Any CPU
{41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Debug|x64.Build.0 = Debug|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Debug|x86.Build.0 = Debug|Any CPU
{41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Release|Any CPU.Build.0 = Release|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Release|x64.ActiveCfg = Release|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Release|x64.Build.0 = Release|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Release|x86.ActiveCfg = Release|Any CPU
+ {41CA2D15-9289-4A24-A519-EFB1F7CEB633}.Release|x86.Build.0 = Release|Any CPU
{1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Debug|x64.Build.0 = Debug|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Debug|x86.Build.0 = Debug|Any CPU
{1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Release|x64.ActiveCfg = Release|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Release|x64.Build.0 = Release|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Release|x86.ActiveCfg = Release|Any CPU
+ {1E7A5065-4B24-4B12-A0F2-7B0564989C95}.Release|x86.Build.0 = Release|Any CPU
{F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Debug|x64.Build.0 = Debug|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Debug|x86.Build.0 = Debug|Any CPU
{F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Release|x64.ActiveCfg = Release|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Release|x64.Build.0 = Release|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Release|x86.ActiveCfg = Release|Any CPU
+ {F4E98BE4-6F95-4B4E-924D-CBAD02AF24F1}.Release|x86.Build.0 = Release|Any CPU
{C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Debug|x64.Build.0 = Debug|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Debug|x86.Build.0 = Debug|Any CPU
{C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Release|x64.ActiveCfg = Release|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Release|x64.Build.0 = Release|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Release|x86.ActiveCfg = Release|Any CPU
+ {C8A16685-0A51-4D1A-B399-FB94C90D9BDC}.Release|x86.Build.0 = Release|Any CPU
{D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Debug|x64.Build.0 = Debug|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Debug|x86.Build.0 = Debug|Any CPU
{D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Release|x64.ActiveCfg = Release|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Release|x64.Build.0 = Release|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Release|x86.ActiveCfg = Release|Any CPU
+ {D70F0C9A-E954-4A67-B23D-9BE22721BD5D}.Release|x86.Build.0 = Release|Any CPU
{663CDDFA-E15F-4356-AE01-2311C9B83D52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{663CDDFA-E15F-4356-AE01-2311C9B83D52}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Debug|x64.Build.0 = Debug|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Debug|x86.Build.0 = Debug|Any CPU
{663CDDFA-E15F-4356-AE01-2311C9B83D52}.Release|Any CPU.ActiveCfg = Release|Any CPU
{663CDDFA-E15F-4356-AE01-2311C9B83D52}.Release|Any CPU.Build.0 = Release|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Release|x64.ActiveCfg = Release|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Release|x64.Build.0 = Release|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Release|x86.ActiveCfg = Release|Any CPU
+ {663CDDFA-E15F-4356-AE01-2311C9B83D52}.Release|x86.Build.0 = Release|Any CPU
{F59861D9-01D6-44C9-85A9-E6050D55D290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F59861D9-01D6-44C9-85A9-E6050D55D290}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Debug|x64.Build.0 = Debug|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Debug|x86.Build.0 = Debug|Any CPU
{F59861D9-01D6-44C9-85A9-E6050D55D290}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F59861D9-01D6-44C9-85A9-E6050D55D290}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Release|x64.ActiveCfg = Release|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Release|x64.Build.0 = Release|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Release|x86.ActiveCfg = Release|Any CPU
+ {F59861D9-01D6-44C9-85A9-E6050D55D290}.Release|x86.Build.0 = Release|Any CPU
{CB77669F-5B48-4AC6-B20E-A928660E93F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB77669F-5B48-4AC6-B20E-A928660E93F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Debug|x64.Build.0 = Debug|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Debug|x86.Build.0 = Debug|Any CPU
{CB77669F-5B48-4AC6-B20E-A928660E93F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB77669F-5B48-4AC6-B20E-A928660E93F8}.Release|Any CPU.Build.0 = Release|Any CPU
- {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|Any CPU.Build.0 = Release|Any CPU
- {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Release|x64.ActiveCfg = Release|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Release|x64.Build.0 = Release|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Release|x86.ActiveCfg = Release|Any CPU
+ {CB77669F-5B48-4AC6-B20E-A928660E93F8}.Release|x86.Build.0 = Release|Any CPU
{E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Debug|x64.Build.0 = Debug|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Debug|x86.Build.0 = Debug|Any CPU
{E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Release|x64.ActiveCfg = Release|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Release|x64.Build.0 = Release|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Release|x86.ActiveCfg = Release|Any CPU
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C}.Release|x86.Build.0 = Release|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|x64.Build.0 = Debug|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Debug|x86.Build.0 = Debug|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|x64.ActiveCfg = Release|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|x64.Build.0 = Release|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|x86.ActiveCfg = Release|Any CPU
+ {DC61324B-D389-4A1D-B048-D0AA43A6BBE7}.Release|x86.Build.0 = Release|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|x64.Build.0 = Debug|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Debug|x86.Build.0 = Debug|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|Any CPU.Build.0 = Release|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|x64.ActiveCfg = Release|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|x64.Build.0 = Release|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|x86.ActiveCfg = Release|Any CPU
+ {03F99CE9-F952-47B0-B71A-1F4865E52443}.Release|x86.Build.0 = Release|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Debug|x64.Build.0 = Debug|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Debug|x86.Build.0 = Debug|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Release|x64.ActiveCfg = Release|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Release|x64.Build.0 = Release|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Release|x86.ActiveCfg = Release|Any CPU
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528}.Release|x86.Build.0 = Release|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Debug|x64.Build.0 = Debug|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Debug|x86.Build.0 = Debug|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Release|x64.ActiveCfg = Release|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Release|x64.Build.0 = Release|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Release|x86.ActiveCfg = Release|Any CPU
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300}.Release|x86.Build.0 = Release|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Debug|x64.Build.0 = Debug|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Debug|x86.Build.0 = Debug|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Release|x64.ActiveCfg = Release|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Release|x64.Build.0 = Release|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Release|x86.ActiveCfg = Release|Any CPU
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F}.Release|x86.Build.0 = Release|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Debug|x64.Build.0 = Debug|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Debug|x86.Build.0 = Debug|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Release|x64.ActiveCfg = Release|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Release|x64.Build.0 = Release|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Release|x86.ActiveCfg = Release|Any CPU
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -99,8 +264,12 @@ Global
{663CDDFA-E15F-4356-AE01-2311C9B83D52} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
{F59861D9-01D6-44C9-85A9-E6050D55D290} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
{CB77669F-5B48-4AC6-B20E-A928660E93F8} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
+ {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
{DC61324B-D389-4A1D-B048-D0AA43A6BBE7} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
{03F99CE9-F952-47B0-B71A-1F4865E52443} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
- {E3F6D1B7-DB78-4F36-BE77-2F9D2D7B5B7C} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
+ {5547FB9B-7AEF-49C7-AA11-119D37DD9528} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
+ {B1380466-18E7-4CAD-88F8-E1419D2B6300} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
+ {B6EAE0A3-3427-4D86-B2BA-B185F476B74F} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
+ {CCA23A57-4BC4-4C53-9A96-41FCFF5407F5} = {CA9BF4D6-6729-4011-888E-48F5F739B469}
EndGlobalSection
EndGlobal
diff --git a/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/ConfigureServices.cs b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/ConfigureServices.cs
new file mode 100644
index 0000000..860e2a2
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/ConfigureServices.cs
@@ -0,0 +1,13 @@
+using FluentValidation;
+using System.Reflection;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class ConfigureServices
+{
+ public static IServiceCollection AddClubMembershipProtobufServices(this IServiceCollection services)
+ {
+ services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
+ return services;
+ }
+}
diff --git a/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/FrontOffice.BFF.ClubMembership.Protobuf.csproj b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/FrontOffice.BFF.ClubMembership.Protobuf.csproj
new file mode 100644
index 0000000..4acdc9e
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/FrontOffice.BFF.ClubMembership.Protobuf.csproj
@@ -0,0 +1,25 @@
+
+
+ net9.0
+ enable
+ enable
+ 0.0.1
+ Foursat.FrontOffice.BFF.ClubMembership.Protobuf
+ False
+ False
+ None
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/clubmembership.proto b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/clubmembership.proto
new file mode 100644
index 0000000..e2b6953
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/clubmembership.proto
@@ -0,0 +1,61 @@
+syntax = "proto3";
+
+package clubmembership;
+
+import "google/protobuf/empty.proto";
+import "google/protobuf/wrappers.proto";
+import "google/protobuf/timestamp.proto";
+import "google/api/annotations.proto";
+
+option csharp_namespace = "FrontOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership";
+
+// سرویس عضویت باشگاه - برای کاربران FrontOffice
+service ClubMembershipContract
+{
+ // دریافت وضعیت عضویت باشگاه کاربر جاری
+ rpc GetMyClubMembership(google.protobuf.Empty) returns (GetMyClubMembershipResponse){
+ option (google.api.http) = {
+ get: "/ClubMembership/MyStatus"
+ };
+ };
+
+ // فعالسازی عضویت باشگاه (پرداخت 56M)
+ rpc ActivateMyClubMembership(ActivateMyClubMembershipRequest) returns (ActivateMyClubMembershipResponse){
+ option (google.api.http) = {
+ post: "/ClubMembership/Activate"
+ body: "*"
+ };
+ };
+}
+
+// ============ GetMyClubMembership ============
+message GetMyClubMembershipResponse
+{
+ int64 user_id = 1;
+ int64 package_id = 2;
+ string package_name = 3;
+ string activation_code = 4;
+ bool is_active = 5;
+ google.protobuf.Timestamp activation_date = 6;
+ google.protobuf.Timestamp expiration_date = 7;
+ string status = 8; // Trial/Active/Expired/Inactive
+ int32 days_remaining = 9;
+ bool is_trial_period = 10;
+}
+
+// ============ ActivateMyClubMembership ============
+message ActivateMyClubMembershipRequest
+{
+ int64 package_id = 1;
+ google.protobuf.StringValue activation_code = 2;
+ int32 duration_months = 3; // پیشفرض: 12
+}
+
+message ActivateMyClubMembershipResponse
+{
+ bool success = 1;
+ string message = 2;
+ google.protobuf.Timestamp activation_date = 3;
+ google.protobuf.Timestamp expiration_date = 4;
+ int64 amount_paid = 5;
+}
diff --git a/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/google/api/annotations.proto b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/google/api/annotations.proto
new file mode 100644
index 0000000..85c361b
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/google/api/annotations.proto
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+ // See `HttpRule`.
+ HttpRule http = 72295728;
+}
diff --git a/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/google/api/http.proto b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/google/api/http.proto
new file mode 100644
index 0000000..b8426ba
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.ClubMembership.Protobuf/Protos/google/api/http.proto
@@ -0,0 +1,377 @@
+// Copyright 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+syntax = "proto3";
+
+package google.api;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+ // A list of HTTP configuration rules that apply to individual API methods.
+ //
+ // **NOTE:** All service configuration rules follow "last one wins" order.
+ repeated HttpRule rules = 1;
+
+ // When set to true, URL path parameters will be fully URI-decoded except in
+ // cases of single segment matches in reserved expansion, where "%2F" will be
+ // left encoded.
+ //
+ // The default behavior is to not decode RFC 6570 reserved characters in multi
+ // segment matches.
+ bool fully_decode_reserved_expansion = 2;
+}
+
+// # gRPC Transcoding
+//
+// gRPC Transcoding is a feature for mapping between a gRPC method and one or
+// more HTTP REST endpoints. It allows developers to build a single API service
+// that supports both gRPC APIs and REST APIs. Many systems, including [Google
+// APIs](https://github.com/googleapis/googleapis),
+// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
+// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
+// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
+// and use it for large scale production services.
+//
+// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
+// how different portions of the gRPC request message are mapped to the URL
+// path, URL query parameters, and HTTP request body. It also controls how the
+// gRPC response message is mapped to the HTTP response body. `HttpRule` is
+// typically specified as an `google.api.http` annotation on the gRPC method.
+//
+// Each mapping specifies a URL path template and an HTTP method. The path
+// template may refer to one or more fields in the gRPC request message, as long
+// as each field is a non-repeated field with a primitive (non-message) type.
+// The path template controls how fields of the request message are mapped to
+// the URL path.
+//
+// Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/{name=messages/*}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// string name = 1; // Mapped to URL path.
+// }
+// message Message {
+// string text = 1; // The resource content.
+// }
+//
+// This enables an HTTP REST to gRPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")`
+//
+// Any fields in the request message which are not bound by the path template
+// automatically become HTTP query parameters if there is no HTTP request body.
+// For example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get:"/v1/messages/{message_id}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // Mapped to URL path.
+// int64 revision = 2; // Mapped to URL query parameter `revision`.
+// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
+// }
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` |
+// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
+// "foo"))`
+//
+// Note that fields which are mapped to URL query parameters must have a
+// primitive type or a repeated primitive type or a non-repeated message type.
+// In the case of a repeated type, the parameter can be repeated in the URL
+// as `...?param=A¶m=B`. In the case of a message type, each field of the
+// message is mapped to a separate parameter, such as
+// `...?foo.a=A&foo.b=B&foo.c=C`.
+//
+// For HTTP methods that allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+// service Messaging {
+// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "message"
+// };
+// }
+// }
+// message UpdateMessageRequest {
+// string message_id = 1; // mapped to the URL
+// Message message = 2; // mapped to the body
+// }
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body. This enables the following alternative definition of
+// the update method:
+//
+// service Messaging {
+// rpc UpdateMessage(Message) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "*"
+// };
+// }
+// }
+// message Message {
+// string message_id = 1;
+// string text = 2;
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice when
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/messages/{message_id}"
+// additional_bindings {
+// get: "/v1/users/{user_id}/messages/{message_id}"
+// }
+// };
+// }
+// }
+// message GetMessageRequest {
+// string message_id = 1;
+// string user_id = 2;
+// }
+//
+// This enables the following two alternative HTTP JSON to RPC mappings:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
+// "123456")`
+//
+// ## Rules for HTTP mapping
+//
+// 1. Leaf request fields (recursive expansion nested messages in the request
+// message) are classified into three categories:
+// - Fields referred by the path template. They are passed via the URL path.
+// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP
+// request body.
+// - All other fields are passed via the URL query parameters, and the
+// parameter name is the field path in the request message. A repeated
+// field can be represented as multiple query parameters under the same
+// name.
+// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields
+// are passed via URL path and HTTP request body.
+// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all
+// fields are passed via URL path and URL query parameters.
+//
+// ### Path template syntax
+//
+// Template = "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+//
+// The syntax `*` matches a single URL path segment. The syntax `**` matches
+// zero or more URL path segments, which must be the last part of the URL path
+// except the `Verb`.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
+// contains any reserved character, such characters should be percent-encoded
+// before the matching.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path on the client
+// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
+// server side does the reverse decoding. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{var}`.
+//
+// If a variable contains multiple path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path on the
+// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
+// The server side does the reverse decoding, except "%2F" and "%2f" are left
+// unchanged. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{+var}`.
+//
+// ## Using gRPC API Service Configuration
+//
+// gRPC API Service Configuration (service config) is a configuration language
+// for configuring a gRPC service to become a user-facing product. The
+// service config is simply the YAML representation of the `google.api.Service`
+// proto message.
+//
+// As an alternative to annotating your proto file, you can configure gRPC
+// transcoding in your service config YAML files. You do this by specifying a
+// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
+// effect as the proto annotation. This can be particularly useful if you
+// have a proto that is reused in multiple services. Note that any transcoding
+// specified in the service config will override any matching transcoding
+// configuration in the proto.
+//
+// Example:
+//
+// http:
+// rules:
+// # Selects a gRPC method and applies HttpRule to it.
+// - selector: example.v1.Messaging.GetMessage
+// get: /v1/messages/{message_id}/{sub.subfield}
+//
+// ## Special notes
+//
+// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
+// proto to JSON conversion must follow the [proto3
+// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
+//
+// While the single segment variable follows the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
+// Expansion, the multi segment variable **does not** follow RFC 6570 Section
+// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
+// for multi segment variables.
+//
+// The path variables **must not** refer to any repeated or mapped field,
+// because client libraries are not capable of handling such variable expansion.
+//
+// The path variables **must not** capture the leading "/" character. The reason
+// is that the most common use case "{var}" does not capture the leading "/"
+// character. For consistency, all path variables must share the same behavior.
+//
+// Repeated message fields must not be mapped to URL query parameters, because
+// no client library can support such complicated mapping.
+//
+// If an API needs to use a JSON array for request or response body, it can map
+// the request or response body to a repeated field. However, some gRPC
+// Transcoding implementations may not support this feature.
+message HttpRule {
+ // Selects a method to which this rule applies.
+ //
+ // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+ string selector = 1;
+
+ // Determines the URL pattern is matched by this rules. This pattern can be
+ // used with any of the {get|put|post|delete|patch} methods. A custom method
+ // can be defined using the 'custom' field.
+ oneof pattern {
+ // Maps to HTTP GET. Used for listing and getting information about
+ // resources.
+ string get = 2;
+
+ // Maps to HTTP PUT. Used for replacing a resource.
+ string put = 3;
+
+ // Maps to HTTP POST. Used for creating a resource or performing an action.
+ string post = 4;
+
+ // Maps to HTTP DELETE. Used for deleting a resource.
+ string delete = 5;
+
+ // Maps to HTTP PATCH. Used for updating a resource.
+ string patch = 6;
+
+ // The custom pattern is used for specifying an HTTP method that is not
+ // included in the `pattern` field, such as HEAD, or "*" to leave the
+ // HTTP method unspecified for this rule. The wild-card rule is useful
+ // for services that provide content to Web (HTML) clients.
+ CustomHttpPattern custom = 8;
+ }
+
+ // The name of the request field whose value is mapped to the HTTP request
+ // body, or `*` for mapping all request fields not captured by the path
+ // pattern to the HTTP body, or omitted for not having any HTTP request body.
+ //
+ // NOTE: the referred field must be present at the top-level of the request
+ // message type.
+ string body = 7;
+
+ // Optional. The name of the response field whose value is mapped to the HTTP
+ // response body. When omitted, the entire response message will be used
+ // as the HTTP response body.
+ //
+ // NOTE: The referred field must be present at the top-level of the response
+ // message type.
+ string response_body = 12;
+
+ // Additional HTTP bindings for the selector. Nested bindings must
+ // not contain an `additional_bindings` field themselves (that is,
+ // the nesting may only be one level deep).
+ repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+ // The name of this custom HTTP verb.
+ string kind = 1;
+
+ // The path matched by this custom verb.
+ string path = 2;
+}
+
diff --git a/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/ConfigureServices.cs b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/ConfigureServices.cs
new file mode 100644
index 0000000..a137c4b
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/ConfigureServices.cs
@@ -0,0 +1,13 @@
+using FluentValidation;
+using System.Reflection;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class ConfigureServices
+{
+ public static IServiceCollection AddCommissionProtobufServices(this IServiceCollection services)
+ {
+ services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
+ return services;
+ }
+}
diff --git a/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/FrontOffice.BFF.Commission.Protobuf.csproj b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/FrontOffice.BFF.Commission.Protobuf.csproj
new file mode 100644
index 0000000..2612d4b
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/FrontOffice.BFF.Commission.Protobuf.csproj
@@ -0,0 +1,25 @@
+
+
+ net9.0
+ enable
+ enable
+ 0.0.1
+ Foursat.FrontOffice.BFF.Commission.Protobuf
+ False
+ False
+ None
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/commission.proto b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/commission.proto
new file mode 100644
index 0000000..1cabdd1
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/commission.proto
@@ -0,0 +1,94 @@
+syntax = "proto3";
+
+package commission;
+
+import "google/protobuf/empty.proto";
+import "google/protobuf/wrappers.proto";
+import "google/protobuf/timestamp.proto";
+import "google/api/annotations.proto";
+
+option csharp_namespace = "FrontOffice.BFF.Commission.Protobuf.Protos.Commission";
+
+// سرویس کمیسیون - برای کاربران FrontOffice
+service CommissionContract
+{
+ // دریافت لیست پرداختهای کمیسیون کاربر
+ rpc GetMyCommissionPayouts(GetMyCommissionPayoutsRequest) returns (GetMyCommissionPayoutsResponse){
+ option (google.api.http) = {
+ get: "/Commission/MyPayouts"
+ };
+ };
+
+ // دریافت لیست بالانسهای هفتگی کاربر
+ rpc GetMyWeeklyBalances(GetMyWeeklyBalancesRequest) returns (GetMyWeeklyBalancesResponse){
+ option (google.api.http) = {
+ get: "/Commission/MyWeeklyBalances"
+ };
+ };
+}
+
+// ============ MetaData ============
+message MetaData
+{
+ int64 total_count = 1;
+}
+
+// ============ GetMyCommissionPayouts ============
+message GetMyCommissionPayoutsRequest
+{
+ int32 page_number = 1;
+ int32 page_size = 2;
+ google.protobuf.StringValue week_number = 3;
+ google.protobuf.Int32Value status = 4; // 0=Pending, 1=Calculated, 2=Paid, 3=Withdrawn
+}
+
+message GetMyCommissionPayoutsResponse
+{
+ MetaData meta_data = 1;
+ repeated CommissionPayoutModel payouts = 2;
+}
+
+message CommissionPayoutModel
+{
+ int64 id = 1;
+ string week_number = 2;
+ string week_label = 3;
+ int32 balances_earned = 4;
+ int64 total_amount = 5;
+ string amount_formatted = 6;
+ string status = 7;
+ string status_badge_color = 8;
+ google.protobuf.Timestamp calculated_date = 9;
+ string date_persian = 10;
+}
+
+// ============ GetMyWeeklyBalances ============
+message GetMyWeeklyBalancesRequest
+{
+ int32 page_number = 1;
+ int32 page_size = 2;
+ google.protobuf.StringValue week_number = 3;
+ bool only_active = 4;
+}
+
+message GetMyWeeklyBalancesResponse
+{
+ MetaData meta_data = 1;
+ repeated WeeklyBalanceModel balances = 2;
+ int32 total_left_balances = 3;
+ int32 total_right_balances = 4;
+ string weaker_leg = 5;
+}
+
+message WeeklyBalanceModel
+{
+ int64 id = 1;
+ string week_number = 2;
+ int32 left_leg_balances = 3;
+ int32 right_leg_balances = 4;
+ int32 total_balances = 5;
+ int64 weekly_pool_contribution = 6;
+ bool is_expired = 7;
+ google.protobuf.Timestamp calculated_at = 8;
+ string date_persian = 9;
+}
diff --git a/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/google/api/annotations.proto b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/google/api/annotations.proto
new file mode 100644
index 0000000..85c361b
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/google/api/annotations.proto
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+ // See `HttpRule`.
+ HttpRule http = 72295728;
+}
diff --git a/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/google/api/http.proto b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/google/api/http.proto
new file mode 100644
index 0000000..b8426ba
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.Commission.Protobuf/Protos/google/api/http.proto
@@ -0,0 +1,377 @@
+// Copyright 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+syntax = "proto3";
+
+package google.api;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+ // A list of HTTP configuration rules that apply to individual API methods.
+ //
+ // **NOTE:** All service configuration rules follow "last one wins" order.
+ repeated HttpRule rules = 1;
+
+ // When set to true, URL path parameters will be fully URI-decoded except in
+ // cases of single segment matches in reserved expansion, where "%2F" will be
+ // left encoded.
+ //
+ // The default behavior is to not decode RFC 6570 reserved characters in multi
+ // segment matches.
+ bool fully_decode_reserved_expansion = 2;
+}
+
+// # gRPC Transcoding
+//
+// gRPC Transcoding is a feature for mapping between a gRPC method and one or
+// more HTTP REST endpoints. It allows developers to build a single API service
+// that supports both gRPC APIs and REST APIs. Many systems, including [Google
+// APIs](https://github.com/googleapis/googleapis),
+// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
+// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
+// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
+// and use it for large scale production services.
+//
+// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
+// how different portions of the gRPC request message are mapped to the URL
+// path, URL query parameters, and HTTP request body. It also controls how the
+// gRPC response message is mapped to the HTTP response body. `HttpRule` is
+// typically specified as an `google.api.http` annotation on the gRPC method.
+//
+// Each mapping specifies a URL path template and an HTTP method. The path
+// template may refer to one or more fields in the gRPC request message, as long
+// as each field is a non-repeated field with a primitive (non-message) type.
+// The path template controls how fields of the request message are mapped to
+// the URL path.
+//
+// Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/{name=messages/*}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// string name = 1; // Mapped to URL path.
+// }
+// message Message {
+// string text = 1; // The resource content.
+// }
+//
+// This enables an HTTP REST to gRPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")`
+//
+// Any fields in the request message which are not bound by the path template
+// automatically become HTTP query parameters if there is no HTTP request body.
+// For example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get:"/v1/messages/{message_id}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // Mapped to URL path.
+// int64 revision = 2; // Mapped to URL query parameter `revision`.
+// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
+// }
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` |
+// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
+// "foo"))`
+//
+// Note that fields which are mapped to URL query parameters must have a
+// primitive type or a repeated primitive type or a non-repeated message type.
+// In the case of a repeated type, the parameter can be repeated in the URL
+// as `...?param=A¶m=B`. In the case of a message type, each field of the
+// message is mapped to a separate parameter, such as
+// `...?foo.a=A&foo.b=B&foo.c=C`.
+//
+// For HTTP methods that allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+// service Messaging {
+// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "message"
+// };
+// }
+// }
+// message UpdateMessageRequest {
+// string message_id = 1; // mapped to the URL
+// Message message = 2; // mapped to the body
+// }
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body. This enables the following alternative definition of
+// the update method:
+//
+// service Messaging {
+// rpc UpdateMessage(Message) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "*"
+// };
+// }
+// }
+// message Message {
+// string message_id = 1;
+// string text = 2;
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice when
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/messages/{message_id}"
+// additional_bindings {
+// get: "/v1/users/{user_id}/messages/{message_id}"
+// }
+// };
+// }
+// }
+// message GetMessageRequest {
+// string message_id = 1;
+// string user_id = 2;
+// }
+//
+// This enables the following two alternative HTTP JSON to RPC mappings:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
+// "123456")`
+//
+// ## Rules for HTTP mapping
+//
+// 1. Leaf request fields (recursive expansion nested messages in the request
+// message) are classified into three categories:
+// - Fields referred by the path template. They are passed via the URL path.
+// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP
+// request body.
+// - All other fields are passed via the URL query parameters, and the
+// parameter name is the field path in the request message. A repeated
+// field can be represented as multiple query parameters under the same
+// name.
+// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields
+// are passed via URL path and HTTP request body.
+// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all
+// fields are passed via URL path and URL query parameters.
+//
+// ### Path template syntax
+//
+// Template = "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+//
+// The syntax `*` matches a single URL path segment. The syntax `**` matches
+// zero or more URL path segments, which must be the last part of the URL path
+// except the `Verb`.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
+// contains any reserved character, such characters should be percent-encoded
+// before the matching.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path on the client
+// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
+// server side does the reverse decoding. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{var}`.
+//
+// If a variable contains multiple path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path on the
+// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
+// The server side does the reverse decoding, except "%2F" and "%2f" are left
+// unchanged. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{+var}`.
+//
+// ## Using gRPC API Service Configuration
+//
+// gRPC API Service Configuration (service config) is a configuration language
+// for configuring a gRPC service to become a user-facing product. The
+// service config is simply the YAML representation of the `google.api.Service`
+// proto message.
+//
+// As an alternative to annotating your proto file, you can configure gRPC
+// transcoding in your service config YAML files. You do this by specifying a
+// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
+// effect as the proto annotation. This can be particularly useful if you
+// have a proto that is reused in multiple services. Note that any transcoding
+// specified in the service config will override any matching transcoding
+// configuration in the proto.
+//
+// Example:
+//
+// http:
+// rules:
+// # Selects a gRPC method and applies HttpRule to it.
+// - selector: example.v1.Messaging.GetMessage
+// get: /v1/messages/{message_id}/{sub.subfield}
+//
+// ## Special notes
+//
+// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
+// proto to JSON conversion must follow the [proto3
+// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
+//
+// While the single segment variable follows the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
+// Expansion, the multi segment variable **does not** follow RFC 6570 Section
+// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
+// for multi segment variables.
+//
+// The path variables **must not** refer to any repeated or mapped field,
+// because client libraries are not capable of handling such variable expansion.
+//
+// The path variables **must not** capture the leading "/" character. The reason
+// is that the most common use case "{var}" does not capture the leading "/"
+// character. For consistency, all path variables must share the same behavior.
+//
+// Repeated message fields must not be mapped to URL query parameters, because
+// no client library can support such complicated mapping.
+//
+// If an API needs to use a JSON array for request or response body, it can map
+// the request or response body to a repeated field. However, some gRPC
+// Transcoding implementations may not support this feature.
+message HttpRule {
+ // Selects a method to which this rule applies.
+ //
+ // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+ string selector = 1;
+
+ // Determines the URL pattern is matched by this rules. This pattern can be
+ // used with any of the {get|put|post|delete|patch} methods. A custom method
+ // can be defined using the 'custom' field.
+ oneof pattern {
+ // Maps to HTTP GET. Used for listing and getting information about
+ // resources.
+ string get = 2;
+
+ // Maps to HTTP PUT. Used for replacing a resource.
+ string put = 3;
+
+ // Maps to HTTP POST. Used for creating a resource or performing an action.
+ string post = 4;
+
+ // Maps to HTTP DELETE. Used for deleting a resource.
+ string delete = 5;
+
+ // Maps to HTTP PATCH. Used for updating a resource.
+ string patch = 6;
+
+ // The custom pattern is used for specifying an HTTP method that is not
+ // included in the `pattern` field, such as HEAD, or "*" to leave the
+ // HTTP method unspecified for this rule. The wild-card rule is useful
+ // for services that provide content to Web (HTML) clients.
+ CustomHttpPattern custom = 8;
+ }
+
+ // The name of the request field whose value is mapped to the HTTP request
+ // body, or `*` for mapping all request fields not captured by the path
+ // pattern to the HTTP body, or omitted for not having any HTTP request body.
+ //
+ // NOTE: the referred field must be present at the top-level of the request
+ // message type.
+ string body = 7;
+
+ // Optional. The name of the response field whose value is mapped to the HTTP
+ // response body. When omitted, the entire response message will be used
+ // as the HTTP response body.
+ //
+ // NOTE: The referred field must be present at the top-level of the response
+ // message type.
+ string response_body = 12;
+
+ // Additional HTTP bindings for the selector. Nested bindings must
+ // not contain an `additional_bindings` field themselves (that is,
+ // the nesting may only be one level deep).
+ repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+ // The name of this custom HTTP verb.
+ string kind = 1;
+
+ // The path matched by this custom verb.
+ string path = 2;
+}
+
diff --git a/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/ConfigureServices.cs b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/ConfigureServices.cs
new file mode 100644
index 0000000..e907775
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/ConfigureServices.cs
@@ -0,0 +1,13 @@
+using FluentValidation;
+using System.Reflection;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class ConfigureServices
+{
+ public static IServiceCollection AddDiscountShopProtobufServices(this IServiceCollection services)
+ {
+ services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
+ return services;
+ }
+}
diff --git a/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/FrontOffice.BFF.DiscountShop.Protobuf.csproj b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/FrontOffice.BFF.DiscountShop.Protobuf.csproj
new file mode 100644
index 0000000..3390c03
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/FrontOffice.BFF.DiscountShop.Protobuf.csproj
@@ -0,0 +1,25 @@
+
+
+ net9.0
+ enable
+ enable
+ 0.0.1
+ Foursat.FrontOffice.BFF.DiscountShop.Protobuf
+ False
+ False
+ None
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/discountshop.proto b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/discountshop.proto
new file mode 100644
index 0000000..70392a2
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/discountshop.proto
@@ -0,0 +1,190 @@
+syntax = "proto3";
+
+package discountshop;
+
+import "google/protobuf/empty.proto";
+import "google/protobuf/wrappers.proto";
+import "google/protobuf/timestamp.proto";
+import "google/api/annotations.proto";
+
+option csharp_namespace = "FrontOffice.BFF.DiscountShop.Protobuf.Protos.DiscountShop";
+
+// سرویس فروشگاه تخفیفی - برای کاربران عضو باشگاه
+service DiscountShopContract
+{
+ // محصولات
+ rpc GetDiscountProducts(GetDiscountProductsRequest) returns (GetDiscountProductsResponse){
+ option (google.api.http) = {
+ get: "/DiscountShop/Products"
+ };
+ };
+
+ // دستهبندیها
+ rpc GetDiscountCategories(GetDiscountCategoriesRequest) returns (GetDiscountCategoriesResponse){
+ option (google.api.http) = {
+ get: "/DiscountShop/Categories"
+ };
+ };
+
+ // سبد خرید
+ rpc GetMyDiscountCart(google.protobuf.Empty) returns (GetMyDiscountCartResponse){
+ option (google.api.http) = {
+ get: "/DiscountShop/Cart"
+ };
+ };
+
+ rpc AddToDiscountCart(AddToDiscountCartRequest) returns (google.protobuf.Empty){
+ option (google.api.http) = {
+ post: "/DiscountShop/Cart/Add"
+ body: "*"
+ };
+ };
+
+ rpc RemoveFromDiscountCart(RemoveFromDiscountCartRequest) returns (google.protobuf.Empty){
+ option (google.api.http) = {
+ post: "/DiscountShop/Cart/Remove"
+ body: "*"
+ };
+ };
+
+ // سفارشات
+ rpc GetMyDiscountOrders(GetMyDiscountOrdersRequest) returns (GetMyDiscountOrdersResponse){
+ option (google.api.http) = {
+ get: "/DiscountShop/Orders"
+ };
+ };
+
+ rpc PlaceDiscountOrder(PlaceDiscountOrderRequest) returns (PlaceDiscountOrderResponse){
+ option (google.api.http) = {
+ post: "/DiscountShop/Orders/Place"
+ body: "*"
+ };
+ };
+}
+
+// ============ MetaData ============
+message MetaData
+{
+ int64 total_count = 1;
+}
+
+// ============ Products ============
+message GetDiscountProductsRequest
+{
+ int32 page_number = 1;
+ int32 page_size = 2;
+ google.protobuf.StringValue search_term = 3;
+ google.protobuf.Int64Value category_id = 4;
+}
+
+message GetDiscountProductsResponse
+{
+ MetaData meta_data = 1;
+ repeated DiscountProductModel products = 2;
+}
+
+message DiscountProductModel
+{
+ int64 id = 1;
+ string title = 2;
+ string description = 3;
+ string image_path = 4;
+ int64 original_price = 5;
+ int64 discounted_price = 6;
+ int32 discount_percent = 7;
+ int32 remaining_count = 8;
+ bool is_available = 9;
+}
+
+// ============ Categories ============
+message GetDiscountCategoriesRequest
+{
+ google.protobuf.Int64Value parent_category_id = 1;
+ bool only_active = 2;
+}
+
+message GetDiscountCategoriesResponse
+{
+ repeated DiscountCategoryModel categories = 1;
+}
+
+message DiscountCategoryModel
+{
+ int64 id = 1;
+ string title = 2;
+ google.protobuf.Int64Value parent_id = 3;
+ string icon_path = 4;
+ int32 sort_order = 5;
+ repeated DiscountCategoryModel children = 6;
+}
+
+// ============ Cart ============
+message GetMyDiscountCartResponse
+{
+ repeated DiscountCartItemModel items = 1;
+ int64 total_price = 2;
+ int64 total_discount_amount = 3;
+}
+
+message DiscountCartItemModel
+{
+ int64 product_id = 1;
+ string product_title = 2;
+ string product_image_path = 3;
+ int64 unit_price = 4;
+ int32 count = 5;
+ int64 total_price = 6;
+}
+
+message AddToDiscountCartRequest
+{
+ int64 product_id = 1;
+ int32 count = 2;
+}
+
+message RemoveFromDiscountCartRequest
+{
+ int64 product_id = 1;
+}
+
+// ============ Orders ============
+message GetMyDiscountOrdersRequest
+{
+ int32 page_number = 1;
+ int32 page_size = 2;
+}
+
+message GetMyDiscountOrdersResponse
+{
+ MetaData meta_data = 1;
+ repeated DiscountOrderModel orders = 2;
+}
+
+message DiscountOrderModel
+{
+ int64 id = 1;
+ string order_number = 2;
+ string status = 3;
+ string status_color = 4;
+ int64 total_amount = 5;
+ int64 discount_used = 6;
+ int64 gateway_paid = 7;
+ google.protobuf.Timestamp created_at = 8;
+ string created_at_persian = 9;
+}
+
+message PlaceDiscountOrderRequest
+{
+ int64 user_address_id = 1;
+ int64 discount_balance_to_use = 2;
+ google.protobuf.StringValue notes = 3;
+}
+
+message PlaceDiscountOrderResponse
+{
+ bool success = 1;
+ string message = 2;
+ int64 order_id = 3;
+ string order_number = 4;
+ google.protobuf.StringValue payment_url = 5;
+}
diff --git a/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/google/api/annotations.proto b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/google/api/annotations.proto
new file mode 100644
index 0000000..85c361b
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/google/api/annotations.proto
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+ // See `HttpRule`.
+ HttpRule http = 72295728;
+}
diff --git a/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/google/api/http.proto b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/google/api/http.proto
new file mode 100644
index 0000000..b8426ba
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.DiscountShop.Protobuf/Protos/google/api/http.proto
@@ -0,0 +1,377 @@
+// Copyright 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+syntax = "proto3";
+
+package google.api;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+ // A list of HTTP configuration rules that apply to individual API methods.
+ //
+ // **NOTE:** All service configuration rules follow "last one wins" order.
+ repeated HttpRule rules = 1;
+
+ // When set to true, URL path parameters will be fully URI-decoded except in
+ // cases of single segment matches in reserved expansion, where "%2F" will be
+ // left encoded.
+ //
+ // The default behavior is to not decode RFC 6570 reserved characters in multi
+ // segment matches.
+ bool fully_decode_reserved_expansion = 2;
+}
+
+// # gRPC Transcoding
+//
+// gRPC Transcoding is a feature for mapping between a gRPC method and one or
+// more HTTP REST endpoints. It allows developers to build a single API service
+// that supports both gRPC APIs and REST APIs. Many systems, including [Google
+// APIs](https://github.com/googleapis/googleapis),
+// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
+// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
+// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
+// and use it for large scale production services.
+//
+// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
+// how different portions of the gRPC request message are mapped to the URL
+// path, URL query parameters, and HTTP request body. It also controls how the
+// gRPC response message is mapped to the HTTP response body. `HttpRule` is
+// typically specified as an `google.api.http` annotation on the gRPC method.
+//
+// Each mapping specifies a URL path template and an HTTP method. The path
+// template may refer to one or more fields in the gRPC request message, as long
+// as each field is a non-repeated field with a primitive (non-message) type.
+// The path template controls how fields of the request message are mapped to
+// the URL path.
+//
+// Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/{name=messages/*}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// string name = 1; // Mapped to URL path.
+// }
+// message Message {
+// string text = 1; // The resource content.
+// }
+//
+// This enables an HTTP REST to gRPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")`
+//
+// Any fields in the request message which are not bound by the path template
+// automatically become HTTP query parameters if there is no HTTP request body.
+// For example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get:"/v1/messages/{message_id}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // Mapped to URL path.
+// int64 revision = 2; // Mapped to URL query parameter `revision`.
+// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
+// }
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` |
+// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
+// "foo"))`
+//
+// Note that fields which are mapped to URL query parameters must have a
+// primitive type or a repeated primitive type or a non-repeated message type.
+// In the case of a repeated type, the parameter can be repeated in the URL
+// as `...?param=A¶m=B`. In the case of a message type, each field of the
+// message is mapped to a separate parameter, such as
+// `...?foo.a=A&foo.b=B&foo.c=C`.
+//
+// For HTTP methods that allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+// service Messaging {
+// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "message"
+// };
+// }
+// }
+// message UpdateMessageRequest {
+// string message_id = 1; // mapped to the URL
+// Message message = 2; // mapped to the body
+// }
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body. This enables the following alternative definition of
+// the update method:
+//
+// service Messaging {
+// rpc UpdateMessage(Message) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "*"
+// };
+// }
+// }
+// message Message {
+// string message_id = 1;
+// string text = 2;
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice when
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/messages/{message_id}"
+// additional_bindings {
+// get: "/v1/users/{user_id}/messages/{message_id}"
+// }
+// };
+// }
+// }
+// message GetMessageRequest {
+// string message_id = 1;
+// string user_id = 2;
+// }
+//
+// This enables the following two alternative HTTP JSON to RPC mappings:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
+// "123456")`
+//
+// ## Rules for HTTP mapping
+//
+// 1. Leaf request fields (recursive expansion nested messages in the request
+// message) are classified into three categories:
+// - Fields referred by the path template. They are passed via the URL path.
+// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP
+// request body.
+// - All other fields are passed via the URL query parameters, and the
+// parameter name is the field path in the request message. A repeated
+// field can be represented as multiple query parameters under the same
+// name.
+// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields
+// are passed via URL path and HTTP request body.
+// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all
+// fields are passed via URL path and URL query parameters.
+//
+// ### Path template syntax
+//
+// Template = "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+//
+// The syntax `*` matches a single URL path segment. The syntax `**` matches
+// zero or more URL path segments, which must be the last part of the URL path
+// except the `Verb`.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
+// contains any reserved character, such characters should be percent-encoded
+// before the matching.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path on the client
+// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
+// server side does the reverse decoding. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{var}`.
+//
+// If a variable contains multiple path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path on the
+// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
+// The server side does the reverse decoding, except "%2F" and "%2f" are left
+// unchanged. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{+var}`.
+//
+// ## Using gRPC API Service Configuration
+//
+// gRPC API Service Configuration (service config) is a configuration language
+// for configuring a gRPC service to become a user-facing product. The
+// service config is simply the YAML representation of the `google.api.Service`
+// proto message.
+//
+// As an alternative to annotating your proto file, you can configure gRPC
+// transcoding in your service config YAML files. You do this by specifying a
+// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
+// effect as the proto annotation. This can be particularly useful if you
+// have a proto that is reused in multiple services. Note that any transcoding
+// specified in the service config will override any matching transcoding
+// configuration in the proto.
+//
+// Example:
+//
+// http:
+// rules:
+// # Selects a gRPC method and applies HttpRule to it.
+// - selector: example.v1.Messaging.GetMessage
+// get: /v1/messages/{message_id}/{sub.subfield}
+//
+// ## Special notes
+//
+// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
+// proto to JSON conversion must follow the [proto3
+// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
+//
+// While the single segment variable follows the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
+// Expansion, the multi segment variable **does not** follow RFC 6570 Section
+// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
+// for multi segment variables.
+//
+// The path variables **must not** refer to any repeated or mapped field,
+// because client libraries are not capable of handling such variable expansion.
+//
+// The path variables **must not** capture the leading "/" character. The reason
+// is that the most common use case "{var}" does not capture the leading "/"
+// character. For consistency, all path variables must share the same behavior.
+//
+// Repeated message fields must not be mapped to URL query parameters, because
+// no client library can support such complicated mapping.
+//
+// If an API needs to use a JSON array for request or response body, it can map
+// the request or response body to a repeated field. However, some gRPC
+// Transcoding implementations may not support this feature.
+message HttpRule {
+ // Selects a method to which this rule applies.
+ //
+ // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+ string selector = 1;
+
+ // Determines the URL pattern is matched by this rules. This pattern can be
+ // used with any of the {get|put|post|delete|patch} methods. A custom method
+ // can be defined using the 'custom' field.
+ oneof pattern {
+ // Maps to HTTP GET. Used for listing and getting information about
+ // resources.
+ string get = 2;
+
+ // Maps to HTTP PUT. Used for replacing a resource.
+ string put = 3;
+
+ // Maps to HTTP POST. Used for creating a resource or performing an action.
+ string post = 4;
+
+ // Maps to HTTP DELETE. Used for deleting a resource.
+ string delete = 5;
+
+ // Maps to HTTP PATCH. Used for updating a resource.
+ string patch = 6;
+
+ // The custom pattern is used for specifying an HTTP method that is not
+ // included in the `pattern` field, such as HEAD, or "*" to leave the
+ // HTTP method unspecified for this rule. The wild-card rule is useful
+ // for services that provide content to Web (HTML) clients.
+ CustomHttpPattern custom = 8;
+ }
+
+ // The name of the request field whose value is mapped to the HTTP request
+ // body, or `*` for mapping all request fields not captured by the path
+ // pattern to the HTTP body, or omitted for not having any HTTP request body.
+ //
+ // NOTE: the referred field must be present at the top-level of the request
+ // message type.
+ string body = 7;
+
+ // Optional. The name of the response field whose value is mapped to the HTTP
+ // response body. When omitted, the entire response message will be used
+ // as the HTTP response body.
+ //
+ // NOTE: The referred field must be present at the top-level of the response
+ // message type.
+ string response_body = 12;
+
+ // Additional HTTP bindings for the selector. Nested bindings must
+ // not contain an `additional_bindings` field themselves (that is,
+ // the nesting may only be one level deep).
+ repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+ // The name of this custom HTTP verb.
+ string kind = 1;
+
+ // The path matched by this custom verb.
+ string path = 2;
+}
+
diff --git a/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/ConfigureServices.cs b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/ConfigureServices.cs
new file mode 100644
index 0000000..03ac326
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/ConfigureServices.cs
@@ -0,0 +1,13 @@
+using FluentValidation;
+using System.Reflection;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class ConfigureServices
+{
+ public static IServiceCollection AddNetworkMembershipProtobufServices(this IServiceCollection services)
+ {
+ services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
+ return services;
+ }
+}
diff --git a/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/FrontOffice.BFF.NetworkMembership.Protobuf.csproj b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/FrontOffice.BFF.NetworkMembership.Protobuf.csproj
new file mode 100644
index 0000000..b0ba280
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/FrontOffice.BFF.NetworkMembership.Protobuf.csproj
@@ -0,0 +1,25 @@
+
+
+ net9.0
+ enable
+ enable
+ 0.0.1
+ Foursat.FrontOffice.BFF.NetworkMembership.Protobuf
+ False
+ False
+ None
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/google/api/annotations.proto b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/google/api/annotations.proto
new file mode 100644
index 0000000..85c361b
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/google/api/annotations.proto
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+ // See `HttpRule`.
+ HttpRule http = 72295728;
+}
diff --git a/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/google/api/http.proto b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/google/api/http.proto
new file mode 100644
index 0000000..b8426ba
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/google/api/http.proto
@@ -0,0 +1,377 @@
+// Copyright 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+syntax = "proto3";
+
+package google.api;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+ // A list of HTTP configuration rules that apply to individual API methods.
+ //
+ // **NOTE:** All service configuration rules follow "last one wins" order.
+ repeated HttpRule rules = 1;
+
+ // When set to true, URL path parameters will be fully URI-decoded except in
+ // cases of single segment matches in reserved expansion, where "%2F" will be
+ // left encoded.
+ //
+ // The default behavior is to not decode RFC 6570 reserved characters in multi
+ // segment matches.
+ bool fully_decode_reserved_expansion = 2;
+}
+
+// # gRPC Transcoding
+//
+// gRPC Transcoding is a feature for mapping between a gRPC method and one or
+// more HTTP REST endpoints. It allows developers to build a single API service
+// that supports both gRPC APIs and REST APIs. Many systems, including [Google
+// APIs](https://github.com/googleapis/googleapis),
+// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
+// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
+// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
+// and use it for large scale production services.
+//
+// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
+// how different portions of the gRPC request message are mapped to the URL
+// path, URL query parameters, and HTTP request body. It also controls how the
+// gRPC response message is mapped to the HTTP response body. `HttpRule` is
+// typically specified as an `google.api.http` annotation on the gRPC method.
+//
+// Each mapping specifies a URL path template and an HTTP method. The path
+// template may refer to one or more fields in the gRPC request message, as long
+// as each field is a non-repeated field with a primitive (non-message) type.
+// The path template controls how fields of the request message are mapped to
+// the URL path.
+//
+// Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/{name=messages/*}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// string name = 1; // Mapped to URL path.
+// }
+// message Message {
+// string text = 1; // The resource content.
+// }
+//
+// This enables an HTTP REST to gRPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")`
+//
+// Any fields in the request message which are not bound by the path template
+// automatically become HTTP query parameters if there is no HTTP request body.
+// For example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get:"/v1/messages/{message_id}"
+// };
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // Mapped to URL path.
+// int64 revision = 2; // Mapped to URL query parameter `revision`.
+// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
+// }
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` |
+// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
+// "foo"))`
+//
+// Note that fields which are mapped to URL query parameters must have a
+// primitive type or a repeated primitive type or a non-repeated message type.
+// In the case of a repeated type, the parameter can be repeated in the URL
+// as `...?param=A¶m=B`. In the case of a message type, each field of the
+// message is mapped to a separate parameter, such as
+// `...?foo.a=A&foo.b=B&foo.c=C`.
+//
+// For HTTP methods that allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+// service Messaging {
+// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "message"
+// };
+// }
+// }
+// message UpdateMessageRequest {
+// string message_id = 1; // mapped to the URL
+// Message message = 2; // mapped to the body
+// }
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body. This enables the following alternative definition of
+// the update method:
+//
+// service Messaging {
+// rpc UpdateMessage(Message) returns (Message) {
+// option (google.api.http) = {
+// patch: "/v1/messages/{message_id}"
+// body: "*"
+// };
+// }
+// }
+// message Message {
+// string message_id = 1;
+// string text = 2;
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice when
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/messages/{message_id}"
+// additional_bindings {
+// get: "/v1/users/{user_id}/messages/{message_id}"
+// }
+// };
+// }
+// }
+// message GetMessageRequest {
+// string message_id = 1;
+// string user_id = 2;
+// }
+//
+// This enables the following two alternative HTTP JSON to RPC mappings:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
+// "123456")`
+//
+// ## Rules for HTTP mapping
+//
+// 1. Leaf request fields (recursive expansion nested messages in the request
+// message) are classified into three categories:
+// - Fields referred by the path template. They are passed via the URL path.
+// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP
+// request body.
+// - All other fields are passed via the URL query parameters, and the
+// parameter name is the field path in the request message. A repeated
+// field can be represented as multiple query parameters under the same
+// name.
+// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields
+// are passed via URL path and HTTP request body.
+// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all
+// fields are passed via URL path and URL query parameters.
+//
+// ### Path template syntax
+//
+// Template = "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+//
+// The syntax `*` matches a single URL path segment. The syntax `**` matches
+// zero or more URL path segments, which must be the last part of the URL path
+// except the `Verb`.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
+// contains any reserved character, such characters should be percent-encoded
+// before the matching.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path on the client
+// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
+// server side does the reverse decoding. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{var}`.
+//
+// If a variable contains multiple path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path on the
+// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
+// The server side does the reverse decoding, except "%2F" and "%2f" are left
+// unchanged. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{+var}`.
+//
+// ## Using gRPC API Service Configuration
+//
+// gRPC API Service Configuration (service config) is a configuration language
+// for configuring a gRPC service to become a user-facing product. The
+// service config is simply the YAML representation of the `google.api.Service`
+// proto message.
+//
+// As an alternative to annotating your proto file, you can configure gRPC
+// transcoding in your service config YAML files. You do this by specifying a
+// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
+// effect as the proto annotation. This can be particularly useful if you
+// have a proto that is reused in multiple services. Note that any transcoding
+// specified in the service config will override any matching transcoding
+// configuration in the proto.
+//
+// Example:
+//
+// http:
+// rules:
+// # Selects a gRPC method and applies HttpRule to it.
+// - selector: example.v1.Messaging.GetMessage
+// get: /v1/messages/{message_id}/{sub.subfield}
+//
+// ## Special notes
+//
+// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
+// proto to JSON conversion must follow the [proto3
+// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
+//
+// While the single segment variable follows the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
+// Expansion, the multi segment variable **does not** follow RFC 6570 Section
+// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
+// for multi segment variables.
+//
+// The path variables **must not** refer to any repeated or mapped field,
+// because client libraries are not capable of handling such variable expansion.
+//
+// The path variables **must not** capture the leading "/" character. The reason
+// is that the most common use case "{var}" does not capture the leading "/"
+// character. For consistency, all path variables must share the same behavior.
+//
+// Repeated message fields must not be mapped to URL query parameters, because
+// no client library can support such complicated mapping.
+//
+// If an API needs to use a JSON array for request or response body, it can map
+// the request or response body to a repeated field. However, some gRPC
+// Transcoding implementations may not support this feature.
+message HttpRule {
+ // Selects a method to which this rule applies.
+ //
+ // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+ string selector = 1;
+
+ // Determines the URL pattern is matched by this rules. This pattern can be
+ // used with any of the {get|put|post|delete|patch} methods. A custom method
+ // can be defined using the 'custom' field.
+ oneof pattern {
+ // Maps to HTTP GET. Used for listing and getting information about
+ // resources.
+ string get = 2;
+
+ // Maps to HTTP PUT. Used for replacing a resource.
+ string put = 3;
+
+ // Maps to HTTP POST. Used for creating a resource or performing an action.
+ string post = 4;
+
+ // Maps to HTTP DELETE. Used for deleting a resource.
+ string delete = 5;
+
+ // Maps to HTTP PATCH. Used for updating a resource.
+ string patch = 6;
+
+ // The custom pattern is used for specifying an HTTP method that is not
+ // included in the `pattern` field, such as HEAD, or "*" to leave the
+ // HTTP method unspecified for this rule. The wild-card rule is useful
+ // for services that provide content to Web (HTML) clients.
+ CustomHttpPattern custom = 8;
+ }
+
+ // The name of the request field whose value is mapped to the HTTP request
+ // body, or `*` for mapping all request fields not captured by the path
+ // pattern to the HTTP body, or omitted for not having any HTTP request body.
+ //
+ // NOTE: the referred field must be present at the top-level of the request
+ // message type.
+ string body = 7;
+
+ // Optional. The name of the response field whose value is mapped to the HTTP
+ // response body. When omitted, the entire response message will be used
+ // as the HTTP response body.
+ //
+ // NOTE: The referred field must be present at the top-level of the response
+ // message type.
+ string response_body = 12;
+
+ // Additional HTTP bindings for the selector. Nested bindings must
+ // not contain an `additional_bindings` field themselves (that is,
+ // the nesting may only be one level deep).
+ repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+ // The name of this custom HTTP verb.
+ string kind = 1;
+
+ // The path matched by this custom verb.
+ string path = 2;
+}
+
diff --git a/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto
new file mode 100644
index 0000000..f2edf8f
--- /dev/null
+++ b/src/Protobufs/FrontOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto
@@ -0,0 +1,101 @@
+syntax = "proto3";
+
+package networkmembership;
+
+import "google/protobuf/empty.proto";
+import "google/protobuf/wrappers.proto";
+import "google/protobuf/timestamp.proto";
+import "google/api/annotations.proto";
+
+option csharp_namespace = "FrontOffice.BFF.NetworkMembership.Protobuf.Protos.NetworkMembership";
+
+// سرویس عضویت شبکهای - برای کاربران FrontOffice
+service NetworkMembershipContract
+{
+ // دریافت درخت شبکه کاربر جاری
+ rpc GetMyNetworkTree(GetMyNetworkTreeRequest) returns (GetMyNetworkTreeResponse){
+ option (google.api.http) = {
+ get: "/NetworkMembership/MyTree"
+ };
+ };
+
+ // دریافت آمار شبکه کاربر جاری
+ rpc GetMyNetworkStatistics(google.protobuf.Empty) returns (GetMyNetworkStatisticsResponse){
+ option (google.api.http) = {
+ get: "/NetworkMembership/MyStats"
+ };
+ };
+
+ // دریافت موقعیت کاربر در شبکه
+ rpc GetMyNetworkPosition(google.protobuf.Empty) returns (GetMyNetworkPositionResponse){
+ option (google.api.http) = {
+ get: "/NetworkMembership/MyPosition"
+ };
+ };
+}
+
+// ============ GetMyNetworkTree ============
+message GetMyNetworkTreeRequest
+{
+ int32 max_depth = 1; // حداکثر عمق درخت (پیشفرض: 3)
+}
+
+message GetMyNetworkTreeResponse
+{
+ NetworkNodeModel root_node = 1;
+ int32 total_members = 2;
+ int32 current_depth = 3;
+}
+
+message NetworkNodeModel
+{
+ int64 user_id = 1;
+ string full_name = 2;
+ string mobile = 3;
+ google.protobuf.StringValue avatar = 4;
+ string position = 5; // Root/Left/Right
+ NetworkNodeModel left_child = 6;
+ NetworkNodeModel right_child = 7;
+ int32 level = 8;
+ bool has_children = 9;
+}
+
+// ============ GetMyNetworkStatistics ============
+message GetMyNetworkStatisticsResponse
+{
+ int32 total_members = 1;
+ int32 active_members = 2;
+ int32 left_leg_count = 3;
+ int32 right_leg_count = 4;
+ double left_percentage = 5;
+ double right_percentage = 6;
+ double average_depth = 7;
+ int32 max_depth = 8;
+ string weaker_leg = 9;
+ int32 my_network_level = 10;
+ string my_network_leg = 11;
+ string my_referral_code = 12;
+ LastMemberModel last_member = 13;
+}
+
+message LastMemberModel
+{
+ int64 user_id = 1;
+ string full_name = 2;
+ string position = 3;
+ int32 total_children = 4;
+}
+
+// ============ GetMyNetworkPosition ============
+message GetMyNetworkPositionResponse
+{
+ int64 user_id = 1;
+ int64 parent_id = 2;
+ string parent_name = 3;
+ string position = 4; // Left/Right
+ int32 level = 5;
+ string referral_code = 6;
+ google.protobuf.Timestamp joined_at = 7;
+ bool has_left_child = 8;
+ bool has_right_child = 9;
+}