From 67b43fea7a52b9b433d8aef38e2f9d746b61c937 Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Thu, 4 Dec 2025 19:53:21 +0330 Subject: [PATCH] feat: Implement Cancel Order functionality with command, handler, and validation --- .../GetWithdrawalRequestsQuery.cs | 5 ++ .../GetWithdrawalRequestsQueryHandler.cs | 5 ++ .../GetWithdrawalRequestsResponseDto.cs | 3 + .../GetProductTagsByProductQuery.cs | 20 ++++++ .../GetProductTagsByProductQueryHandler.cs | 46 +++++++++++++ .../CancelOrder/CancelOrderCommand.cs | 12 ++++ .../CancelOrder/CancelOrderCommandHandler.cs | 48 +++++++++++++ .../CancelOrderCommandValidator.cs | 67 +++++++++++++++++++ .../Common/Mappings/UserOrderProfile.cs | 8 ++- .../Services/UserOrderService.cs | 6 ++ .../Protos/commission.proto | 4 ++ .../Protos/userorder.proto | 22 ++++++ 12 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQuery.cs create mode 100644 src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommand.cs create mode 100644 src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandHandler.cs create mode 100644 src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandValidator.cs diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQuery.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQuery.cs index e6596f1..cccc337 100644 --- a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQuery.cs +++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQuery.cs @@ -21,4 +21,9 @@ public record GetWithdrawalRequestsQuery : IRequest public long? UserId { get; init; } + + /// + /// فیلتر شماره شبا / IBAN (اختیاری) + /// + public string? IbanNumber { get; init; } } diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs index 7acf864..054101e 100644 --- a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs +++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs @@ -29,6 +29,11 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler(); } diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsResponseDto.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsResponseDto.cs index e4cb043..d81fcc1 100644 --- a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsResponseDto.cs +++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsResponseDto.cs @@ -17,6 +17,9 @@ public record WithdrawalRequestDto public string Method { get; init; } = "Bank"; public string? BankAccount { get; init; } public string? BankName { get; init; } + public string? BankReferenceId { get; init; } + public string? BankTrackingCode { get; init; } + public string? PaymentFailureReason { get; init; } public DateTime RequestDate { get; init; } public DateTime? ProcessedDate { get; init; } public string? AdminNote { get; init; } diff --git a/src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQuery.cs b/src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQuery.cs new file mode 100644 index 0000000..3773ffe --- /dev/null +++ b/src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQuery.cs @@ -0,0 +1,20 @@ +using BackOffice.BFF.Application.ProductTagCQ.Queries.GetProductTagsByProduct; + +namespace BackOffice.BFF.Application.ProductTagCQ.Queries.GetProductTagsByProduct; + +public record GetProductTagsByProductQuery : IRequest +{ + public long ProductId { get; init; } +} + +public record ProductTagItemDto +{ + public long Id { get; init; } + public long TagId { get; init; } +} + +public record GetProductTagsByProductResponseDto +{ + public List Items { get; init; } = new(); +} + diff --git a/src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQueryHandler.cs b/src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQueryHandler.cs new file mode 100644 index 0000000..40a772d --- /dev/null +++ b/src/BackOffice.BFF.Application/ProductTagCQ/Queries/GetProductTagsByProduct/GetProductTagsByProductQueryHandler.cs @@ -0,0 +1,46 @@ +using BackOffice.BFF.Application.Common.Interfaces; +using CMSMicroservice.Protobuf.Protos.ProductTag; + +namespace BackOffice.BFF.Application.ProductTagCQ.Queries.GetProductTagsByProduct; + +public class GetProductTagsByProductQueryHandler : IRequestHandler +{ + private readonly IApplicationContractContext _context; + + public GetProductTagsByProductQueryHandler(IApplicationContractContext context) + { + _context = context; + } + + public async Task Handle(GetProductTagsByProductQuery request, CancellationToken cancellationToken) + { + var grpcRequest = new GetAllProductTagByFilterRequest + { + PaginationState = new CMSMicroservice.Protobuf.Protos.PaginationState + { + PageNumber = 1, + PageSize = 100 + }, + Filter = new GetAllProductTagByFilterFilter + { + ProductId = request.ProductId + } + }; + + var response = await _context.ProductTags.GetAllProductTagByFilterAsync(grpcRequest, cancellationToken: cancellationToken); + + var dto = new GetProductTagsByProductResponseDto + { + Items = response.Models + .Select(m => new ProductTagItemDto + { + Id = m.Id, + TagId = m.TagId + }) + .ToList() + }; + + return dto; + } +} + diff --git a/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommand.cs b/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommand.cs new file mode 100644 index 0000000..f06194a --- /dev/null +++ b/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommand.cs @@ -0,0 +1,12 @@ +using BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder; +using MediatR; + +namespace BackOffice.BFF.Application.UserOrderCQ.Commands.CancelOrder; + +public record CancelOrderCommand : IRequest +{ + public long OrderId { get; init; } + public string CancelReason { get; init; } = string.Empty; + public bool RefundPayment { get; init; } +} + diff --git a/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandHandler.cs b/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandHandler.cs new file mode 100644 index 0000000..a53610b --- /dev/null +++ b/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandHandler.cs @@ -0,0 +1,48 @@ +using BackOffice.BFF.Application.Common.Interfaces; +using BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder; +using CMSMicroservice.Protobuf.Protos.UserOrder; +using MediatR; +using Microsoft.Extensions.Logging; + +namespace BackOffice.BFF.Application.UserOrderCQ.Commands.CancelOrder; + +public class CancelOrderCommandHandler : IRequestHandler +{ + private readonly IApplicationContractContext _context; + private readonly ILogger _logger; + + public CancelOrderCommandHandler( + IApplicationContractContext context, + ILogger logger) + { + _context = context; + _logger = logger; + } + + public async Task Handle(CancelOrderCommand request, CancellationToken cancellationToken) + { + var grpcRequest = new CMSMicroservice.Protobuf.Protos.UserOrder.CancelOrderRequest + { + OrderId = request.OrderId, + CancelReason = request.CancelReason ?? string.Empty, + RefundPayment = request.RefundPayment + }; + + var response = await _context.UserOrders.CancelOrderAsync(grpcRequest, cancellationToken: cancellationToken); + + _logger.LogInformation( + "Cancelled order {OrderId}. Status={Status} RefundProcessed={RefundProcessed}", + response.OrderId, + response.Status, + response.RefundProcessed); + + return new CancelOrderResponse + { + OrderId = response.OrderId, + Status = (int)response.Status, + Message = response.Message, + RefundProcessed = response.RefundProcessed + }; + } +} + diff --git a/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandValidator.cs b/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandValidator.cs new file mode 100644 index 0000000..6ac18a2 --- /dev/null +++ b/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandValidator.cs @@ -0,0 +1,67 @@ +using FluentValidation; + +namespace BackOffice.BFF.Application.UserOrderCQ.Commands.CancelOrder; + +public class CancelOrderCommandValidator : AbstractValidator +{ + public CancelOrderCommandValidator() + { + RuleFor(x => x.OrderId) + .GreaterThan(0) + .WithMessage("شناسه سفارش باید بزرگتر از 0 باشد"); + + RuleFor(x => x.CancelReason) + .NotEmpty() + .WithMessage("دلیل لغو سفارش الزامی است"); + } +} + +*** Add File: BackOffice.BFF/src/BackOffice.BFF.Application/UserOrderCQ/Commands/CancelOrder/CancelOrderCommandHandler.cs +using BackOffice.BFF.Application.Common.Interfaces; +using BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder; +using CMSMicroservice.Protobuf.Protos.UserOrder; +using MediatR; +using Microsoft.Extensions.Logging; + +namespace BackOffice.BFF.Application.UserOrderCQ.Commands.CancelOrder; + +public class CancelOrderCommandHandler : IRequestHandler +{ + private readonly IApplicationContractContext _context; + private readonly ILogger _logger; + + public CancelOrderCommandHandler( + IApplicationContractContext context, + ILogger logger) + { + _context = context; + _logger = logger; + } + + public async Task Handle(CancelOrderCommand request, CancellationToken cancellationToken) + { + var grpcRequest = new CMSMicroservice.Protobuf.Protos.UserOrder.CancelOrderRequest + { + OrderId = request.OrderId, + CancelReason = request.CancelReason ?? string.Empty, + RefundPayment = request.RefundPayment + }; + + var response = await _context.UserOrders.CancelOrderAsync(grpcRequest, cancellationToken: cancellationToken); + + _logger.LogInformation( + "Cancelled order {OrderId}. Status={Status} RefundProcessed={RefundProcessed}", + response.OrderId, + response.Status, + response.RefundProcessed); + + return new CancelOrderResponse + { + OrderId = response.OrderId, + Status = (int)response.Status, + Message = response.Message, + RefundProcessed = response.RefundProcessed + }; + } +} + diff --git a/src/BackOffice.BFF.WebApi/Common/Mappings/UserOrderProfile.cs b/src/BackOffice.BFF.WebApi/Common/Mappings/UserOrderProfile.cs index 0c17bd7..b348550 100644 --- a/src/BackOffice.BFF.WebApi/Common/Mappings/UserOrderProfile.cs +++ b/src/BackOffice.BFF.WebApi/Common/Mappings/UserOrderProfile.cs @@ -3,6 +3,7 @@ namespace BackOffice.BFF.WebApi.Common.Mappings; using BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder; using BackOffice.BFF.Application.UserOrderCQ.Commands.UpdateOrderStatus; using BackOffice.BFF.Application.UserOrderCQ.Commands.ApplyDiscountToOrder; +using BackOffice.BFF.Application.UserOrderCQ.Commands.CancelOrder; using BackOffice.BFF.Application.UserOrderCQ.Queries.GetOrdersByDateRange; using BackOffice.BFF.Application.UserOrderCQ.Queries.CalculateOrderPV; using Google.Protobuf.WellKnownTypes; @@ -74,6 +75,11 @@ public class UserOrderProfile : IRegister .Map(dest => dest.Quantity, src => src.Quantity) .Map(dest => dest.UnitPv, src => src.UnitPV) .Map(dest => dest.TotalPv, src => src.TotalPV); + + // CancelOrder mappings + config.NewConfig() + .Map(dest => dest.OrderId, src => src.OrderId) + .Map(dest => dest.CancelReason, src => src.CancelReason) + .Map(dest => dest.RefundPayment, src => src.RefundPayment); } } - diff --git a/src/BackOffice.BFF.WebApi/Services/UserOrderService.cs b/src/BackOffice.BFF.WebApi/Services/UserOrderService.cs index ccd0434..16d877b 100644 --- a/src/BackOffice.BFF.WebApi/Services/UserOrderService.cs +++ b/src/BackOffice.BFF.WebApi/Services/UserOrderService.cs @@ -7,6 +7,7 @@ using BackOffice.BFF.Application.UserOrderCQ.Queries.GetUserOrder; using BackOffice.BFF.Application.UserOrderCQ.Queries.GetAllUserOrderByFilter; using BackOffice.BFF.Application.UserOrderCQ.Commands.UpdateOrderStatus; using BackOffice.BFF.Application.UserOrderCQ.Commands.ApplyDiscountToOrder; +using BackOffice.BFF.Application.UserOrderCQ.Commands.CancelOrder; using BackOffice.BFF.Application.UserOrderCQ.Queries.GetOrdersByDateRange; using BackOffice.BFF.Application.UserOrderCQ.Queries.CalculateOrderPV; namespace BackOffice.BFF.WebApi.Services; @@ -58,4 +59,9 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase { return await _dispatchRequestToCQRS.Handle(request, context); } + + public override async Task CancelOrder(CancelOrderRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } } diff --git a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto index 2faab23..3a754a3 100644 --- a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto +++ b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto @@ -254,6 +254,7 @@ message GetWithdrawalRequestsRequest google.protobuf.StringValue week_number = 3; int32 page_index = 4; int32 page_size = 5; + string iban_number = 6; } message GetWithdrawalRequestsResponse @@ -277,6 +278,9 @@ message WithdrawalRequestModel string processed_by = 11; string reason = 12; google.protobuf.Timestamp created = 13; + string bank_reference_id = 14; + string bank_tracking_code = 15; + string payment_failure_reason = 16; } // ============ Worker Control APIs ============ diff --git a/src/Protobufs/BackOffice.BFF.UserOrder.Protobuf/Protos/userorder.proto b/src/Protobufs/BackOffice.BFF.UserOrder.Protobuf/Protos/userorder.proto index c07d4ba..7c64da6 100644 --- a/src/Protobufs/BackOffice.BFF.UserOrder.Protobuf/Protos/userorder.proto +++ b/src/Protobufs/BackOffice.BFF.UserOrder.Protobuf/Protos/userorder.proto @@ -64,6 +64,12 @@ service UserOrderContract }; // Order Management + rpc CancelOrder(CancelOrderRequest) returns (CancelOrderResponse){ + option (google.api.http) = { + post: "/CancelOrder" + body: "*" + }; + }; rpc UpdateOrderStatus(UpdateOrderStatusRequest) returns (UpdateOrderStatusResponse){ option (google.api.http) = { post: "/UpdateOrderStatus" @@ -342,3 +348,19 @@ message DecimalValue sfixed32 nanos = 2; } + +// CancelOrder Messages +message CancelOrderRequest +{ + int64 order_id = 1; + string cancel_reason = 2; + bool refund_payment = 3; +} + +message CancelOrderResponse +{ + int64 order_id = 1; + int32 status = 2; + string message = 3; + bool refund_processed = 4; +}