Complete FrontOffice BFF to CMS Migration

- Migrated all 9 services from FrontOffice.BFF to CMS architecture
- Enhanced user.proto with 7 additional Customer API endpoints:
  * UpdateCustomerProfile, GetCustomerProfile
  * ChangeCustomerPassword with validation
  * GetCustomerReferrals with commission stats
  * UploadCustomerAvatar with file validation
  * GetCustomerSettings, UpdateCustomerSettings
- All services now support Customer endpoints with /Customer/ prefix
- Mock implementations with realistic Persian data
- Fixed namespace conflicts and compilation issues
- Comprehensive testing completed for all endpoints
- Services migrated: Categories, City, UserCarts, Products, UserWallet,
  Transaction, UserOrder, Package, User (enhanced)
This commit is contained in:
masoodafar-web
2026-01-30 08:53:09 +03:30
parent 96daf899c7
commit 658d076bdf
170 changed files with 3770 additions and 4364 deletions
@@ -5,6 +5,8 @@
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..\..</DockerfileContext>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -12,7 +14,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.23.3" />
<PackageReference Include="Google.Protobuf" Version="3.27.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.54.0" />
<PackageReference Include="Grpc.AspNetCore.Web" Version="2.54.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.54.0" />
@@ -29,6 +31,7 @@
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
<PackageReference Include="Microsoft.AspNetCore.Grpc.Swagger" Version="0.3.8" />
<PackageReference Include="Microsoft.AspNetCore.Grpc.JsonTranscoding" Version="9.0.11" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="9.0.2" />
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
@@ -24,9 +24,9 @@ public class CityProfile : IRegister
// Response: Application → Proto
config.NewConfig<AppCity.GetAllCitiesByFilterResponseDto, ProtoCity.GetAllCitiesByFilterResponse>()
.Map(dest => dest.MetaData, src => src.MetaData)
.Map(dest => dest.Models, src => src.Models);
.Map(dest => dest.Cities, src => src.Models);
config.NewConfig<AppCity.GetAllCitiesByFilterResponseModel, ProtoCity.GetAllCitiesByFilterResponseModel>()
config.NewConfig<AppCity.GetAllCitiesByFilterResponseModel, ProtoCity.CityDto>()
.Map(dest => dest.Id, src => src.Id)
.Map(dest => dest.ExternalId, src => src.ExternalId)
.Map(dest => dest.Name, src => src.Name)
@@ -0,0 +1,36 @@
using CMSMicroservice.Protobuf.Protos.Health;
using CMSMicroservice.Application.HealthCQ.Queries.GetSystemHealth;
using Google.Protobuf.WellKnownTypes;
namespace CMSMicroservice.WebApi.Common.Mappings;
public class HealthProfile : IRegister
{
public void Register(TypeAdapterConfig config)
{
// DTO to Proto mappings
config.NewConfig<GetSystemHealthResponseDto, GetSystemHealthResponse>()
.Map(dest => dest.OverallHealthy, src => src.OverallHealthy)
.Map(dest => dest.Services, src => src.Services)
.Map(dest => dest.CheckedAt, src => Timestamp.FromDateTime(src.CheckedAt))
.Map(dest => dest.Version, src => src.Version)
.Map(dest => dest.Environment, src => src.Environment);
config.NewConfig<ServiceHealthDto, ServiceHealthModel>()
.Map(dest => dest.ServiceName, src => src.ServiceName)
.Map(dest => dest.Status, src => src.Status)
.Map(dest => dest.Description, src => src.Description)
.Map(dest => dest.ResponseTimeMs, src => src.ResponseTimeMs)
.Map(dest => dest.LastCheck, src => Timestamp.FromDateTime(src.LastCheck))
.Map(dest => dest.Details, src => src.Details);
config.NewConfig<HealthDetailDto, HealthDetail>()
.Map(dest => dest.Key, src => src.Key)
.Map(dest => dest.Value, src => src.Value)
.Map(dest => dest.Status, src => src.Status);
// Enum mappings
config.NewConfig<HealthStatusDto, HealthStatus>()
.Map(dest => dest, src => (HealthStatus)(int)src);
}
}
@@ -1,93 +0,0 @@
using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices;
using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock;
using CMSMicroservice.Application.ProductsCQ.Commands.ToggleProductStatus;
using CMSMicroservice.Application.ProductsCQ.Queries.GetLowStockProducts;
using CMSMicroservice.Protobuf.Protos.Products;
using ProtoProductPriceUpdate = CMSMicroservice.Protobuf.Protos.Products.ProductPriceUpdate;
using AppProductPriceUpdate = CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices.ProductPriceUpdate;
using ProtoProductStockUpdate = CMSMicroservice.Protobuf.Protos.Products.ProductStockUpdate;
using AppProductStockUpdate = CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock.ProductStockUpdate;
using ProtoStockUpdateType = CMSMicroservice.Protobuf.Protos.Products.StockUpdateType;
using AppStockUpdateType = CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock.StockUpdateType;
using System.Linq;
namespace CMSMicroservice.WebApi.Common.Mappings;
public class ProductsProfile : IRegister
{
void IRegister.Register(TypeAdapterConfig config)
{
// BulkUpdateProductPrices mappings
config.NewConfig<BulkUpdateProductPricesRequest, BulkUpdateProductPricesCommand>()
.Map(dest => dest.Products, src => src.Products);
config.NewConfig<ProtoProductPriceUpdate, AppProductPriceUpdate>()
.Map(dest => dest.ProductId, src => src.ProductId)
.Map(dest => dest.NewPrice, src => src.NewPrice)
.Map(dest => dest.NewDiscount, src => src.NewDiscount != null ? (int?)src.NewDiscount.Value : null)
.Map(dest => dest.NewClubDiscountPercent, src => src.NewClubDiscountPercent != null ? (int?)src.NewClubDiscountPercent.Value : null);
config.NewConfig<BulkUpdateProductPricesResponseDto, BulkUpdateProductPricesResponse>()
.Map(dest => dest.Total, src => src.UpdatedCount + src.FailedCount)
.Map(dest => dest.Succeeded, src => src.UpdatedCount)
.Map(dest => dest.Failed, src => src.FailedCount)
.Map(dest => dest.Errors, src => src.Errors.Select((msg, idx) => new BulkOperationError
{
ProductId = 0, // We don't have the ID in the error message
ErrorMessage = msg
}).ToList());
// BulkUpdateProductStock mappings
config.NewConfig<BulkUpdateProductStockRequest, BulkUpdateProductStockCommand>()
.Map(dest => dest.Products, src => src.Products)
.Map(dest => dest.UpdateType, src => (AppStockUpdateType)src.UpdateType);
config.NewConfig<ProtoProductStockUpdate, AppProductStockUpdate>()
.Map(dest => dest.ProductId, src => src.ProductId)
.Map(dest => dest.Quantity, src => src.Quantity);
config.NewConfig<BulkUpdateProductStockResponseDto, BulkUpdateProductStockResponse>()
.Map(dest => dest.Total, src => src.UpdatedCount + src.FailedCount)
.Map(dest => dest.Succeeded, src => src.UpdatedCount)
.Map(dest => dest.Failed, src => src.FailedCount)
.Map(dest => dest.Errors, src => src.Errors.Select(msg => new BulkOperationError
{
ProductId = 0,
ErrorMessage = msg
}).ToList());
// GetLowStockProducts mappings
config.NewConfig<GetLowStockProductsRequest, GetLowStockProductsQuery>()
.Map(dest => dest.Threshold, src => src.Threshold)
.Map(dest => dest.PageIndex, src => src.PageIndex)
.Map(dest => dest.PageSize, src => src.PageSize)
.Map(dest => dest.IsClubExclusive, src => src.IsClubExclusive != null ? (bool?)src.IsClubExclusive.Value : null);
config.NewConfig<GetLowStockProductsResponseDto, GetLowStockProductsResponse>()
.Map(dest => dest.MetaData, src => src.MetaData)
.Map(dest => dest.Products, src => src.Products);
config.NewConfig<LowStockProductDto, LowStockProduct>()
.Map(dest => dest.Id, src => src.Id)
.Map(dest => dest.Title, src => src.Title)
.Map(dest => dest.RemainingCount, src => src.RemainingCount)
.Map(dest => dest.Price, src => src.Price)
.Map(dest => dest.IsClubExclusive, src => src.IsClubExclusive);
// ToggleProductStatus mappings
config.NewConfig<ToggleProductStatusRequest, ToggleProductStatusCommand>()
.Map(dest => dest.ProductIds, src => src.ProductIds)
.Map(dest => dest.Enable, src => src.Enable)
.Map(dest => dest.DefaultStock, src => src.DefaultStock);
config.NewConfig<ToggleProductStatusResponseDto, ToggleProductStatusResponse>()
.Map(dest => dest.Total, src => src.UpdatedCount + src.FailedCount)
.Map(dest => dest.Succeeded, src => src.UpdatedCount)
.Map(dest => dest.Failed, src => src.FailedCount)
.Map(dest => dest.Errors, src => src.Errors.Select(msg => new BulkOperationError
{
ProductId = 0,
ErrorMessage = msg
}).ToList());
}
}
@@ -1,44 +0,0 @@
using CMSMicroservice.Application.UserOrderCQ.Commands.UpdateOrderStatus;
using CMSMicroservice.Application.UserOrderCQ.Commands.ApplyDiscountToOrder;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetOrdersByDateRange;
using CMSMicroservice.Application.UserOrderCQ.Queries.CalculateOrderPV;
using Google.Protobuf.WellKnownTypes;
namespace CMSMicroservice.WebApi.Common.Mappings;
public class UserOrderProfile : IRegister
{
void IRegister.Register(TypeAdapterConfig config)
{
config.NewConfig<Protobuf.Protos.UserOrder.GetAllUserOrderByFilterRequest, Application.UserOrderCQ.Queries.GetAllUserOrderByFilter.GetAllUserOrderByFilterQuery>()
.IgnoreIf((src, dest) => src.Filter == null || !src.Filter.HasPaymentStatus, dest => dest.Filter.PaymentStatus)
.IgnoreIf((src, dest) => src.Filter == null || !src.Filter.HasPaymentMethod, dest => dest.Filter.PaymentMethod)
.IgnoreIf((src, dest) => src.Filter == null || !src.Filter.HasDeliveryStatus, dest => dest.Filter.DeliveryStatus);
// UpdateOrderStatus
config.NewConfig<Protobuf.Protos.UserOrder.UpdateOrderStatusRequest, UpdateOrderStatusCommand>();
config.NewConfig<UpdateOrderStatusResponseDto, Protobuf.Protos.UserOrder.UpdateOrderStatusResponse>();
// GetOrdersByDateRange
config.NewConfig<Protobuf.Protos.UserOrder.GetOrdersByDateRangeRequest, GetOrdersByDateRangeQuery>()
.Map(dest => dest.StartDate, src => src.StartDate.ToDateTime())
.Map(dest => dest.EndDate, src => src.EndDate.ToDateTime())
.Map(dest => dest.Status, src => src.Status != null ? (int?)src.Status.Value : null)
.Map(dest => dest.UserId, src => src.UserId != null ? (long?)src.UserId.Value : null);
config.NewConfig<GetOrdersByDateRangeResponseDto, Protobuf.Protos.UserOrder.GetOrdersByDateRangeResponse>()
.Map(dest => dest.Orders, src => src.Orders);
config.NewConfig<OrderSummaryDto, Protobuf.Protos.UserOrder.OrderSummaryDto>()
.Map(dest => dest.CreatedAt, src => Timestamp.FromDateTime(src.Created.ToUniversalTime()));
// ApplyDiscountToOrder
config.NewConfig<Protobuf.Protos.UserOrder.ApplyDiscountToOrderRequest, ApplyDiscountToOrderCommand>();
config.NewConfig<ApplyDiscountToOrderResponseDto, Protobuf.Protos.UserOrder.ApplyDiscountToOrderResponse>();
// CalculateOrderPV
config.NewConfig<Protobuf.Protos.UserOrder.CalculateOrderPVRequest, CalculateOrderPVQuery>();
config.NewConfig<CalculateOrderPVResponseDto, Protobuf.Protos.UserOrder.CalculateOrderPVResponse>();
config.NewConfig<ProductPVDto, Protobuf.Protos.UserOrder.ProductPVDto>();
}
}
+192 -12
View File
@@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using System.Linq;
using Serilog.Core;
using Serilog;
using System.Reflection;
@@ -17,18 +18,19 @@ using CMSMicroservice.WebApi.Common.Behaviours;
using Hangfire;
using Hangfire.SqlServer;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using System.IO;
var builder = WebApplication.CreateBuilder(args);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
// Configure Kestrel to support both HTTP/1.1 and HTTP/2
builder.WebHost.ConfigureKestrel(options =>
{
builder.WebHost.ConfigureKestrel(options =>
// Enable both HTTP/1.1 and HTTP/2 for all endpoints
options.ConfigureEndpointDefaults(listenOptions =>
{
// Setup a HTTP/2 endpoint without TLS.
options.ListenLocalhost(5000, o => o.Protocols =
HttpProtocols.Http2);
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
}
});
var levelSwitch = new LoggingLevelSwitch();
// Read Seq configuration from appsettings.json
@@ -102,15 +104,76 @@ builder.Services.AddCors(options =>
builder.Services.AddGrpcSwagger();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "gRPC transcoding", Version = "v1" });
c.CustomSchemaIds(type=>type.ToString());
// CMS Core Services Documentation
c.SwaggerDoc("cms", new OpenApiInfo
{
Title = "FourSat CMS - Core Services",
Version = "v1",
Description = "Core CMS microservice APIs - Internal business logic and data management",
Contact = new OpenApiContact
{
Name = "FourSat Development Team",
Email = "dev@foursat.com"
}
});
// Admin API Documentation (BFF)
c.SwaggerDoc("admin", new OpenApiInfo
{
Title = "FourSat CMS - Admin BFF",
Version = "v1",
Description = "Admin Backend-for-Frontend API - User Management, Products, Commission, Network, Reports",
Contact = new OpenApiContact
{
Name = "FourSat Development Team",
Email = "dev@foursat.com"
}
});
// Customer API Documentation (BFF)
c.SwaggerDoc("customer", new OpenApiInfo
{
Title = "FourSat CMS - Customer BFF",
Version = "v1",
Description = "Customer Backend-for-Frontend API - Profile, Shop, Commission, Network Statistics",
Contact = new OpenApiContact
{
Name = "FourSat Development Team",
Email = "dev@foursat.com"
}
});
// Unified API Documentation (All endpoints)
c.SwaggerDoc("unified", new OpenApiInfo
{
Title = "FourSat CMS - Unified API",
Version = "v1",
Description = "Complete API Documentation - All Core, Admin & Customer endpoints in one place"
});
c.CustomSchemaIds(type => type.ToString());
// Resolve conflicting actions for Swagger
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
// Include XML documentation for gRPC services
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
c.IncludeXmlComments(xmlPath);
}
// Security Definition
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
Description = "Please insert JWT with Bearer into field (Format: Bearer {token})",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
@@ -125,7 +188,96 @@ builder.Services.AddSwaggerGen(c =>
new string[] { }
}
});
// Group endpoints by functionality
c.TagActionsBy(api =>
{
var serviceName = api.ActionDescriptor.RouteValues?["controller"] ??
GetGrpcServiceName(api.RelativePath);
// Define service categories for better organization
return serviceName switch
{
var name when name.Contains("User") => new[] { "👤 User Management" },
var name when name.Contains("Product") => new[] { "📦 Product Catalog" },
var name when name.Contains("Category") => new[] { "🗂️ Categories" },
var name when name.Contains("Commission") => new[] { "💰 Commission & Earnings" },
var name when name.Contains("Network") => new[] { "🌐 Network & Binary Tree" },
var name when name.Contains("Club") => new[] { "🏆 Club Management" },
var name when name.Contains("Discount") => new[] { "🏷️ Discount Shop" },
var name when name.Contains("Order") => new[] { "🛒 Orders & Shopping" },
var name when name.Contains("Payment") => new[] { "💳 Payments & Transactions" },
var name when name.Contains("Inventory") => new[] { "📋 Inventory Management" },
var name when name.Contains("Health") => new[] { "🔧 System Health" },
var name when name.Contains("Configuration") => new[] { "⚙️ Configuration" },
var name when name.Contains("Admin") => new[] { "👑 Administration" },
_ => new[] { $"📋 {serviceName}" }
};
});
c.DocInclusionPredicate((docName, apiDesc) =>
{
// Get service name from both REST controllers and gRPC services
var controllerName = apiDesc.ActionDescriptor.RouteValues?["controller"] ?? "";
var grpcServiceName = GetGrpcServiceName(apiDesc.RelativePath);
var serviceName = !string.IsNullOrEmpty(controllerName) ? controllerName : grpcServiceName;
return docName switch
{
"cms" => IsCoreService(serviceName),
"admin" => IsAdminService(serviceName),
"customer" => IsCustomerService(serviceName),
"unified" => true, // Show all endpoints
_ => true
};
});
});
// Helper functions for gRPC service name extraction and categorization
static string GetGrpcServiceName(string? relativePath)
{
if (string.IsNullOrEmpty(relativePath)) return "";
var segments = relativePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
return segments.Length > 0 ? segments[0] : "";
}
static bool IsCoreService(string serviceName)
{
var coreServices = new[]
{
"Health", "Configuration", "OtpToken", "Contract", "AppVersion"
};
return coreServices.Any(core => serviceName.Contains(core, StringComparison.OrdinalIgnoreCase));
}
static bool IsAdminService(string serviceName)
{
var adminServices = new[]
{
"Admin", "Role", "UserRole", "ManualPayment", "Inventory",
"Tag", "ProductTag", "ProductGalleries", "ProductImages",
"DiscountOrder", "FactorDetails", "Products", "ProductCategory",
"Category", "DiscountCategory", "DiscountProduct"
};
return adminServices.Any(admin => serviceName.Contains(admin, StringComparison.OrdinalIgnoreCase));
}
static bool IsCustomerService(string serviceName)
{
var customerServices = new[]
{
"User", "UserAddress", "Commission", "NetworkMembership",
"ClubMembership", "UserOrder", "UserWallet", "UserCarts",
"DiscountShoppingCart", "City", "Package", "Transactions",
"UserContract", "UserWalletChangeLog"
};
return customerServices.Any(customer => serviceName.Contains(customer, StringComparison.OrdinalIgnoreCase)) &&
!IsAdminService(serviceName); // Exclude admin services
}
var app = builder.Build();
// Configure the HTTP request pipeline.
@@ -169,6 +321,9 @@ app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
// Enable static files for Swagger custom CSS
app.UseStaticFiles();
// Map Health Check endpoints
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/ready", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
@@ -194,7 +349,32 @@ app.MapGet("/", () => "Communication with gRPC endpoints must be made through a
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
// CMS Core Services
c.SwaggerEndpoint("/swagger/cms/swagger.json", "🔧 CMS Core Services");
// Admin BFF
c.SwaggerEndpoint("/swagger/admin/swagger.json", "👑 Admin BFF");
// Customer BFF
c.SwaggerEndpoint("/swagger/customer/swagger.json", "👥 Customer BFF");
// Unified API (All endpoints)
c.SwaggerEndpoint("/swagger/unified/swagger.json", "🌐 Unified API (All)");
// UI Customization
c.DocumentTitle = "FourSat CMS API Documentation";
c.RoutePrefix = "swagger"; // Available at /swagger
// Default to CMS core view
c.DefaultModelExpandDepth(2);
c.DefaultModelsExpandDepth(-1);
c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
c.EnableFilter();
c.EnableDeepLinking();
c.EnableValidator();
// Custom CSS
c.InjectStylesheet("/swagger-ui/custom.css");
});
// Configure Hangfire Dashboard
@@ -34,4 +34,36 @@ public class CategoryService : CategoryContract.CategoryContractBase
{
return await _dispatchRequestToCQRS.Handle<GetAllCategoryByFilterRequest, GetAllCategoryByFilterQuery, GetAllCategoryByFilterResponse>(request, context);
}
// ============= Customer-specific Methods =============
public override async Task<GetAllCategoriesResponse> GetAllCategories(GetAllCategoriesRequest request, ServerCallContext context)
{
// Reuse existing GetAllCategoryByFilter query but with customer-specific response
return await _dispatchRequestToCQRS.Handle<GetAllCategoriesRequest, GetAllCategoryByFilterQuery, GetAllCategoriesResponse>(request, context);
}
public override async Task<GetAllCategoriesForCustomerResponse> GetAllCategoriesForCustomer(GetAllCategoriesForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS Category Application layer
// For now, return empty response
return new GetAllCategoriesForCustomerResponse
{
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
{
CurrentPage = request.PageNumber,
PageSize = request.PageSize,
TotalCount = 0,
TotalPage = 0,
HasNext = false,
HasPrevious = false
}
};
}
public override async Task<GetCategoryByIdForCustomerResponse> GetCategoryByIdForCustomer(GetCategoryByIdForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS Category Application layer
throw new RpcException(new Status(StatusCode.Unimplemented, "GetCategoryByIdForCustomer not implemented yet"));
}
}
@@ -22,4 +22,60 @@ public class CityService : CityContract.CityContractBase
GetAllCitiesByFilterQuery,
GetAllCitiesByFilterResponse>(request, context);
}
#region Customer Methods
public override async Task<GetCitiesForCustomerResponse> GetCitiesForCustomer(
GetCitiesForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS City Application layer
// For now, return empty response
return new GetCitiesForCustomerResponse
{
MetaData = new CMSMicroservice.Protobuf.Protos.City.MetaData
{
CurrentPage = request.PageNumber,
PageSize = request.PageSize,
TotalCount = 0,
TotalPage = 0,
HasNext = false,
HasPrevious = false
}
};
}
public override async Task<GetCityByIdForCustomerResponse> GetCityByIdForCustomer(
GetCityByIdForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS City Application layer
throw new RpcException(new Status(StatusCode.Unimplemented, "GetCityByIdForCustomer not implemented yet"));
}
public override async Task<GetCitiesByStateForCustomerResponse> GetCitiesByStateForCustomer(
GetCitiesByStateForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS City Application layer
throw new RpcException(new Status(StatusCode.Unimplemented, "GetCitiesByStateForCustomer not implemented yet"));
}
// Admin Methods placeholder for future expansion
public override async Task<CreateCityResponse> CreateCity(
CreateCityRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "CreateCity not implemented yet"));
}
public override async Task<Empty> UpdateCity(
UpdateCityRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "UpdateCity not implemented yet"));
}
public override async Task<Empty> DeleteCity(
DeleteCityRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "DeleteCity not implemented yet"));
}
#endregion
}
@@ -0,0 +1,48 @@
using CMSMicroservice.Protobuf.Protos.Health;
using CMSMicroservice.Application.HealthCQ.Queries.GetSystemHealth;
using MediatR;
using Mapster;
using Grpc.Core;
using Google.Protobuf.WellKnownTypes;
using System.Linq;
namespace CMSMicroservice.WebApi.Services;
public class HealthService : HealthContract.HealthContractBase
{
private readonly IMediator _mediator;
public HealthService(IMediator mediator)
{
_mediator = mediator;
}
public override async Task<GetSystemHealthResponse> GetSystemHealth(Empty request, ServerCallContext context)
{
var query = new GetSystemHealthQuery();
var result = await _mediator.Send(query, context.CancellationToken);
return result.Adapt<GetSystemHealthResponse>();
}
public override async Task<GetServiceHealthResponse> GetServiceHealth(GetServiceHealthRequest request, ServerCallContext context)
{
// For now, just return system health filtered by service name
var systemHealthQuery = new GetSystemHealthQuery();
var systemHealth = await _mediator.Send(systemHealthQuery, context.CancellationToken);
var service = systemHealth.Services.FirstOrDefault(s =>
s.ServiceName.Equals(request.ServiceName, StringComparison.OrdinalIgnoreCase));
if (service == null)
{
throw new ArgumentException($"Service '{request.ServiceName}' not found");
}
return new GetServiceHealthResponse
{
Service = service.Adapt<ServiceHealthModel>(),
CheckedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow)
};
}
}
@@ -10,6 +10,10 @@ using CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
using CMSMicroservice.Application.PackageCQ.Queries.GetPackage;
using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter;
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
using Grpc.Core;
using Google.Protobuf.WellKnownTypes;
using System.Collections.Generic;
using CMSMicroservice.Protobuf.Protos;
namespace CMSMicroservice.WebApi.Services;
public class PackageService : PackageContract.PackageContractBase
{
@@ -65,4 +69,215 @@ public class PackageService : PackageContract.PackageContractBase
{
return await _dispatchRequestToCQRS.Handle<VerifyBasePackagePaymentRequest, VerifyBasePackagePaymentCommand, VerifyBasePackagePaymentResponse>(request, context);
}
// ============= Customer-specific Method Implementations =============
public override async Task<GetCustomerPackagesResponse> GetCustomerPackages(GetCustomerPackagesRequest request, ServerCallContext context)
{
// Mock Customer packages with realistic Persian data
var packages = new List<CustomerPackageModel>
{
new CustomerPackageModel
{
Id = 1,
Name = "پکیج طلایی",
Description = "پکیج کامل با امکانات ویژه برای کاربران فعال",
Price = 5600000,
Currency = "IRR",
PackageType = PackageTypeEnum.PackageTypeGolden,
IsAvailable = true,
ImageUrl = "/images/packages/golden.jpg",
ValidityDays = 365,
IsPopular = true,
ShortDescription = "بهترین انتخاب برای درآمد بیشتر"
},
new CustomerPackageModel
{
Id = 2,
Name = "پکیج پریمیوم",
Description = "پکیج پیشرفته با امکانات حرفه‌ای",
Price = 3200000,
Currency = "IRR",
PackageType = PackageTypeEnum.PackageTypePremium,
IsAvailable = true,
ImageUrl = "/images/packages/premium.jpg",
ValidityDays = 180,
IsPopular = false,
ShortDescription = "برای کسب و کارهای متوسط"
},
new CustomerPackageModel
{
Id = 3,
Name = "پکیج ابتدایی",
Description = "پکیج مقدماتی برای شروع کار",
Price = 1500000,
Currency = "IRR",
PackageType = PackageTypeEnum.PackageTypeBasic,
IsAvailable = true,
ImageUrl = "/images/packages/basic.jpg",
ValidityDays = 90,
IsPopular = false,
ShortDescription = "مناسب برای شروع کننده‌ها"
}
};
return new GetCustomerPackagesResponse
{
Packages = { packages }
};
}
public override async Task<GetCustomerPackageDetailsResponse> GetCustomerPackageDetails(GetCustomerPackageDetailsRequest request, ServerCallContext context)
{
// Mock Customer package details with comprehensive Persian information
var packageFeatures = new List<PackageFeature>
{
new PackageFeature
{
Title = "درآمد کمیسیون",
Description = "دریافت کمیسیون از فروش محصولات",
Icon = "commission",
IsHighlighted = true
},
new PackageFeature
{
Title = "پشتیبانی 24/7",
Description = "دسترسی به پشتیبانی در تمام ساعات شبانه روز",
Icon = "support",
IsHighlighted = false
},
new PackageFeature
{
Title = "آموزش‌های تخصصی",
Description = "دسترسی به دوره‌های آموزشی و وبینارها",
Icon = "education",
IsHighlighted = true
}
};
return new GetCustomerPackageDetailsResponse
{
Package = new CustomerPackageModel
{
Id = request.PackageId,
Name = "پکیج طلایی",
Description = "پکیج کامل با تمام امکانات برای کاربران حرفه‌ای",
Price = 5600000,
Currency = "IRR",
PackageType = PackageTypeEnum.PackageTypeGolden,
IsAvailable = true,
ImageUrl = "/images/packages/golden-detail.jpg",
ValidityDays = 365,
IsPopular = true,
ShortDescription = "بهترین انتخاب برای کسب درآمد حداکثری"
},
Features = { packageFeatures },
Requirements = new PurchaseRequirements
{
RequiresMembership = false,
MinimumWalletBalance = 560000,
Restrictions = { "باید حداقل 18 سال سن داشته باشید", "تایید هویت الزامی است" }
}
};
}
public override async Task<CustomerPurchasePackageResponse> CustomerPurchasePackage(CustomerPurchasePackageRequest request, ServerCallContext context)
{
// Mock Customer package purchase with realistic Persian response
var orderId = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var authority = "A" + orderId.ToString("D19");
return new CustomerPurchasePackageResponse
{
Success = true,
Message = "درخواست خرید پکیج با موفقیت ثبت شد",
OrderId = orderId,
PaymentGatewayUrl = $"https://payment.gateway.com/payment?authority={authority}&amount={GetPackagePrice(request.PackageId)}",
Authority = authority
};
}
public override async Task<CustomerVerifyPackagePurchaseResponse> CustomerVerifyPackagePurchase(CustomerVerifyPackagePurchaseRequest request, ServerCallContext context)
{
// Mock Customer purchase verification with realistic Persian data
var transactionId = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var referenceCode = "REF" + transactionId.ToString();
var isSuccessful = request.Status == "OK";
return new CustomerVerifyPackagePurchaseResponse
{
Success = isSuccessful,
Message = isSuccessful ? "خرید پکیج با موفقیت تایید شد" : "خرید پکیج ناموفق بود",
TransactionId = transactionId,
ReferenceCode = referenceCode,
PurchaseInfo = isSuccessful ? new PackagePurchaseInfo
{
PackageId = 1,
PackageName = "پکیج طلایی",
AmountPaid = 5600000,
PurchaseDate = Timestamp.FromDateTime(DateTime.UtcNow),
ExpiryDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(365))
} : null
};
}
public override async Task<GetCustomerPurchaseHistoryResponse> GetCustomerPurchaseHistory(GetCustomerPurchaseHistoryRequest request, ServerCallContext context)
{
// Mock Customer purchase history with realistic Persian data
var purchases = new List<PackagePurchaseHistory>
{
new PackagePurchaseHistory
{
Id = 1,
PackageId = 1,
PackageName = "پکیج طلایی",
Amount = 5600000,
PackageType = PackageTypeEnum.PackageTypeGolden,
PurchaseDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-30)),
ExpiryDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(335)),
Status = PaymentStatusEnum.PaymentStatusSuccess,
StatusMessage = "فعال",
ReferenceCode = "REF123456789"
},
new PackagePurchaseHistory
{
Id = 2,
PackageId = 2,
PackageName = "پکیج پریمیوم",
Amount = 3200000,
PackageType = PackageTypeEnum.PackageTypePremium,
PurchaseDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-180)),
ExpiryDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-150)),
Status = PaymentStatusEnum.PaymentStatusSuccess,
StatusMessage = "منقضی شده",
ReferenceCode = "REF987654321"
}
};
return new GetCustomerPurchaseHistoryResponse
{
MetaData = new MetaData
{
CurrentPage = request.PaginationState?.PageNumber ?? 1,
TotalPage = 1,
PageSize = request.PaginationState?.PageSize ?? 10,
TotalCount = purchases.Count,
HasPrevious = false,
HasNext = false
},
Purchases = { purchases }
};
}
private long GetPackagePrice(long packageId)
{
return packageId switch
{
1 => 5600000, // Golden
2 => 3200000, // Premium
3 => 1500000, // Basic
_ => 1000000 // Default
};
}
}
@@ -1,61 +1,174 @@
using CMSMicroservice.Protobuf.Protos.Products;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
using CMSMicroservice.Application.ProductsCQ.Commands.UpdateProducts;
using CMSMicroservice.Application.ProductsCQ.Commands.DeleteProducts;
using CMSMicroservice.Application.ProductsCQ.Queries.GetProducts;
using CMSMicroservice.Application.ProductsCQ.Queries.GetAllProductsByFilter;
using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices;
using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock;
using CMSMicroservice.Application.ProductsCQ.Queries.GetLowStockProducts;
using CMSMicroservice.Application.ProductsCQ.Commands.ToggleProductStatus;
using Grpc.Core;
namespace CMSMicroservice.WebApi.Services;
public class ProductsService : ProductsContract.ProductsContractBase
{
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
public ProductsService(IDispatchRequestToCQRS dispatchRequestToCQRS)
{
_dispatchRequestToCQRS = dispatchRequestToCQRS;
}
public override async Task<CreateNewProductsResponse> CreateNewProducts(CreateNewProductsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CreateNewProductsRequest, CreateNewProductsCommand, CreateNewProductsResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<Empty> UpdateProducts(UpdateProductsRequest request, ServerCallContext context)
public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateProducts(UpdateProductsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<UpdateProductsRequest, UpdateProductsCommand>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<Empty> DeleteProducts(DeleteProductsRequest request, ServerCallContext context)
public override async Task<Google.Protobuf.WellKnownTypes.Empty> DeleteProducts(DeleteProductsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<DeleteProductsRequest, DeleteProductsCommand>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<GetProductsResponse> GetProducts(GetProductsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetProductsRequest, GetProductsQuery, GetProductsResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<GetAllProductsByFilterResponse> GetAllProductsByFilter(GetAllProductsByFilterRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetAllProductsByFilterRequest, GetAllProductsByFilterQuery, GetAllProductsByFilterResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<BulkUpdateProductPricesResponse> BulkUpdateProductPrices(BulkUpdateProductPricesRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<BulkUpdateProductPricesRequest, BulkUpdateProductPricesCommand, BulkUpdateProductPricesResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<BulkUpdateProductStockResponse> BulkUpdateProductStock(BulkUpdateProductStockRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<BulkUpdateProductStockRequest, BulkUpdateProductStockCommand, BulkUpdateProductStockResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<GetLowStockProductsResponse> GetLowStockProducts(GetLowStockProductsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetLowStockProductsRequest, GetLowStockProductsQuery, GetLowStockProductsResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<ToggleProductStatusResponse> ToggleProductStatus(ToggleProductStatusRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ToggleProductStatusRequest, ToggleProductStatusCommand, ToggleProductStatusResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
// ============= Customer-specific Methods =============
public override async Task<GetProductsResponse> GetCustomerProducts(GetProductsRequest request, ServerCallContext context)
{
// For now, return mock response with gallery and categories
return new GetProductsResponse
{
Id = request.Id,
Title = $"Product {request.Id}",
Description = "Sample product description for customers",
ShortInfomation = "Short info",
FullInformation = "Full product information for customers",
Price = 50000,
Discount = 10,
Rate = 4,
ImagePath = "/images/product.jpg",
ThumbnailPath = "/images/product-thumb.jpg",
SaleCount = 25,
ViewCount = 150,
RemainingCount = 10,
Gallery =
{
new ProductGalleryItem
{
ProductGalleryId = 1,
ProductImageId = 1,
Title = "Main Image",
ImagePath = "/gallery/main.jpg",
ImageThumbnailPath = "/gallery/main-thumb.jpg"
}
},
Categories =
{
new ProductCategoryPath
{
CategoryId = 1,
Title = "Electronics",
Path =
{
new CategoryNode { Id = 1, Title = "Electronics" }
}
}
}
};
}
public override async Task<GetCustomerProductsByFilterResponse> GetCustomerProductsByFilter(GetAllProductsByFilterRequest request, ServerCallContext context)
{
// Mock response for customers with categories
return new GetCustomerProductsByFilterResponse
{
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
{
CurrentPage = 1,
TotalPage = 1,
PageSize = 10,
TotalCount = 2,
HasPrevious = false,
HasNext = false
},
Models =
{
new GetCustomerProductsByFilterResponseModel
{
Id = 1,
Title = "Sample Product 1",
Description = "Description 1",
ShortInfomation = "Short info 1",
FullInformation = "Full info 1",
Price = 45000,
Discount = 5,
Rate = 4,
ImagePath = "/images/product1.jpg",
ThumbnailPath = "/images/product1-thumb.jpg",
SaleCount = 15,
ViewCount = 120,
RemainingCount = 8,
Categories =
{
new ProductCategoryPath
{
CategoryId = 1,
Title = "Electronics",
Path =
{
new CategoryNode { Id = 1, Title = "Electronics" }
}
}
}
},
new GetCustomerProductsByFilterResponseModel
{
Id = 2,
Title = "Sample Product 2",
Description = "Description 2",
ShortInfomation = "Short info 2",
FullInformation = "Full info 2",
Price = 35000,
Discount = 15,
Rate = 5,
ImagePath = "/images/product2.jpg",
ThumbnailPath = "/images/product2-thumb.jpg",
SaleCount = 30,
ViewCount = 200,
RemainingCount = 5,
Categories =
{
new ProductCategoryPath
{
CategoryId = 2,
Title = "Books",
Path =
{
new CategoryNode { Id = 2, Title = "Books" }
}
}
}
}
}
};
}
}
@@ -47,4 +47,116 @@ public class TransactionsService : TransactionsContract.TransactionsContractBase
{
return await _dispatchRequestToCQRS.Handle<RefundTransactionRequest, RefundTransactionCommand, RefundTransactionResponse>(request, context);
}
// ============= Customer-specific Methods =============
public override async Task<GetCustomerTransactionResponse> GetCustomerTransaction(GetCustomerTransactionRequest request, ServerCallContext context)
{
// Mock response for customer transaction
return new GetCustomerTransactionResponse
{
Id = request.Id ?? 1,
MerchantId = "MERCHANT123",
Amount = 150000,
CallbackUrl = "https://mysite.com/callback",
Description = "خرید محصولات",
Mobile = "09123456789",
Email = "customer@example.com",
RequestStatusCode = 100,
RequestStatusMessage = "Success",
Authority = request.Authority ?? "A0000000000000000000000000001234567",
FeeType = "Payer",
Fee = 1500,
Currency = CurrencyEnum.Irr,
PaymentStatus = true,
VerificationStatusCode = 101,
VerificationStatusMessage = "Verified",
CardHash = "4F8A56B2C1D3E9A7B5C2F1E8D6A9B4C7E3F2A1D5",
CardPan = "622106******4567",
RefId = "REF123456789",
OrderId = "ORDER001",
Type = TransactionTypeEnum.Real
};
}
public override async Task<GetCustomerTransactionsByFilterResponse> GetCustomerTransactionsByFilter(GetCustomerTransactionsByFilterRequest request, ServerCallContext context)
{
// Mock response for customer transactions list
return new GetCustomerTransactionsByFilterResponse
{
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
{
CurrentPage = 1,
TotalPage = 1,
PageSize = 10,
TotalCount = 2,
HasPrevious = false,
HasNext = false
},
Models =
{
new GetCustomerTransactionsByFilterResponseModel
{
Id = 1,
MerchantId = "MERCHANT123",
Amount = 150000,
CallbackUrl = "https://mysite.com/callback",
Description = "خرید محصولات",
Mobile = "09123456789",
Email = "customer@example.com",
Authority = "A0000000000000000000000000001234567",
Fee = 1500,
Currency = CurrencyEnum.Irr,
PaymentStatus = true,
CardHash = "4F8A56B2C1D3E9A7B5C2F1E8D6A9B4C7E3F2A1D5",
CardPan = "622106******4567",
RefId = "REF123456789",
OrderId = "ORDER001",
Type = TransactionTypeEnum.Real
},
new GetCustomerTransactionsByFilterResponseModel
{
Id = 2,
MerchantId = "MERCHANT123",
Amount = 75000,
CallbackUrl = "https://mysite.com/callback",
Description = "تست پرداخت",
Mobile = "09123456789",
Email = "customer@example.com",
Authority = "A0000000000000000000000000001234568",
Fee = 750,
Currency = CurrencyEnum.Irr,
PaymentStatus = false,
RefId = "REF123456790",
OrderId = "ORDER002",
Type = TransactionTypeEnum.Sandbox
}
}
};
}
public override async Task<CustomerPaymentRequestResponse> CustomerPaymentRequest(CustomerPaymentRequestRequest request, ServerCallContext context)
{
// Mock payment gateway response
return new CustomerPaymentRequestResponse
{
PaymentGWUrl = $"https://payment.gateway.com/payment?amount={request.Amount}&callback={request.CallbackUrl}&description={request.Description}"
};
}
public override async Task<CustomerPaymentVerificationResponse> CustomerPaymentVerification(CustomerPaymentVerificationRequest request, ServerCallContext context)
{
// Mock payment verification response
bool isSuccessful = request.Status == "OK";
return new CustomerPaymentVerificationResponse
{
Id = 12345,
PaymentStatus = isSuccessful,
Message = isSuccessful ? "پرداخت با موفقیت انجام شد" : "پرداخت ناموفق",
RefId = isSuccessful ? "REF123456789" : null,
OrderId = "ORDER001",
VerificationStatusCode = isSuccessful ? 101 : 102
};
}
}
@@ -1,44 +1,89 @@
using CMSMicroservice.Protobuf.Protos.UserCarts;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.UserCartsCQ.Commands.CreateNewUserCarts;
using CMSMicroservice.Application.UserCartsCQ.Commands.UpdateUserCarts;
using CMSMicroservice.Application.UserCartsCQ.Commands.DeleteUserCarts;
using CMSMicroservice.Application.UserCartsCQ.Queries.GetUserCarts;
using CMSMicroservice.Application.UserCartsCQ.Queries.GetAllUserCartsByFilter;
using CMSMicroservice.Application.UserCartsCQ.Commands.ClearCart;
namespace CMSMicroservice.WebApi.Services;
public class UserCartsService : UserCartsContract.UserCartsContractBase
{
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
private readonly IDispatchRequestToCQRS _dispatcher;
public UserCartsService(IDispatchRequestToCQRS dispatchRequestToCQRS)
public UserCartsService(IDispatchRequestToCQRS dispatcher)
{
_dispatchRequestToCQRS = dispatchRequestToCQRS;
_dispatcher = dispatcher;
}
public override async Task<CreateNewUserCartsResponse> CreateNewUserCarts(CreateNewUserCartsRequest request, ServerCallContext context)
#region Customer Methods
public override async Task<AddNewUserCartForCustomerResponse> AddNewUserCartForCustomer(
AddNewUserCartForCustomerRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CreateNewUserCartsRequest, CreateNewUserCartsCommand, CreateNewUserCartsResponse>(request, context);
// TODO: Map to DiscountShop AddToCart command
return new AddNewUserCartForCustomerResponse
{
Message = "AddNewUserCartForCustomer not implemented yet"
};
}
public override async Task<Empty> UpdateUserCarts(UpdateUserCartsRequest request, ServerCallContext context)
public override async Task<UpdateUserCartForCustomerResponse> UpdateUserCartForCustomer(
UpdateUserCartForCustomerRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<UpdateUserCartsRequest, UpdateUserCartsCommand>(request, context);
// TODO: Map to DiscountShop UpdateCartItemCount command
return new UpdateUserCartForCustomerResponse
{
Message = "UpdateUserCartForCustomer not implemented yet"
};
}
public override async Task<Empty> DeleteUserCarts(DeleteUserCartsRequest request, ServerCallContext context)
public override async Task<RemoveUserCartForCustomerResponse> RemoveUserCartForCustomer(
RemoveUserCartForCustomerRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<DeleteUserCartsRequest, DeleteUserCartsCommand>(request, context);
// TODO: Map to DiscountShop RemoveFromCart command
return new RemoveUserCartForCustomerResponse
{
Message = "RemoveUserCartForCustomer not implemented yet"
};
}
public override async Task<GetUserCartsResponse> GetUserCarts(GetUserCartsRequest request, ServerCallContext context)
public override async Task<GetUserCartForCustomerResponse> GetCustomerCart(
GetUserCartForCustomerRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetUserCartsRequest, GetUserCartsQuery, GetUserCartsResponse>(request, context);
// TODO: Map to DiscountShop GetUserCart query
return new GetUserCartForCustomerResponse();
}
public override async Task<GetAllUserCartsByFilterResponse> GetAllUserCartsByFilter(GetAllUserCartsByFilterRequest request, ServerCallContext context)
#endregion
#region Admin Methods
public override async Task<AddNewUserCartResponse> AddNewUserCart(
AddNewUserCartRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetAllUserCartsByFilterRequest, GetAllUserCartsByFilterQuery, GetAllUserCartsByFilterResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "AddNewUserCart not implemented yet"));
}
public override async Task<ClearCartResponse> ClearCart(ClearCartRequest request, ServerCallContext context)
public override async Task<Empty> UpdateUserCart(
UpdateUserCartRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ClearCartRequest, ClearCartCommand, ClearCartResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "UpdateUserCart not implemented yet"));
}
public override async Task<Empty> DeleteUserCart(
DeleteUserCartRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "DeleteUserCart not implemented yet"));
}
public override async Task<GetUserCartResponse> GetUserCart(
GetUserCartRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "GetUserCart not implemented yet"));
}
public override async Task<GetAllUserCartsByFilterResponse> GetAllUserCartsByFilter(
GetAllUserCartsByFilterRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "GetAllUserCartsByFilter not implemented yet"));
}
#endregion
}
@@ -0,0 +1,158 @@
using CMSMicroservice.Protobuf.Protos.UserCarts;
using CMSMicroservice.WebApi.Common.Services;
namespace CMSMicroservice.WebApi.Services;
public class UserCartsService : UserCartsContract.UserCartsContractBase
{
private readonly IDispatchRequestToCQRS _dispatcher;
public UserCartsService(IDispatchRequestToCQRS dispatcher)
{
_dispatcher = dispatcher;
}
#region Customer Methods
public override async Task<AddNewUserCartForCustomerResponse> AddNewUserCartForCustomer(
AddNewUserCartForCustomerRequest request, ServerCallContext context)
{
// TODO: Map to DiscountShop AddToCart command
return new AddNewUserCartForCustomerResponse
{
Message = "AddNewUserCartForCustomer not implemented yet"
};
}
public override async Task<UpdateUserCartForCustomerResponse> UpdateUserCartForCustomer(
UpdateUserCartForCustomerRequest request, ServerCallContext context)
{
// TODO: Map to DiscountShop UpdateCartItemCount command
return new UpdateUserCartForCustomerResponse
{
Message = "UpdateUserCartForCustomer not implemented yet"
};
}
public override async Task<RemoveUserCartForCustomerResponse> RemoveUserCartForCustomer(
RemoveUserCartForCustomerRequest request, ServerCallContext context)
{
// TODO: Map to DiscountShop RemoveFromCart command
return new RemoveUserCartForCustomerResponse
{
Message = "RemoveUserCartForCustomer not implemented yet"
};
}
public override async Task<GetUserCartForCustomerResponse> GetUserCartForCustomer(
GetUserCartForCustomerRequest request, ServerCallContext context)
{
// TODO: Map to DiscountShop GetUserCart query
return new GetUserCartForCustomerResponse();
}
#endregion
#region Admin Methods
public override async Task<AddNewUserCartResponse> AddNewUserCart(
AddNewUserCartRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "AddNewUserCart not implemented yet"));
}
public override async Task<Empty> UpdateUserCart(
UpdateUserCartRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "UpdateUserCart not implemented yet"));
}
public override async Task<Empty> DeleteUserCart(
DeleteUserCartRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "DeleteUserCart not implemented yet"));
}
public override async Task<GetUserCartResponse> GetUserCart(
GetUserCartRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "GetUserCart not implemented yet"));
}
public override async Task<GetAllUserCartsByFilterResponse> GetAllUserCartsByFilter(
GetAllUserCartsByFilterRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "GetAllUserCartsByFilter not implemented yet"));
}
#endregion
}
using CMSMicroservice.Application.UserCartsCQ.Commands.ClearCart;
using Grpc.Core;
namespace CMSMicroservice.WebApi.Services;
public class UserCartsService : UserCartsContract.UserCartsContractBase
{
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
public UserCartsService(IDispatchRequestToCQRS dispatchRequestToCQRS)
{
_dispatchRequestToCQRS = dispatchRequestToCQRS;
}
public override async Task<CreateNewUserCartsResponse> CreateNewUserCarts(CreateNewUserCartsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CreateNewUserCartsRequest, CreateNewUserCartsCommand, CreateNewUserCartsResponse>(request, context);
}
public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateUserCarts(UpdateUserCartsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<UpdateUserCartsRequest, UpdateUserCartsCommand>(request, context);
}
public override async Task<Google.Protobuf.WellKnownTypes.Empty> DeleteUserCarts(DeleteUserCartsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<DeleteUserCartsRequest, DeleteUserCartsCommand>(request, context);
}
public override async Task<GetUserCartsResponse> GetUserCarts(GetUserCartsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetUserCartsRequest, GetUserCartsQuery, GetUserCartsResponse>(request, context);
}
public override async Task<GetAllUserCartsByFilterResponse> GetAllUserCartsByFilter(GetAllUserCartsByFilterRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetAllUserCartsByFilterRequest, GetAllUserCartsByFilterQuery, GetAllUserCartsByFilterResponse>(request, context);
}
public override async Task<ClearCartResponse> ClearCart(ClearCartRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ClearCartRequest, ClearCartCommand, ClearCartResponse>(request, context);
}
public override async Task<ClearCartResponse> ClearCartForCustomer(ClearCartRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ClearCartRequest, ClearCartCommand, ClearCartResponse>(request, context);
}
// ============= Customer-specific Methods =============
public override async Task<CreateNewUserCartsResponse> AddNewUserCartForCustomer(CreateNewUserCartsRequest request, ServerCallContext context)
{
// Reuse the existing CreateNewUserCarts logic
return await CreateNewUserCarts(request, context);
}
public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateUserCartForCustomer(UpdateUserCartsRequest request, ServerCallContext context)
{
// Reuse the existing UpdateUserCarts logic
return await UpdateUserCarts(request, context);
}
public override async Task<GetAllUserCartsByFilterResponse> GetUserCartForCustomer(GetAllUserCartsByFilterRequest request, ServerCallContext context)
{
// Reuse the existing GetAllUserCartsByFilter logic
return await GetAllUserCartsByFilter(request, context);
}
}
@@ -1,73 +1,270 @@
using CMSMicroservice.Protobuf.Protos.UserOrder;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.UserOrderCQ.Commands.CreateNewUserOrder;
using CMSMicroservice.Application.UserOrderCQ.Commands.UpdateUserOrder;
using CMSMicroservice.Application.UserOrderCQ.Commands.DeleteUserOrder;
using CMSMicroservice.Application.UserOrderCQ.Commands.UpdateOrderStatus;
using CMSMicroservice.Application.UserOrderCQ.Commands.ApplyDiscountToOrder;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetUserOrder;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetAllUserOrderByFilter;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetOrdersByDateRange;
using CMSMicroservice.Application.UserOrderCQ.Queries.CalculateOrderPV;
using CMSMicroservice.Application.UserOrderCQ.Commands.SubmitShopBuyOrder;
using CMSMicroservice.Application.UserOrderCQ.Commands.CancelOrder;
using Grpc.Core;
using Google.Protobuf.WellKnownTypes;
using System.Collections.Generic;
using CMSMicroservice.Protobuf.Protos;
namespace CMSMicroservice.WebApi.Services;
public class UserOrderService : UserOrderContract.UserOrderContractBase
{
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
public UserOrderService(IDispatchRequestToCQRS dispatchRequestToCQRS)
{
_dispatchRequestToCQRS = dispatchRequestToCQRS;
}
public override async Task<CreateNewUserOrderResponse> CreateNewUserOrder(CreateNewUserOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CreateNewUserOrderRequest, CreateNewUserOrderCommand, CreateNewUserOrderResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<Empty> UpdateUserOrder(UpdateUserOrderRequest request, ServerCallContext context)
public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateUserOrder(UpdateUserOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<UpdateUserOrderRequest, UpdateUserOrderCommand>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<Empty> DeleteUserOrder(DeleteUserOrderRequest request, ServerCallContext context)
public override async Task<Google.Protobuf.WellKnownTypes.Empty> DeleteUserOrder(DeleteUserOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<DeleteUserOrderRequest, DeleteUserOrderCommand>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<GetUserOrderResponse> GetUserOrder(GetUserOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetUserOrderRequest, GetUserOrderQuery, GetUserOrderResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<GetAllUserOrderByFilterResponse> GetAllUserOrderByFilter(GetAllUserOrderByFilterRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetAllUserOrderByFilterRequest, GetAllUserOrderByFilterQuery, GetAllUserOrderByFilterResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<SubmitShopBuyOrderResponse> SubmitShopBuyOrder(SubmitShopBuyOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<SubmitShopBuyOrderRequest, SubmitShopBuyOrderCommand, SubmitShopBuyOrderResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<CancelOrderResponse> CancelOrder(CancelOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CancelOrderRequest, CancelOrderCommand, CancelOrderResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<UpdateOrderStatusResponse> UpdateOrderStatus(UpdateOrderStatusRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<UpdateOrderStatusRequest, UpdateOrderStatusCommand, UpdateOrderStatusResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<GetOrdersByDateRangeResponse> GetOrdersByDateRange(GetOrdersByDateRangeRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetOrdersByDateRangeRequest, GetOrdersByDateRangeQuery, GetOrdersByDateRangeResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<ApplyDiscountToOrderResponse> ApplyDiscountToOrder(ApplyDiscountToOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ApplyDiscountToOrderRequest, ApplyDiscountToOrderCommand, ApplyDiscountToOrderResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
public override async Task<CalculateOrderPVResponse> CalculateOrderPV(CalculateOrderPVRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CalculateOrderPVRequest, CalculateOrderPVQuery, CalculateOrderPVResponse>(request, context);
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
}
// ============= Customer-specific Methods =============
public override async Task<CreateNewUserOrderResponse> CreateNewOrderForCustomer(CreateNewUserOrderRequest request, ServerCallContext context)
{
// For now, return empty response - will be implemented properly later
return new CreateNewUserOrderResponse();
}
public override async Task<SubmitShopBuyOrderResponse> SubmitOrderForCustomer(SubmitShopBuyOrderRequest request, ServerCallContext context)
{
// For now, return empty response - will be implemented properly later
return new SubmitShopBuyOrderResponse();
}
public override async Task<GetAllUserOrderByFilterResponse> GetCustomerOrders(GetAllUserOrderByFilterRequest request, ServerCallContext context)
{
// For now, return empty response - will be implemented properly later
return new GetAllUserOrderByFilterResponse();
}
public override async Task<GetUserOrderResponse> GetCustomerOrder(GetUserOrderRequest request, ServerCallContext context)
{
// Mock Customer order details with correct property names
return new GetUserOrderResponse
{
Id = request.Id,
Amount = 250000,
PackageId = 1,
UserId = 1,
PaymentStatus = PaymentStatus.Success,
PaymentDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-2))
};
}
// ============= Customer-specific Method Implementations =============
public override async Task<CustomerCancelOrderResponse> CustomerCancelOrder(CustomerCancelOrderRequest request, ServerCallContext context)
{
// Mock Customer order cancellation with realistic Persian response
return new CustomerCancelOrderResponse
{
Success = true,
Message = "سفارش شما با موفقیت لغو شد",
RefundAmount = 180000,
RefundTransactionId = "REF" + DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
}
public override async Task<GetCustomerOrderHistoryResponse> GetCustomerOrderHistory(GetCustomerOrderHistoryRequest request, ServerCallContext context)
{
// Mock Customer order history with realistic Persian data
var orders = new List<CustomerOrderModel>
{
new CustomerOrderModel
{
Id = 1,
Amount = 250000,
PackageId = 1,
PackageName = "پکیج اسپشیال",
Status = OrderStatusEnum.OrderStatusDelivered,
StatusMessage = "تحویل داده شد",
OrderDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-10)),
DeliveryDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-3)),
TrackingCode = "TRK001",
ItemsCount = 5,
CanCancel = false,
CanReorder = true
},
new CustomerOrderModel
{
Id = 2,
Amount = 150000,
PackageId = 2,
PackageName = "پکیج عادی",
Status = OrderStatusEnum.OrderStatusShipped,
StatusMessage = "ارسال شده",
OrderDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-3)),
DeliveryDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(2)),
TrackingCode = "TRK002",
ItemsCount = 3,
CanCancel = true,
CanReorder = true
}
};
return new GetCustomerOrderHistoryResponse
{
MetaData = new MetaData
{
CurrentPage = request.PaginationState?.PageNumber ?? 1,
TotalPage = 1,
PageSize = request.PaginationState?.PageSize ?? 10,
TotalCount = orders.Count,
HasPrevious = false,
HasNext = false
},
Orders = { orders }
};
}
public override async Task<CustomerTrackOrderResponse> CustomerTrackOrder(CustomerTrackOrderRequest request, ServerCallContext context)
{
// Mock Customer order tracking with detailed Persian information
var statusHistory = new List<OrderStatusHistory>
{
new OrderStatusHistory
{
Status = OrderStatusEnum.OrderStatusPending,
StatusMessage = "در انتظار تایید",
ChangedAt = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-5)),
ChangedBy = "سیستم"
},
new OrderStatusHistory
{
Status = OrderStatusEnum.OrderStatusConfirmed,
StatusMessage = "تایید شده",
ChangedAt = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-4)),
ChangedBy = "کارشناس فروش"
},
new OrderStatusHistory
{
Status = OrderStatusEnum.OrderStatusProcessing,
StatusMessage = "در حال آماده‌سازی",
ChangedAt = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-3)),
ChangedBy = "انبار"
},
new OrderStatusHistory
{
Status = OrderStatusEnum.OrderStatusShipped,
StatusMessage = "ارسال شده",
ChangedAt = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-2)),
ChangedBy = "پست پیشتاز"
}
};
var deliverySteps = new List<DeliveryStep>
{
new DeliveryStep
{
StepName = "دریافت از فروشنده",
StepDescription = "بسته از فروشنده دریافت شد",
StepTime = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-2)),
IsCompleted = true
},
new DeliveryStep
{
StepName = "مرکز پردازش تهران",
StepDescription = "بسته در مرکز پردازش تهران",
StepTime = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-1)),
IsCompleted = true
},
new DeliveryStep
{
StepName = "در حال ارسال",
StepDescription = "بسته در حال ارسال به آدرس مقصد",
StepTime = Timestamp.FromDateTime(DateTime.UtcNow.AddHours(-8)),
IsCompleted = false
}
};
return new CustomerTrackOrderResponse
{
Order = new CustomerOrderModel
{
Id = request.OrderId,
Amount = 180000,
PackageId = 1,
PackageName = "پکیج ویژه",
Status = OrderStatusEnum.OrderStatusShipped,
StatusMessage = "در حال ارسال",
OrderDate = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-5)),
TrackingCode = "TRK" + request.OrderId.ToString("000"),
ItemsCount = 4,
CanCancel = false,
CanReorder = true
},
StatusHistory = { statusHistory },
DeliveryInfo = new DeliveryTrackingInfo
{
TrackingCode = "TRK" + request.OrderId.ToString("000"),
CourierName = "پست پیشتاز",
EstimatedDelivery = "فردا تا ساعت 18:00",
CurrentLocation = "مرکز پخش منطقه 5 تهران",
DeliverySteps = { deliverySteps }
}
};
}
public override async Task<CustomerReorderResponse> CustomerReorderPreviousOrder(CustomerReorderRequest request, ServerCallContext context)
{
// Mock Customer reorder functionality
var newOrderId = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var totalAmount = request.UseCurrentPrices ? 280000 : 250000;
return new CustomerReorderResponse
{
Success = true,
Message = request.UseCurrentPrices ?
"سفارش مجدد با قیمت‌های جدید ثبت شد" :
"سفارش مجدد با قیمت‌های قبلی ثبت شد",
NewOrderId = newOrderId,
TotalAmount = totalAmount
};
}
}
@@ -1,4 +1,5 @@
using CMSMicroservice.Protobuf.Protos.User;
using CMSMicroservice.Protobuf.Protos.City;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.UserCQ.Commands.CreateNewUser;
using CMSMicroservice.Application.UserCQ.Commands.UpdateUser;
@@ -9,6 +10,12 @@ using CMSMicroservice.Application.UserCQ.Queries.GetJwtToken;
using CMSMicroservice.Application.UserCQ.Queries.AdminGetJwtToken;
using CMSMicroservice.Application.UserCQ.Commands.SetPasswordForUser;
using CMSMicroservice.Application.UserCQ.Commands.RefreshToken;
using CMSMicroservice.Application.UserCQ.Commands.CreateNewOtpToken;
using CMSMicroservice.Application.UserCQ.Commands.VerifyOtpToken;
using CMSMicroservice.Application.UserCQ.Commands.AcceptContract;
using Google.Protobuf.WellKnownTypes;
using System.Collections.Generic;
using System.Linq;
namespace CMSMicroservice.WebApi.Services;
public class UserService : UserContract.UserContractBase
{
@@ -54,4 +61,246 @@ public class UserService : UserContract.UserContractBase
{
return await _dispatchRequestToCQRS.Handle<RefreshTokenRequest, RefreshTokenCommand, RefreshTokenResponse>(request, context);
}
// ============= Customer-specific Methods =============
public override async Task<CreateNewOtpTokenResponse> CreateNewOtpToken(CreateNewOtpTokenRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CreateNewOtpTokenRequest, CreateNewOtpTokenCommand, CreateNewOtpTokenResponse>(request, context);
}
public override async Task<VerifyOtpTokenResponse> VerifyOtpToken(VerifyOtpTokenRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<VerifyOtpTokenRequest, VerifyOtpTokenCommand, VerifyOtpTokenResponse>(request, context);
}
public override async Task<AcceptContractResponse> AcceptContract(AcceptContractRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<AcceptContractRequest, AcceptContractCommand, AcceptContractResponse>(request, context);
}
public override async Task<GetUserForCustomerResponse> GetUserForCustomer(GetUserForCustomerRequest request, ServerCallContext context)
{
// Mock implementation for Customer Get User
await Task.Delay(10); // Simulate async operation
return new GetUserForCustomerResponse
{
Id = 123,
FirstName = "احمد",
LastName = "محمدی",
Mobile = "09123456789",
Email = "ahmad.mohammadi@example.com",
NationalCode = "1234567890",
AvatarPath = "/avatars/user_123.jpg",
ParentId = 100,
ReferralCode = "REF123456",
IsMobileVerified = true,
MobileVerifiedAt = Timestamp.FromDateTime(DateTime.SpecifyKind(new DateTime(2024, 1, 15), DateTimeKind.Utc)),
EmailNotifications = true,
SmsNotifications = true,
PushNotifications = false,
BirthDate = Timestamp.FromDateTime(DateTime.SpecifyKind(new DateTime(1990, 5, 20), DateTimeKind.Utc))
};
}
public override async Task<Empty> UpdateCustomerProfile(UpdateCustomerProfileRequest request, ServerCallContext context)
{
// Mock implementation for Update Customer Profile
await Task.Delay(10);
return new Empty();
}
public override async Task<GetCustomerProfileResponse> GetCustomerProfile(GetCustomerProfileRequest request, ServerCallContext context)
{
// Mock implementation for Get Customer Profile
await Task.Delay(10);
return new GetCustomerProfileResponse
{
Id = 123,
FirstName = "احمد",
LastName = "محمدی",
Mobile = "09123456789",
Email = "ahmad.mohammadi@example.com",
NationalCode = "1234567890",
AvatarPath = "/avatars/user_123.jpg",
ParentId = 100,
ReferralCode = "REF123456",
IsMobileVerified = true,
MobileVerifiedAt = Timestamp.FromDateTime(DateTime.SpecifyKind(new DateTime(2024, 1, 15), DateTimeKind.Utc)),
EmailNotifications = true,
SmsNotifications = true,
PushNotifications = false,
BirthDate = Timestamp.FromDateTime(DateTime.SpecifyKind(new DateTime(1990, 5, 20), DateTimeKind.Utc)),
FullName = "احمد محمدی",
ProfileCompletionPercentage = 85
};
}
public override async Task<ChangeCustomerPasswordResponse> ChangeCustomerPassword(ChangeCustomerPasswordRequest request, ServerCallContext context)
{
// Mock implementation for Change Customer Password
await Task.Delay(10);
if (request.NewPassword != request.ConfirmPassword)
{
return new ChangeCustomerPasswordResponse
{
Success = false,
Message = "رمز عبور جدید و تکرار آن یکسان نیستند"
};
}
if (request.NewPassword.Length < 6)
{
return new ChangeCustomerPasswordResponse
{
Success = false,
Message = "رمز عبور باید حداقل 6 کاراکتر باشد"
};
}
return new ChangeCustomerPasswordResponse
{
Success = true,
Message = "رمز عبور با موفقیت تغییر یافت"
};
}
public override async Task<GetCustomerReferralsResponse> GetCustomerReferrals(GetCustomerReferralsRequest request, ServerCallContext context)
{
// Mock implementation for Get Customer Referrals
await Task.Delay(10);
var referrals = new List<CustomerReferralModel>
{
new CustomerReferralModel
{
Id = 1,
FirstName = "علی",
LastName = "احمدی",
Mobile = "09121234567",
JoinDate = Timestamp.FromDateTime(DateTime.SpecifyKind(new DateTime(2025, 12, 1), DateTimeKind.Utc)),
IsActive = true,
StatusMessage = "فعال",
Level = 1,
TotalCommission = 2500000
},
new CustomerReferralModel
{
Id = 2,
FirstName = "فاطمه",
LastName = "کریمی",
Mobile = "09122345678",
JoinDate = Timestamp.FromDateTime(DateTime.SpecifyKind(new DateTime(2025, 11, 15), DateTimeKind.Utc)),
IsActive = true,
StatusMessage = "فعال",
Level = 1,
TotalCommission = 1800000
},
new CustomerReferralModel
{
Id = 3,
FirstName = "محسن",
LastName = "رضایی",
Mobile = "09123456789",
JoinDate = Timestamp.FromDateTime(DateTime.SpecifyKind(new DateTime(2025, 10, 20), DateTimeKind.Utc)),
IsActive = false,
StatusMessage = "غیرفعال",
Level = 1,
TotalCommission = 950000
}
};
return new GetCustomerReferralsResponse
{
MetaData = new MetaData
{
CurrentPage = 1,
TotalPage = 1,
PageSize = 10,
TotalCount = 3,
HasPrevious = false,
HasNext = false
},
Referrals = { referrals },
Stats = new CustomerReferralStats
{
TotalReferrals = 3,
ActiveReferrals = 2,
TotalCommissionEarned = 5250000,
ThisMonthCommission = 850000
}
};
}
public override async Task<UploadCustomerAvatarResponse> UploadCustomerAvatar(UploadCustomerAvatarRequest request, ServerCallContext context)
{
// Mock implementation for Upload Customer Avatar
await Task.Delay(10);
if (request.FileData == null || request.FileData.Length == 0)
{
return new UploadCustomerAvatarResponse
{
Success = false,
Message = "فایل انتخاب نشده است"
};
}
if (request.FileData.Length > 5 * 1024 * 1024) // 5MB limit
{
return new UploadCustomerAvatarResponse
{
Success = false,
Message = "حجم فایل نباید بیش از 5 مگابایت باشد"
};
}
var allowedTypes = new[] { "image/jpeg", "image/jpg", "image/png", "image/gif" };
if (!allowedTypes.Contains(request.FileMimeType?.ToLower()))
{
return new UploadCustomerAvatarResponse
{
Success = false,
Message = "فرمت فایل مجاز نیست. فقط JPG, PNG و GIF مجاز هستند"
};
}
// Simulate file upload and generate URL
var fileName = $"avatar_{DateTime.Now.Ticks}.{request.FileMimeType?.Split('/').LastOrDefault()}";
var avatarUrl = $"/uploads/avatars/{fileName}";
return new UploadCustomerAvatarResponse
{
Success = true,
Message = "تصویر پروفایل با موفقیت آپلود شد",
AvatarUrl = avatarUrl
};
}
public override async Task<GetCustomerSettingsResponse> GetCustomerSettings(GetCustomerSettingsRequest request, ServerCallContext context)
{
// Mock implementation for Get Customer Settings
await Task.Delay(10);
return new GetCustomerSettingsResponse
{
EmailNotifications = true,
SmsNotifications = true,
PushNotifications = false,
MarketingNotifications = true,
PreferredLanguage = "fa-IR",
TimeZone = "Asia/Tehran",
TwoFactorAuthEnabled = false
};
}
public override async Task<Empty> UpdateCustomerSettings(UpdateCustomerSettingsRequest request, ServerCallContext context)
{
// Mock implementation for Update Customer Settings
await Task.Delay(10);
return new Empty();
}
}
@@ -34,4 +34,103 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
{
return await _dispatchRequestToCQRS.Handle<GetAllUserWalletByFilterRequest, GetAllUserWalletByFilterQuery, GetAllUserWalletByFilterResponse>(request, context);
}
// ============= Customer-specific Methods =============
public override async Task<GetCustomerWalletResponse> GetCustomerWallet(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
{
// Mock response for customer wallet
return new GetCustomerWalletResponse
{
Balance = 150000,
NetworkBalance = 75000,
DiscountBalance = 25000
};
}
public override async Task<GetCustomerWalletChangeLogResponse> GetCustomerWalletChangeLog(GetCustomerWalletChangeLogRequest request, ServerCallContext context)
{
// Mock response for wallet change log
return new GetCustomerWalletChangeLogResponse
{
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
{
CurrentPage = 1,
TotalPage = 1,
PageSize = 10,
TotalCount = 3,
HasPrevious = false,
HasNext = false
},
Models =
{
new CustomerWalletChangeLogModel
{
CurrentBalance = 150000,
ChangeValue = 50000,
CurrentNetworkBalance = 75000,
ChangeNerworkValue = 25000,
IsIncrease = true,
RefrenceId = 123,
CreatedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-1))
},
new CustomerWalletChangeLogModel
{
CurrentBalance = 100000,
ChangeValue = -20000,
CurrentNetworkBalance = 50000,
ChangeNerworkValue = -10000,
IsIncrease = false,
RefrenceId = 124,
CreatedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-2))
}
}
};
}
public override async Task<Google.Protobuf.WellKnownTypes.Empty> CustomerWithdrawBalance(CustomerWithdrawBalanceRequest request, ServerCallContext context)
{
// Mock implementation - would handle withdrawal
return new Google.Protobuf.WellKnownTypes.Empty();
}
public override async Task<GetCustomerWithdrawalsResponse> GetCustomerWithdrawals(GetCustomerWithdrawalsRequest request, ServerCallContext context)
{
// Mock response for customer withdrawals
return new GetCustomerWithdrawalsResponse
{
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
{
CurrentPage = 1,
TotalPage = 1,
PageSize = 10,
TotalCount = 1,
HasPrevious = false,
HasNext = false
},
Models =
{
new CustomerWithdrawalModel
{
Id = 1,
WeekDefinitionId = 1,
WeekDisplayName = "هفته 1 - دی 1403",
TotalAmount = 50000,
Status = 1, // 0: Pending, 1: Approved, 2: Rejected
WithdrawalMethod = 0, // 0: Cash, 1: Diamond
IbanNumber = "IR123456789",
Created = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow.AddDays(-3))
}
}
};
}
public override async Task<GetCustomerWithdrawalSettingsResponse> GetCustomerWithdrawalSettings(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
{
// Mock response for withdrawal settings
return new GetCustomerWithdrawalSettingsResponse
{
MinWithdrawalAmount = 50000 // Minimum 50,000 for withdrawal
};
}
}
@@ -0,0 +1,158 @@
/* FourSat CMS Swagger Custom Styles */
/* Header styling */
.swagger-ui .topbar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-bottom: 3px solid #5a67d8;
}
.swagger-ui .topbar .download-url-wrapper {
display: none;
}
/* API sections styling */
.swagger-ui .scheme-container {
background: #f7fafc;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
}
/* Tag headers (API groups) */
.swagger-ui .opblock-tag {
border-bottom: 2px solid #e2e8f0;
color: #2d3748;
font-weight: 600;
font-size: 18px;
padding: 15px 0;
}
/* Method buttons styling */
.swagger-ui .opblock.opblock-get .opblock-summary-method {
background: #48bb78;
}
.swagger-ui .opblock.opblock-post .opblock-summary-method {
background: #4299e1;
}
.swagger-ui .opblock.opblock-put .opblock-summary-method {
background: #ed8936;
}
.swagger-ui .opblock.opblock-delete .opblock-summary-method {
background: #f56565;
}
/* Response sections */
.swagger-ui .responses-inner {
border: 1px solid #e2e8f0;
border-radius: 6px;
background: #f7fafc;
}
/* Model schema styling */
.swagger-ui .model-box {
background: #edf2f7;
border: 1px solid #cbd5e0;
border-radius: 6px;
}
/* Try it out button */
.swagger-ui .btn.try-out {
background: #667eea;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-weight: 500;
}
.swagger-ui .btn.try-out:hover {
background: #5a67d8;
}
/* Execute button */
.swagger-ui .btn.execute {
background: #48bb78;
border: none;
border-radius: 6px;
padding: 10px 20px;
font-weight: 600;
}
.swagger-ui .btn.execute:hover {
background: #38a169;
}
/* Custom badges for different API types */
.swagger-ui .info .title:after {
content: "🚀 Powered by FourSat";
font-size: 12px;
color: #718096;
font-weight: normal;
display: block;
margin-top: 5px;
}
/* Security badge */
.swagger-ui .auth-btn-wrapper {
display: flex;
justify-content: flex-end;
padding: 10px;
}
.swagger-ui .btn.authorize {
background: #805ad5;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-weight: 500;
}
.swagger-ui .btn.authorize:hover {
background: #6b46c1;
}
/* Loading states */
.swagger-ui .loading-container {
background: #f7fafc;
border: 2px dashed #cbd5e0;
border-radius: 8px;
padding: 20px;
text-align: center;
color: #4a5568;
}
/* Custom scrollbar */
.swagger-ui ::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.swagger-ui ::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.swagger-ui ::-webkit-scrollbar-thumb {
background: #cbd5e0;
border-radius: 4px;
}
.swagger-ui ::-webkit-scrollbar-thumb:hover {
background: #a0aec0;
}
/* Responsive design */
@media (max-width: 768px) {
.swagger-ui .topbar {
padding: 10px;
}
.swagger-ui .info .title {
font-size: 24px;
}
}