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
@@ -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>();
}
}