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 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 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 DeleteBlogPost(DeleteBlogPostRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task 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 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 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 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 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 PublishBlogPost(PublishBlogPostRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task ArchiveBlogPost(ArchiveBlogPostRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task IncrementViewCount(IncrementViewCountRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(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() }; 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; } }