feat: integrate PYMS payment gateway, add blog/sitepage/image services, local file manager
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m44s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m44s
Payment Gateway: - Add PYMSPaymentService: IPaymentGatewayService via gRPC to PYMS microservice - Add ZarinPalPaymentService: direct ZarinPal integration (backup) - Register 'pyms' payment provider in DI ConfigureServices - Add PYMS proto files (pyms_transaction.proto, pyms_public_messages.proto) - Fix VerifyDiscountWalletCharge: pass 'OK' as status instead of Authority - Update appsettings: PaymentProvider=pyms, sandbox mode, merchant ID Blog System: - Add BlogCategory, BlogPost, BlogPostImage entities and CQRS - Add proto files and gRPC services for blog management - Add Mapster profiles for blog responses Content Management: - Add SitePage entity and CQRS for static pages - Add proto and gRPC service for site pages Image/File Management: - Add LocalFileManager with disk storage + base64 serving + FMS fallback - Add ImagePathResolverInterceptor for gRPC responses - Add ImageResolverService for explicit image resolution - Add UploadsController for public file serving with FMS fallback - Add PaymentCallbackController for discount order payment callbacks Database: - Add blog and content entity migrations - Remove ImagePath MaxLength constraints - Remove old FileManagementService (replaced by LocalFileManager)
This commit is contained in:
@@ -16,7 +16,6 @@ public class AppVersionService : AppVersionContract.AppVersionContractBase
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.SettingsView)]
|
||||
public override async Task<GetAppVersionResponse> GetAppVersion(GetAppVersionRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetAppVersionRequest, GetAppVersionQuery, GetAppVersionResponse>(request, context);
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
using CMSMicroservice.Protobuf.Protos.BlogCategory;
|
||||
using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.BlogCategoryCQ.Commands.CreateBlogCategory;
|
||||
using CMSMicroservice.Application.BlogCategoryCQ.Commands.UpdateBlogCategory;
|
||||
using CMSMicroservice.Application.BlogCategoryCQ.Commands.DeleteBlogCategory;
|
||||
using CMSMicroservice.Application.BlogCategoryCQ.Queries.GetBlogCategory;
|
||||
using CMSMicroservice.Application.BlogCategoryCQ.Queries.GetAllBlogCategories;
|
||||
using CMSMicroservice.Application.BlogCategoryCQ.Queries.GetActiveBlogCategories;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using Mapster;
|
||||
using MediatR;
|
||||
using AppModels = CMSMicroservice.Application.Common.Models;
|
||||
using ProtoMetaData = CMSMicroservice.Protobuf.Protos.MetaData;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class BlogCategoryService : BlogCategoryContract.BlogCategoryContractBase
|
||||
{
|
||||
private readonly ISender _sender;
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
|
||||
public BlogCategoryService(ISender sender, IDispatchRequestToCQRS dispatchRequestToCQRS)
|
||||
{
|
||||
_sender = sender;
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
}
|
||||
|
||||
public override async Task<CreateBlogCategoryResponse> CreateBlogCategory(CreateBlogCategoryRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<CreateBlogCategoryRequest, CreateBlogCategoryCommand, CreateBlogCategoryResponse>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<Empty> UpdateBlogCategory(UpdateBlogCategoryRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<UpdateBlogCategoryRequest, UpdateBlogCategoryCommand>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<Empty> DeleteBlogCategory(DeleteBlogCategoryRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<DeleteBlogCategoryRequest, DeleteBlogCategoryCommand>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<GetBlogCategoryResponse> GetBlogCategory(GetBlogCategoryRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetBlogCategoryQuery { Id = request.Id };
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
return MapCategoryToResponse(result);
|
||||
}
|
||||
|
||||
public override async Task<GetAllBlogCategoriesResponse> GetAllBlogCategories(GetAllBlogCategoriesRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetAllBlogCategoriesQuery
|
||||
{
|
||||
PageNumber = request.PaginationState?.PageNumber ?? 1,
|
||||
PageSize = request.PaginationState?.PageSize ?? 20,
|
||||
SearchTerm = request.SortBy
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
var response = new GetAllBlogCategoriesResponse
|
||||
{
|
||||
MetaData = result.MetaData.Adapt<ProtoMetaData>()
|
||||
};
|
||||
|
||||
foreach (var item in result.Models)
|
||||
response.Models.Add(MapToListItem(item));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<GetActiveBlogCategoriesResponse> GetActiveBlogCategories(GetActiveBlogCategoriesRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetActiveBlogCategoriesQuery();
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetActiveBlogCategoriesResponse();
|
||||
foreach (var item in result)
|
||||
response.Categories.Add(MapToListItem(item));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// ── Private Mapping Helpers ──
|
||||
|
||||
private static GetBlogCategoryResponse MapCategoryToResponse(BlogCategoryDto dto)
|
||||
{
|
||||
return new GetBlogCategoryResponse
|
||||
{
|
||||
Id = dto.Id,
|
||||
Title = dto.Title ?? string.Empty,
|
||||
Slug = dto.Slug ?? string.Empty,
|
||||
Description = dto.Description,
|
||||
IconName = dto.IconName,
|
||||
SortOrder = dto.SortOrder,
|
||||
IsActive = dto.IsActive,
|
||||
PostCount = dto.PostCount,
|
||||
Created = dto.Created != default ? Timestamp.FromDateTime(DateTime.SpecifyKind(dto.Created, DateTimeKind.Utc)) : null
|
||||
};
|
||||
}
|
||||
|
||||
private static BlogCategoryListItem MapToListItem(BlogCategoryDto dto)
|
||||
{
|
||||
return new BlogCategoryListItem
|
||||
{
|
||||
Id = dto.Id,
|
||||
Title = dto.Title ?? string.Empty,
|
||||
Slug = dto.Slug ?? string.Empty,
|
||||
Description = dto.Description,
|
||||
IconName = dto.IconName,
|
||||
SortOrder = dto.SortOrder,
|
||||
IsActive = dto.IsActive,
|
||||
PostCount = dto.PostCount
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Linq;
|
||||
using CMSMicroservice.Protobuf.Protos.BlogPostImage;
|
||||
using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.BlogPostImageCQ.Commands.AddBlogPostImage;
|
||||
using CMSMicroservice.Application.BlogPostImageCQ.Commands.DeleteBlogPostImage;
|
||||
using CMSMicroservice.Application.BlogPostImageCQ.Commands.ReorderBlogPostImages;
|
||||
using CMSMicroservice.Application.BlogPostImageCQ.Queries.GetBlogPostImages;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class BlogPostImageService : BlogPostImageContract.BlogPostImageContractBase
|
||||
{
|
||||
private readonly ISender _sender;
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
|
||||
public BlogPostImageService(ISender sender, IDispatchRequestToCQRS dispatchRequestToCQRS)
|
||||
{
|
||||
_sender = sender;
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
}
|
||||
|
||||
public override async Task<AddBlogPostImageResponse> AddBlogPostImage(AddBlogPostImageRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<AddBlogPostImageRequest, AddBlogPostImageCommand, AddBlogPostImageResponse>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<Empty> DeleteBlogPostImage(DeleteBlogPostImageRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<DeleteBlogPostImageRequest, DeleteBlogPostImageCommand>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<GetBlogPostImagesResponse> GetBlogPostImages(GetBlogPostImagesRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetBlogPostImagesQuery { BlogPostId = request.BlogPostId };
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetBlogPostImagesResponse();
|
||||
foreach (var item in result)
|
||||
{
|
||||
response.Images.Add(new BlogPostImageItem
|
||||
{
|
||||
Id = item.Id,
|
||||
BlogPostId = item.BlogPostId,
|
||||
ImagePath = item.ImagePath ?? string.Empty,
|
||||
ThumbnailPath = item.ThumbnailPath ?? string.Empty,
|
||||
AltText = item.AltText,
|
||||
Caption = item.Caption,
|
||||
SortOrder = item.SortOrder
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<Empty> ReorderBlogPostImages(ReorderBlogPostImagesRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new ReorderBlogPostImagesCommand
|
||||
{
|
||||
Items = request.Items.Select(x => new Application.BlogPostImageCQ.Commands.ReorderBlogPostImages.ImageSortItem
|
||||
{
|
||||
Id = x.Id,
|
||||
SortOrder = x.SortOrder
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CMSMicroservice.Protobuf.Protos.BlogPost;
|
||||
using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Commands.CreateBlogPost;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Commands.UpdateBlogPost;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Commands.DeleteBlogPost;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Commands.PublishBlogPost;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Commands.ArchiveBlogPost;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Commands.IncrementViewCount;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Queries.GetBlogPost;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Queries.GetBlogPostBySlug;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Queries.GetAllBlogPosts;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Queries.GetPublishedBlogPosts;
|
||||
using CMSMicroservice.Application.BlogPostCQ.Queries.GetFeaturedBlogPosts;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using Mapster;
|
||||
using MediatR;
|
||||
using AppModels = CMSMicroservice.Application.Common.Models;
|
||||
using ProtoMetaData = CMSMicroservice.Protobuf.Protos.MetaData;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class BlogPostService : BlogPostContract.BlogPostContractBase
|
||||
{
|
||||
private readonly ISender _sender;
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
|
||||
public BlogPostService(ISender sender, IDispatchRequestToCQRS dispatchRequestToCQRS)
|
||||
{
|
||||
_sender = sender;
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
}
|
||||
|
||||
public override async Task<CreateBlogPostResponse> CreateBlogPost(CreateBlogPostRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new CreateBlogPostCommand
|
||||
{
|
||||
Title = request.Title,
|
||||
Slug = request.Slug,
|
||||
Summary = request.Summary,
|
||||
HtmlContent = request.HtmlContent,
|
||||
FeaturedImagePath = request.FeaturedImagePath,
|
||||
FeaturedImageThumbnailPath = request.FeaturedImageThumbnailPath,
|
||||
CategoryIds = request.CategoryIds.ToList(),
|
||||
TagIds = request.TagIds.ToList(),
|
||||
IsFeatured = request.IsFeatured,
|
||||
SortOrder = request.SortOrder,
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName
|
||||
};
|
||||
|
||||
var result = await _sender.Send(command, context.CancellationToken);
|
||||
return new CreateBlogPostResponse { Id = result };
|
||||
}
|
||||
|
||||
public override async Task<Empty> UpdateBlogPost(UpdateBlogPostRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new UpdateBlogPostCommand
|
||||
{
|
||||
Id = request.Id,
|
||||
Title = request.Title,
|
||||
Slug = request.Slug,
|
||||
Summary = request.Summary,
|
||||
HtmlContent = request.HtmlContent,
|
||||
FeaturedImagePath = request.FeaturedImagePath,
|
||||
FeaturedImageThumbnailPath = request.FeaturedImageThumbnailPath,
|
||||
CategoryIds = request.CategoryIds.ToList(),
|
||||
TagIds = request.TagIds.ToList(),
|
||||
IsFeatured = request.IsFeatured,
|
||||
SortOrder = request.SortOrder,
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName
|
||||
};
|
||||
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
|
||||
public override async Task<Empty> DeleteBlogPost(DeleteBlogPostRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<DeleteBlogPostRequest, DeleteBlogPostCommand>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<GetBlogPostResponse> GetBlogPost(GetBlogPostRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetBlogPostQuery { Id = request.Id };
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
return MapBlogPostDtoToResponse(result);
|
||||
}
|
||||
|
||||
public override async Task<GetBlogPostResponse> GetBlogPostBySlug(GetBlogPostBySlugRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetBlogPostBySlugQuery { Slug = request.Slug };
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
return MapBlogPostDtoToResponse(result);
|
||||
}
|
||||
|
||||
public override async Task<GetAllBlogPostsResponse> GetAllBlogPosts(GetAllBlogPostsRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetAllBlogPostsQuery
|
||||
{
|
||||
PageNumber = request.PaginationState?.PageNumber ?? 1,
|
||||
PageSize = request.PaginationState?.PageSize ?? 10,
|
||||
SortBy = request.SortBy,
|
||||
SearchTerm = request.Filter?.SearchTerm,
|
||||
Status = request.Filter?.Status.HasValue == true ? (BlogPostStatus?)request.Filter.Status.Value : null,
|
||||
CategoryId = request.Filter?.CategoryId,
|
||||
IsFeatured = request.Filter?.IsFeatured
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
return MapAllBlogPostsResponse(result);
|
||||
}
|
||||
|
||||
public override async Task<GetAllBlogPostsResponse> GetPublishedBlogPosts(GetPublishedBlogPostsRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetPublishedBlogPostsQuery
|
||||
{
|
||||
PageNumber = request.PaginationState?.PageNumber ?? 1,
|
||||
PageSize = request.PaginationState?.PageSize ?? 10,
|
||||
SearchTerm = request.SearchTerm,
|
||||
CategoryId = request.CategoryId
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
return MapAllBlogPostsResponse(result);
|
||||
}
|
||||
|
||||
public override async Task<GetAllBlogPostsResponse> GetFeaturedBlogPosts(GetFeaturedBlogPostsRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetFeaturedBlogPostsQuery { Count = request.Count > 0 ? request.Count : 5 };
|
||||
var items = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetAllBlogPostsResponse();
|
||||
foreach (var item in items)
|
||||
response.Models.Add(MapToListItem(item));
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<PublishBlogPostResponse> PublishBlogPost(PublishBlogPostRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<PublishBlogPostRequest, PublishBlogPostCommand, PublishBlogPostResponse>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<ArchiveBlogPostResponse> ArchiveBlogPost(ArchiveBlogPostRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<ArchiveBlogPostRequest, ArchiveBlogPostCommand, ArchiveBlogPostResponse>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<Empty> IncrementViewCount(IncrementViewCountRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<IncrementViewCountRequest, IncrementViewCountCommand>(request, context);
|
||||
}
|
||||
|
||||
// ── Private Mapping Helpers ──
|
||||
|
||||
private static GetBlogPostResponse MapBlogPostDtoToResponse(BlogPostDto dto)
|
||||
{
|
||||
var response = new GetBlogPostResponse
|
||||
{
|
||||
Id = dto.Id,
|
||||
Title = dto.Title ?? string.Empty,
|
||||
Slug = dto.Slug ?? string.Empty,
|
||||
Summary = dto.Summary,
|
||||
HtmlContent = dto.HtmlContent ?? string.Empty,
|
||||
FeaturedImagePath = dto.FeaturedImagePath,
|
||||
FeaturedImageThumbnailPath = dto.FeaturedImageThumbnailPath,
|
||||
Status = (int)dto.Status,
|
||||
StatusName = dto.StatusName ?? string.Empty,
|
||||
ViewCount = dto.ViewCount,
|
||||
AuthorUserId = dto.AuthorUserId,
|
||||
IsFeatured = dto.IsFeatured,
|
||||
SortOrder = dto.SortOrder,
|
||||
Created = dto.Created != default ? Timestamp.FromDateTime(DateTime.SpecifyKind(dto.Created, DateTimeKind.Utc)) : null,
|
||||
LastModified = dto.LastModified.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(dto.LastModified.Value, DateTimeKind.Utc)) : null,
|
||||
PublishedAt = dto.PublishedAt.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(dto.PublishedAt.Value, DateTimeKind.Utc)) : null
|
||||
};
|
||||
|
||||
if (dto.Categories != null)
|
||||
foreach (var c in dto.Categories)
|
||||
response.Categories.Add(new BlogPostCategoryInfo { Id = c.Id, Title = c.Title ?? string.Empty, Slug = c.Slug ?? string.Empty });
|
||||
|
||||
if (dto.Tags != null)
|
||||
foreach (var t in dto.Tags)
|
||||
response.Tags.Add(new BlogPostTagInfo { Id = t.Id, Title = t.Title ?? string.Empty, Name = t.Name ?? string.Empty });
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static GetAllBlogPostsResponse MapAllBlogPostsResponse(GetAllBlogPostsResponseDto dto)
|
||||
{
|
||||
var response = new GetAllBlogPostsResponse
|
||||
{
|
||||
MetaData = dto.MetaData.Adapt<ProtoMetaData>()
|
||||
};
|
||||
|
||||
foreach (var item in dto.Models)
|
||||
response.Models.Add(MapToListItem(item));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static BlogPostListItem MapToListItem(BlogPostListItemDto item)
|
||||
{
|
||||
var listItem = new BlogPostListItem
|
||||
{
|
||||
Id = item.Id,
|
||||
Title = item.Title ?? string.Empty,
|
||||
Slug = item.Slug ?? string.Empty,
|
||||
Summary = item.Summary,
|
||||
FeaturedImageThumbnailPath = item.FeaturedImageThumbnailPath,
|
||||
Status = item.Status,
|
||||
StatusName = item.StatusName ?? string.Empty,
|
||||
ViewCount = item.ViewCount,
|
||||
IsFeatured = item.IsFeatured,
|
||||
Created = item.Created != default ? Timestamp.FromDateTime(DateTime.SpecifyKind(item.Created, DateTimeKind.Utc)) : null,
|
||||
PublishedAt = item.PublishedAt.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(item.PublishedAt.Value, DateTimeKind.Utc)) : null
|
||||
};
|
||||
|
||||
if (item.Categories != null)
|
||||
foreach (var c in item.Categories)
|
||||
listItem.Categories.Add(new BlogPostCategoryInfo { Id = c.Id, Title = c.Title ?? string.Empty, Slug = c.Slug ?? string.Empty });
|
||||
|
||||
return listItem;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountOrder;
|
||||
using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.PlaceOrder;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.CompleteOrderPayment;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateOrderStatus;
|
||||
@@ -7,21 +8,57 @@ using CMSMicroservice.Application.DiscountShopCQ.Queries.GetOrderById;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetUserOrders;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetAllDiscountOrders;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetDiscountSalesReport;
|
||||
using Grpc.Core;
|
||||
using Mapster;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class DiscountOrderService : DiscountOrderContract.DiscountOrderContractBase
|
||||
{
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
private readonly ISender _sender;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public DiscountOrderService(IDispatchRequestToCQRS dispatchRequestToCQRS)
|
||||
public DiscountOrderService(
|
||||
IDispatchRequestToCQRS dispatchRequestToCQRS,
|
||||
ISender sender,
|
||||
ICurrentUserService currentUserService)
|
||||
{
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
_sender = sender;
|
||||
_currentUserService = currentUserService;
|
||||
}
|
||||
|
||||
private long GetCurrentUserId()
|
||||
{
|
||||
if (long.TryParse(_currentUserService.UserId, out var uid) && uid > 0)
|
||||
return uid;
|
||||
throw new RpcException(new Status(StatusCode.Unauthenticated, "کاربر احراز هویت نشده است"));
|
||||
}
|
||||
|
||||
public override async Task<PlaceOrderResponse> PlaceOrder(PlaceOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<PlaceOrderRequest, PlaceOrderCommand, PlaceOrderResponse>(request, context);
|
||||
var command = new PlaceOrderCommand
|
||||
{
|
||||
UserId = GetCurrentUserId(),
|
||||
UserAddressId = request.UserAddressId,
|
||||
DiscountBalanceToUse = request.DiscountBalanceToUse
|
||||
};
|
||||
var result = await _sender.Send(command);
|
||||
|
||||
var response = new PlaceOrderResponse
|
||||
{
|
||||
Success = result.Success,
|
||||
Message = result.Message ?? string.Empty,
|
||||
OrderId = result.OrderId ?? 0,
|
||||
GatewayAmount = result.GatewayAmountRequired,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(result.PaymentUrl))
|
||||
response.PaymentUrl = result.PaymentUrl;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<CompleteOrderPaymentResponse> CompleteOrderPayment(CompleteOrderPaymentRequest request, ServerCallContext context)
|
||||
@@ -36,12 +73,23 @@ public class DiscountOrderService : DiscountOrderContract.DiscountOrderContractB
|
||||
|
||||
public override async Task<GetOrderByIdResponse> GetOrderById(GetOrderByIdRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetOrderByIdRequest, GetOrderByIdQuery, GetOrderByIdResponse>(request, context);
|
||||
var query = new GetOrderByIdQuery
|
||||
{
|
||||
OrderId = request.OrderId,
|
||||
UserId = GetCurrentUserId()
|
||||
};
|
||||
var result = await _sender.Send(query);
|
||||
return result.Adapt<GetOrderByIdResponse>();
|
||||
}
|
||||
|
||||
public override async Task<GetUserOrdersResponse> GetUserOrders(GetUserOrdersRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetUserOrdersRequest, GetUserOrdersQuery, GetUserOrdersResponse>(request, context);
|
||||
var query = new GetUserOrdersQuery
|
||||
{
|
||||
UserId = GetCurrentUserId()
|
||||
};
|
||||
var result = await _sender.Send(query);
|
||||
return result.Adapt<GetUserOrdersResponse>();
|
||||
}
|
||||
|
||||
public override async Task<GetAllDiscountOrdersResponse> GetAllDiscountOrders(GetAllDiscountOrdersRequest request, ServerCallContext context)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
|
||||
using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountProduct;
|
||||
@@ -10,26 +12,75 @@ using CMSMicroservice.Application.DiscountShopCQ.Commands.ReorderDiscountProduct
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetDiscountProductById;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetDiscountProducts;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetDiscountProductImages;
|
||||
using MediatR;
|
||||
using Mapster;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class DiscountProductService : DiscountProductContract.DiscountProductContractBase
|
||||
{
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
private readonly ISender _sender;
|
||||
|
||||
public DiscountProductService(IDispatchRequestToCQRS dispatchRequestToCQRS)
|
||||
public DiscountProductService(IDispatchRequestToCQRS dispatchRequestToCQRS, ISender sender)
|
||||
{
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
_sender = sender;
|
||||
}
|
||||
|
||||
public override async Task<CreateDiscountProductResponse> CreateDiscountProduct(CreateDiscountProductRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<CreateDiscountProductRequest, CreateDiscountProductCommand, CreateDiscountProductResponse>(request, context);
|
||||
var command = new CreateDiscountProductCommand
|
||||
{
|
||||
Title = request.Title,
|
||||
ShortInfomation = request.ShortInfomation,
|
||||
FullInformation = request.FullInformation,
|
||||
Price = request.Price,
|
||||
MaxDiscountPercent = request.MaxDiscountPercent,
|
||||
ImagePath = request.ImagePath,
|
||||
ThumbnailPath = request.ThumbnailPath,
|
||||
SortOrder = request.SortOrder,
|
||||
IsActive = request.IsActive,
|
||||
CategoryIds = request.CategoryIds?.ToList() ?? new List<long>(),
|
||||
// Map binary image data
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName,
|
||||
ThumbnailFileBytes = request.ThumbnailFile?.File?.ToByteArray(),
|
||||
ThumbnailFileMime = request.ThumbnailFile?.Mime,
|
||||
ThumbnailFileName = request.ThumbnailFile?.FileName
|
||||
};
|
||||
|
||||
var productId = await _sender.Send(command, context.CancellationToken);
|
||||
return new CreateDiscountProductResponse { ProductId = productId };
|
||||
}
|
||||
|
||||
public override async Task<Empty> UpdateDiscountProduct(UpdateDiscountProductRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<UpdateDiscountProductRequest, UpdateDiscountProductCommand>(request, context);
|
||||
var command = new UpdateDiscountProductCommand
|
||||
{
|
||||
ProductId = request.ProductId,
|
||||
Title = request.Title,
|
||||
ShortInfomation = request.ShortInfomation,
|
||||
FullInformation = request.FullInformation,
|
||||
Price = request.Price,
|
||||
MaxDiscountPercent = request.MaxDiscountPercent,
|
||||
ImagePath = request.ImagePath,
|
||||
ThumbnailPath = request.ThumbnailPath,
|
||||
SortOrder = request.SortOrder,
|
||||
IsActive = request.IsActive,
|
||||
CategoryIds = request.CategoryIds?.ToList() ?? new List<long>(),
|
||||
// Map binary image data
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName,
|
||||
ThumbnailFileBytes = request.ThumbnailFile?.File?.ToByteArray(),
|
||||
ThumbnailFileMime = request.ThumbnailFile?.Mime,
|
||||
ThumbnailFileName = request.ThumbnailFile?.FileName
|
||||
};
|
||||
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
|
||||
public override async Task<Empty> DeleteDiscountProduct(DeleteDiscountProductRequest request, ServerCallContext context)
|
||||
@@ -70,6 +121,11 @@ public class DiscountProductService : DiscountProductContract.DiscountProductCon
|
||||
|
||||
public override async Task<GetDiscountProductImagesResponse> GetDiscountProductImages(GetDiscountProductImagesRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetDiscountProductImagesRequest, GetDiscountProductImagesQuery, GetDiscountProductImagesResponse>(request, context);
|
||||
var query = request.Adapt<GetDiscountProductImagesQuery>();
|
||||
var images = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetDiscountProductImagesResponse();
|
||||
response.Images.AddRange(images.Select(i => i.Adapt<CMSMicroservice.Protobuf.Protos.DiscountProduct.DiscountProductImageDto>()));
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,111 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountShoppingCart;
|
||||
using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.AddToCart;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.RemoveFromCart;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateCartItemCount;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Commands.ClearCart;
|
||||
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetUserCart;
|
||||
using Grpc.Core;
|
||||
using Mapster;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class DiscountShoppingCartService : DiscountShoppingCartContract.DiscountShoppingCartContractBase
|
||||
{
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
private readonly ISender _sender;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public DiscountShoppingCartService(IDispatchRequestToCQRS dispatchRequestToCQRS)
|
||||
public DiscountShoppingCartService(
|
||||
IDispatchRequestToCQRS dispatchRequestToCQRS,
|
||||
ISender sender,
|
||||
ICurrentUserService currentUserService)
|
||||
{
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
_sender = sender;
|
||||
_currentUserService = currentUserService;
|
||||
}
|
||||
|
||||
private long GetCurrentUserId()
|
||||
{
|
||||
if (long.TryParse(_currentUserService.UserId, out var uid) && uid > 0)
|
||||
return uid;
|
||||
throw new RpcException(new Status(StatusCode.Unauthenticated, "کاربر احراز هویت نشده است"));
|
||||
}
|
||||
|
||||
public override async Task<AddToCartResponse> AddToCart(AddToCartRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<AddToCartRequest, AddToCartCommand, AddToCartResponse>(request, context);
|
||||
var command = new AddToCartCommand
|
||||
{
|
||||
UserId = GetCurrentUserId(),
|
||||
ProductId = request.ProductId,
|
||||
Count = request.Count
|
||||
};
|
||||
var result = await _sender.Send(command, context.CancellationToken);
|
||||
return result.Adapt<AddToCartResponse>();
|
||||
}
|
||||
|
||||
public override async Task<RemoveFromCartResponse> RemoveFromCart(RemoveFromCartRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<RemoveFromCartRequest, RemoveFromCartCommand, RemoveFromCartResponse>(request, context);
|
||||
var command = new RemoveFromCartCommand
|
||||
{
|
||||
UserId = GetCurrentUserId(),
|
||||
ProductId = request.ProductId
|
||||
};
|
||||
var result = await _sender.Send(command, context.CancellationToken);
|
||||
return result.Adapt<RemoveFromCartResponse>();
|
||||
}
|
||||
|
||||
public override async Task<UpdateCartItemCountResponse> UpdateCartItemCount(UpdateCartItemCountRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<UpdateCartItemCountRequest, UpdateCartItemCountCommand, UpdateCartItemCountResponse>(request, context);
|
||||
var command = new UpdateCartItemCountCommand
|
||||
{
|
||||
UserId = GetCurrentUserId(),
|
||||
ProductId = request.ProductId,
|
||||
NewCount = request.NewCount
|
||||
};
|
||||
var result = await _sender.Send(command, context.CancellationToken);
|
||||
return result.Adapt<UpdateCartItemCountResponse>();
|
||||
}
|
||||
|
||||
public override async Task<GetUserCartResponse> GetUserCart(GetUserCartRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetUserCartRequest, GetUserCartQuery, GetUserCartResponse>(request, context);
|
||||
var query = new GetUserCartQuery { UserId = GetCurrentUserId() };
|
||||
var cart = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetUserCartResponse
|
||||
{
|
||||
TotalPrice = cart.TotalAmount,
|
||||
TotalDiscountAmount = cart.MaxDiscountAmount,
|
||||
FinalPrice = cart.MinPayableAmount
|
||||
};
|
||||
|
||||
foreach (var item in cart.Items)
|
||||
{
|
||||
response.Items.Add(new CMSMicroservice.Protobuf.Protos.DiscountShoppingCart.CartItemDto
|
||||
{
|
||||
ProductId = item.ProductId,
|
||||
ProductTitle = item.ProductTitle ?? string.Empty,
|
||||
ProductImagePath = item.ProductImagePath ?? string.Empty,
|
||||
UnitPrice = item.UnitPrice,
|
||||
MaxDiscountPercent = item.MaxDiscountPercent,
|
||||
Count = item.Count,
|
||||
TotalPrice = item.SubTotal,
|
||||
DiscountAmount = item.MaxDiscountAmount,
|
||||
FinalPrice = item.MinPayable,
|
||||
ProductRemainingCount = item.RemainingStock
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<Empty> ClearCart(ClearCartRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<ClearCartRequest, ClearCartCommand>(request, context);
|
||||
var command = new ClearCartCommand { UserId = GetCurrentUserId() };
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using CMSMicroservice.Application.Common.FileManager;
|
||||
using CMSMicroservice.Protobuf.Protos.ImageResolver;
|
||||
using Grpc.Core;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// سرویس اختصاصی resolve تصاویر — مسیر نسبی را به base64 data-URI تبدیل میکند.
|
||||
/// FrontOffice از طریق این سرویس تمام تصاویر دینامیک را دریافت میکند.
|
||||
/// </summary>
|
||||
public class ImageResolverService : ImageResolverContract.ImageResolverContractBase
|
||||
{
|
||||
private readonly IFileManager _fileManager;
|
||||
|
||||
public ImageResolverService(IFileManager fileManager)
|
||||
{
|
||||
_fileManager = fileManager;
|
||||
}
|
||||
|
||||
public override Task<ResolveImagesResponse> ResolveImages(ResolveImagesRequest request, ServerCallContext context)
|
||||
{
|
||||
var response = new ResolveImagesResponse();
|
||||
|
||||
foreach (var path in request.Paths)
|
||||
{
|
||||
var dataUri = string.Empty;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
dataUri = _fileManager.ResolveImageUrl(path);
|
||||
}
|
||||
|
||||
response.Images.Add(new ResolvedImage
|
||||
{
|
||||
OriginalPath = path ?? string.Empty,
|
||||
DataUri = dataUri ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
@@ -178,6 +178,7 @@ public class ProductsService : ProductsContract.ProductsContractBase
|
||||
CategoryIds = request.Filter?.CategoryId.HasValue == true
|
||||
? new List<long> { request.Filter.CategoryId.Value }
|
||||
: new List<long>(),
|
||||
IsActive = request.Filter?.IsActive,
|
||||
SortBy = request.SortBy ?? string.Empty,
|
||||
PaginationState = request.PaginationState != null
|
||||
? new AppModels.PaginationState
|
||||
@@ -216,7 +217,8 @@ public class ProductsService : ProductsContract.ProductsContractBase
|
||||
SaleCount = m.SaleCount,
|
||||
ViewCount = m.ViewCount,
|
||||
RemainingCount = m.RemainingCount,
|
||||
CategoryIds = { m.Categories?.Select(c => c.CategoryId) ?? Enumerable.Empty<long>() }
|
||||
CategoryIds = { m.Categories?.Select(c => c.CategoryId) ?? Enumerable.Empty<long>() },
|
||||
IsActive = m.IsActive
|
||||
}) }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
using System.Linq;
|
||||
using CMSMicroservice.Protobuf.Protos.SitePage;
|
||||
using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.SitePageCQ.Commands.CreateSitePage;
|
||||
using CMSMicroservice.Application.SitePageCQ.Commands.DeleteSitePage;
|
||||
using CMSMicroservice.Application.SitePageCQ.Commands.UpdateSitePage;
|
||||
using CMSMicroservice.Application.SitePageCQ.Commands.CreateSitePageSection;
|
||||
using CMSMicroservice.Application.SitePageCQ.Commands.UpdateSitePageSection;
|
||||
using CMSMicroservice.Application.SitePageCQ.Commands.DeleteSitePageSection;
|
||||
using CMSMicroservice.Application.SitePageCQ.Commands.ReorderSitePageSections;
|
||||
using CMSMicroservice.Application.SitePageCQ.Queries.GetSitePage;
|
||||
using CMSMicroservice.Application.SitePageCQ.Queries.GetSitePageByKey;
|
||||
using CMSMicroservice.Application.SitePageCQ.Queries.GetAllSitePages;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class SitePageService : SitePageContract.SitePageContractBase
|
||||
{
|
||||
private readonly ISender _sender;
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
|
||||
public SitePageService(ISender sender, IDispatchRequestToCQRS dispatchRequestToCQRS)
|
||||
{
|
||||
_sender = sender;
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
}
|
||||
|
||||
public override async Task<GetSitePageResponse> GetSitePage(GetSitePageRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetSitePageQuery { Id = request.Id };
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
return MapToResponse(result);
|
||||
}
|
||||
|
||||
public override async Task<GetSitePageResponse> GetSitePageByKey(GetSitePageByKeyRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetSitePageByKeyQuery { PageKey = request.PageKey };
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
return MapToResponse(result);
|
||||
}
|
||||
|
||||
public override async Task<Empty> UpdateSitePage(UpdateSitePageRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new UpdateSitePageCommand
|
||||
{
|
||||
Id = request.Id,
|
||||
Title = request.Title,
|
||||
MetaDescription = request.MetaDescription,
|
||||
HeroTitle = request.HeroTitle,
|
||||
HeroSubtitle = request.HeroSubtitle,
|
||||
HeroImagePath = request.HeroImagePath,
|
||||
IsActive = request.IsActive,
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName
|
||||
};
|
||||
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
|
||||
public override async Task<CreateSitePageResponse> CreateSitePage(CreateSitePageRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new CreateSitePageCommand
|
||||
{
|
||||
PageKey = request.PageKey,
|
||||
Title = request.Title,
|
||||
MetaDescription = request.MetaDescription,
|
||||
HeroTitle = request.HeroTitle,
|
||||
HeroSubtitle = request.HeroSubtitle,
|
||||
IsActive = request.IsActive,
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName
|
||||
};
|
||||
|
||||
var id = await _sender.Send(command, context.CancellationToken);
|
||||
return new CreateSitePageResponse { Id = id };
|
||||
}
|
||||
|
||||
public override async Task<Empty> DeleteSitePage(DeleteSitePageRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new DeleteSitePageCommand { Id = request.Id };
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
|
||||
public override async Task<GetAllSitePagesResponse> GetAllSitePages(GetAllSitePagesRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = new GetAllSitePagesQuery();
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetAllSitePagesResponse();
|
||||
foreach (var item in result)
|
||||
{
|
||||
response.Pages.Add(new SitePageSummary
|
||||
{
|
||||
Id = item.Id,
|
||||
PageKey = item.PageKey ?? string.Empty,
|
||||
Title = item.Title ?? string.Empty,
|
||||
IsActive = item.IsActive,
|
||||
SectionCount = item.SectionCount,
|
||||
LastModified = item.LastModified.HasValue
|
||||
? Timestamp.FromDateTime(DateTime.SpecifyKind(item.LastModified.Value, DateTimeKind.Utc))
|
||||
: null
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<CreateSitePageSectionResponse> CreateSitePageSection(CreateSitePageSectionRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new CreateSitePageSectionCommand
|
||||
{
|
||||
SitePageId = request.SitePageId,
|
||||
SectionKey = request.SectionKey,
|
||||
Title = request.Title,
|
||||
Subtitle = request.Subtitle,
|
||||
HtmlContent = request.HtmlContent,
|
||||
IconName = request.IconName,
|
||||
ImagePath = request.ImagePath,
|
||||
IsActive = true,
|
||||
ExtraData = request.ExtraData,
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName
|
||||
};
|
||||
|
||||
var result = await _sender.Send(command, context.CancellationToken);
|
||||
return new CreateSitePageSectionResponse { Id = result };
|
||||
}
|
||||
|
||||
public override async Task<Empty> UpdateSitePageSection(UpdateSitePageSectionRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new UpdateSitePageSectionCommand
|
||||
{
|
||||
Id = request.Id,
|
||||
SectionKey = request.SectionKey,
|
||||
Title = request.Title,
|
||||
Subtitle = request.Subtitle,
|
||||
HtmlContent = request.HtmlContent,
|
||||
IconName = request.IconName,
|
||||
ImagePath = request.ImagePath,
|
||||
SortOrder = request.SortOrder,
|
||||
IsActive = request.IsActive,
|
||||
ExtraData = request.ExtraData,
|
||||
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||
ImageFileMime = request.ImageFile?.Mime,
|
||||
ImageFileName = request.ImageFile?.FileName
|
||||
};
|
||||
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
|
||||
public override async Task<Empty> DeleteSitePageSection(DeleteSitePageSectionRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<DeleteSitePageSectionRequest, DeleteSitePageSectionCommand>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<Empty> ReorderSitePageSections(ReorderSitePageSectionsRequest request, ServerCallContext context)
|
||||
{
|
||||
var command = new ReorderSitePageSectionsCommand
|
||||
{
|
||||
Items = request.Items.Select(x => new Application.SitePageCQ.Commands.ReorderSitePageSections.SectionSortItem
|
||||
{
|
||||
Id = x.Id,
|
||||
SortOrder = x.SortOrder
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
await _sender.Send(command, context.CancellationToken);
|
||||
return new Empty();
|
||||
}
|
||||
|
||||
// ── Private Mapping Helpers ──
|
||||
|
||||
private static GetSitePageResponse MapToResponse(SitePageDto dto)
|
||||
{
|
||||
var response = new GetSitePageResponse
|
||||
{
|
||||
Id = dto.Id,
|
||||
PageKey = dto.PageKey ?? string.Empty,
|
||||
Title = dto.Title ?? string.Empty,
|
||||
MetaDescription = dto.MetaDescription,
|
||||
HeroTitle = dto.HeroTitle,
|
||||
HeroSubtitle = dto.HeroSubtitle,
|
||||
HeroImagePath = dto.HeroImagePath,
|
||||
IsActive = dto.IsActive
|
||||
};
|
||||
|
||||
if (dto.Sections != null)
|
||||
{
|
||||
foreach (var s in dto.Sections)
|
||||
{
|
||||
response.Sections.Add(new SitePageSectionItem
|
||||
{
|
||||
Id = s.Id,
|
||||
SectionKey = s.SectionKey ?? string.Empty,
|
||||
Title = s.Title ?? string.Empty,
|
||||
Subtitle = s.Subtitle,
|
||||
HtmlContent = s.HtmlContent,
|
||||
IconName = s.IconName,
|
||||
ImagePath = s.ImagePath,
|
||||
SortOrder = s.SortOrder,
|
||||
IsActive = s.IsActive,
|
||||
ExtraData = s.ExtraData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -167,14 +167,14 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
{
|
||||
UserId = request.Filter?.UserId ?? 0, // 0 means all users (admin view)
|
||||
PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
|
||||
PaymentStatusFilter = request.Filter?.PaymentStatus != null
|
||||
PaymentStatusFilter = request.Filter?.HasPaymentStatus == true
|
||||
? (int?)request.Filter.PaymentStatus
|
||||
: null,
|
||||
DeliveryStatusFilter = request.Filter?.DeliveryStatus != null
|
||||
DeliveryStatusFilter = request.Filter?.HasDeliveryStatus == true
|
||||
? (int?)request.Filter.DeliveryStatus
|
||||
: null,
|
||||
FromDate = request.Filter?.PaymentDate?.ToDateTime(),
|
||||
ToDate = null
|
||||
FromDate = request.Filter?.FromDate?.ToDateTime() ?? request.Filter?.PaymentDate?.ToDateTime(),
|
||||
ToDate = request.Filter?.ToDate?.ToDateTime()
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
@@ -261,7 +261,14 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
|
||||
if (defaultAddress == null)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.FailedPrecondition, "آدرس پیشفرض یافت نشد"));
|
||||
// Check if user has any address at all
|
||||
var hasAnyAddress = await _context.UserAddresses
|
||||
.AnyAsync(a => a.UserId == userId && !a.IsDeleted, context.CancellationToken);
|
||||
|
||||
throw new RpcException(new Status(StatusCode.FailedPrecondition,
|
||||
hasAnyAddress
|
||||
? "لطفاً یک آدرس را به عنوان پیشفرض انتخاب کنید."
|
||||
: "آدرسی ثبت نشده است. لطفاً ابتدا یک آدرس اضافه کنید."));
|
||||
}
|
||||
|
||||
// Calculate amounts
|
||||
@@ -591,14 +598,14 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
{
|
||||
UserId = customerUserId,
|
||||
PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
|
||||
PaymentStatusFilter = request.Filter?.PaymentStatus != null
|
||||
PaymentStatusFilter = request.Filter?.HasPaymentStatus == true
|
||||
? (int?)request.Filter.PaymentStatus
|
||||
: null,
|
||||
DeliveryStatusFilter = request.Filter?.DeliveryStatus != null
|
||||
DeliveryStatusFilter = request.Filter?.HasDeliveryStatus == true
|
||||
? (int?)request.Filter.DeliveryStatus
|
||||
: null,
|
||||
FromDate = request.Filter?.PaymentDate?.ToDateTime(),
|
||||
ToDate = null
|
||||
FromDate = request.Filter?.FromDate?.ToDateTime() ?? request.Filter?.PaymentDate?.ToDateTime(),
|
||||
ToDate = request.Filter?.ToDate?.ToDateTime()
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
@@ -34,7 +34,7 @@ public class UserService : UserContract.UserContractBase
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
private readonly IHashService _hashService;
|
||||
private readonly IFileManagementService _fileManagementService;
|
||||
private readonly CMSMicroservice.Application.Common.FileManager.IFileManager _fileManager;
|
||||
|
||||
public UserService(
|
||||
IDispatchRequestToCQRS dispatchRequestToCQRS,
|
||||
@@ -42,14 +42,14 @@ public class UserService : UserContract.UserContractBase
|
||||
IApplicationDbContext context,
|
||||
ICurrentUserService currentUserService,
|
||||
IHashService hashService,
|
||||
IFileManagementService fileManagementService)
|
||||
CMSMicroservice.Application.Common.FileManager.IFileManager fileManager)
|
||||
{
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
_sender = sender;
|
||||
_context = context;
|
||||
_currentUserService = currentUserService;
|
||||
_hashService = hashService;
|
||||
_fileManagementService = fileManagementService;
|
||||
_fileManager = fileManager;
|
||||
}
|
||||
public override async Task<CreateNewUserResponse> CreateNewUser(CreateNewUserRequest request, ServerCallContext context)
|
||||
{
|
||||
@@ -65,6 +65,10 @@ public class UserService : UserContract.UserContractBase
|
||||
}
|
||||
public override async Task<GetUserResponse> GetUser(GetUserRequest request, ServerCallContext context)
|
||||
{
|
||||
// اگر Id ارسال نشده، از JWT بخون (برای کلاینت مشتری)
|
||||
if (request.Id == 0)
|
||||
request.Id = GetCurrentUserId();
|
||||
|
||||
return await _dispatchRequestToCQRS.Handle<GetUserRequest, GetUserQuery, GetUserResponse>(request, context);
|
||||
}
|
||||
public override async Task<GetAllUserByFilterResponse> GetAllUserByFilter(GetAllUserByFilterRequest request, ServerCallContext context)
|
||||
@@ -335,15 +339,28 @@ public class UserService : UserContract.UserContractBase
|
||||
if (user == null)
|
||||
throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد"));
|
||||
|
||||
// Upload to FMS
|
||||
// Upload to local file manager
|
||||
var fileBytes = request.FileData.ToByteArray();
|
||||
var fileName = $"avatar_{userId}_{DateTime.UtcNow.Ticks}";
|
||||
var mime = request.FileMimeType ?? "image/jpeg";
|
||||
|
||||
var avatarUrl = await _fileManagementService.UploadFileAsync(
|
||||
"Avatars", fileBytes, mime, fileName, context.CancellationToken);
|
||||
|
||||
if (string.IsNullOrEmpty(avatarUrl))
|
||||
try
|
||||
{
|
||||
var result = await _fileManager.UploadImageAsync(
|
||||
"Avatars", fileBytes, mime, fileName, context.CancellationToken);
|
||||
|
||||
// Update user avatar path in DB
|
||||
user.AvatarPath = result.Main.Path;
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
return new UploadCustomerAvatarResponse
|
||||
{
|
||||
Success = true,
|
||||
Message = "تصویر پروفایل با موفقیت آپلود شد",
|
||||
AvatarUrl = result.Main.Path
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new UploadCustomerAvatarResponse
|
||||
{
|
||||
@@ -351,17 +368,6 @@ public class UserService : UserContract.UserContractBase
|
||||
Message = "خطا در آپلود فایل. لطفاً مجدد تلاش کنید"
|
||||
};
|
||||
}
|
||||
|
||||
// Update user avatar path in DB
|
||||
user.AvatarPath = avatarUrl;
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
return new UploadCustomerAvatarResponse
|
||||
{
|
||||
Success = true,
|
||||
Message = "تصویر پروفایل با موفقیت آپلود شد",
|
||||
AvatarUrl = avatarUrl
|
||||
};
|
||||
}
|
||||
|
||||
public override async Task<GetCustomerSettingsResponse> GetCustomerSettings(GetCustomerSettingsRequest request, ServerCallContext context)
|
||||
|
||||
Reference in New Issue
Block a user