Files
CMS/src/CMSMicroservice.WebApi/Services/BlogCategoryService.cs
T
masoodafar-web 2502cbbda2
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m44s
feat: integrate PYMS payment gateway, add blog/sitepage/image services, local file manager
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)
2026-02-15 23:01:16 +03:30

117 lines
4.6 KiB
C#

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
};
}
}