订单的取消和发货命令
This commit is contained in:
parent
3cad5598b7
commit
923089b83b
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.Commands.Orders
|
||||||
|
{
|
||||||
|
public record CancelOrderCommand(int OrderNumber) : IRequest<bool>;
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.OrderingService.Entities.Orders;
|
||||||
|
using HelloShop.OrderingService.Infrastructure;
|
||||||
|
using HelloShop.OrderingService.Services;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.Commands.Orders
|
||||||
|
{
|
||||||
|
public class CancelOrderCommandHandler(OrderingServiceDbContext dbContext) : IRequestHandler<CancelOrderCommand, bool>
|
||||||
|
{
|
||||||
|
public async Task<bool> Handle(CancelOrderCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var orderToUpdate = await dbContext.Set<Order>().FindAsync([request.OrderNumber], cancellationToken);
|
||||||
|
|
||||||
|
if (orderToUpdate == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
orderToUpdate.OrderStatus = OrderStatus.Cancelled;
|
||||||
|
|
||||||
|
return await dbContext.SaveChangesAsync(cancellationToken) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CancelOrderIdentifiedCommandHandler(IMediator mediator, IClientRequestManager requestManager) : IdentifiedCommandHandler<CancelOrderCommand, bool>(mediator, requestManager)
|
||||||
|
{
|
||||||
|
protected override bool CreateResultForDuplicateRequest() => true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.Commands.Orders
|
||||||
|
{
|
||||||
|
public record ShipOrderCommand(int OrderNumber) : IRequest<bool>;
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.OrderingService.Entities.Orders;
|
||||||
|
using HelloShop.OrderingService.Infrastructure;
|
||||||
|
using HelloShop.OrderingService.Services;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.Commands.Orders
|
||||||
|
{
|
||||||
|
public class ShipOrderCommandHandler(OrderingServiceDbContext dbContext) : IRequestHandler<ShipOrderCommand, bool>
|
||||||
|
{
|
||||||
|
public async Task<bool> Handle(ShipOrderCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var orderToUpdate = await dbContext.Set<Order>().FindAsync([request.OrderNumber], cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
if (orderToUpdate == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
orderToUpdate.OrderStatus = OrderStatus.Shipped;
|
||||||
|
|
||||||
|
return await dbContext.SaveChangesAsync(cancellationToken) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ShipOrderIdentifiedCommandHandler(IMediator mediator, IClientRequestManager requestManager) : IdentifiedCommandHandler<ShipOrderCommand, bool>(mediator, requestManager)
|
||||||
|
{
|
||||||
|
protected override bool CreateResultForDuplicateRequest() => true;
|
||||||
|
}
|
||||||
|
}
|
@ -7,59 +7,95 @@ using HelloShop.OrderingService.Commands.Orders;
|
|||||||
using HelloShop.OrderingService.Models.Orders;
|
using HelloShop.OrderingService.Models.Orders;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
namespace HelloShop.OrderingService.Controllers
|
namespace HelloShop.OrderingService.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
public class OrdersController(ILogger<OrdersController> logger, IMediator mediator, IMapper mapper) : ControllerBase
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[HttpPost]
|
||||||
[ApiController]
|
public async Task<IActionResult> CreateOrder([FromHeader(Name = "x-request-id")] Guid requestId, CreateOrderRequest request)
|
||||||
[Authorize]
|
|
||||||
public class OrdersController(ILogger<OrdersController> logger, IMediator mediator, IMapper mapper) : ControllerBase
|
|
||||||
{
|
{
|
||||||
[HttpPost]
|
using (logger.BeginScope(new List<KeyValuePair<string, object>> { new("IdentifiedCommandId", requestId) }))
|
||||||
public async Task<IActionResult> CreateOrder([FromHeader(Name = "x-request-id")] Guid requestId, CreateOrderRequest request)
|
|
||||||
{
|
{
|
||||||
using (logger.BeginScope(new List<KeyValuePair<string, object>> { new("IdentifiedCommandId", requestId) }))
|
string? nameIdentifier = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
string? userName = User.FindFirst(ClaimTypes.Name)?.Value;
|
||||||
|
|
||||||
|
if (!int.TryParse(nameIdentifier, out int userId) || string.IsNullOrWhiteSpace(userName))
|
||||||
{
|
{
|
||||||
string? nameIdentifier = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
throw new InvalidOperationException("User id or name not found in claims.");
|
||||||
string? userName = User.FindFirst(ClaimTypes.Name)?.Value;
|
}
|
||||||
|
|
||||||
if (!int.TryParse(nameIdentifier, out int userId) || string.IsNullOrWhiteSpace(userName))
|
CreateOrderCommand createOrderCommand = mapper.Map<CreateOrderCommand>(request, opts => opts.AfterMap((src, dest) =>
|
||||||
|
{
|
||||||
|
dest.UserId = userId;
|
||||||
|
dest.UserName = userName;
|
||||||
|
}));
|
||||||
|
|
||||||
|
var createOrderIdentifiedCommand = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, requestId);
|
||||||
|
|
||||||
|
logger.LogInformation("Sending create order command.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await mediator.Send(createOrderIdentifiedCommand);
|
||||||
|
return result ? Ok() : Problem(detail: "Create order failed to process.", statusCode: 500);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ApplicationException ex) when (ex.InnerException is FluentValidation.ValidationException validationException)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Validation error in create order command.");
|
||||||
|
|
||||||
|
ModelStateDictionary modelState = validationException.Errors.Aggregate(ModelState, (acc, error) =>
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("User id or name not found in claims.");
|
acc.AddModelError(error.PropertyName, error.ErrorMessage);
|
||||||
}
|
return acc;
|
||||||
|
});
|
||||||
|
|
||||||
CreateOrderCommand createOrderCommand = mapper.Map<CreateOrderCommand>(request, opts => opts.AfterMap((src, dest) =>
|
return ValidationProblem(modelState);
|
||||||
{
|
|
||||||
dest.UserId = userId;
|
|
||||||
dest.UserName = userName;
|
|
||||||
}));
|
|
||||||
|
|
||||||
var createOrderIdentifiedCommand = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, requestId);
|
|
||||||
|
|
||||||
logger.LogInformation("Sending create order command.");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = await mediator.Send(createOrderIdentifiedCommand);
|
|
||||||
return result ? Ok() : Problem(detail: "Create order failed to process.", statusCode: 500);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (ApplicationException ex) when (ex.InnerException is FluentValidation.ValidationException validationException)
|
|
||||||
{
|
|
||||||
logger.LogWarning("Validation error in create order command.");
|
|
||||||
|
|
||||||
ModelStateDictionary modelState = validationException.Errors.Aggregate(ModelState, (acc, error) =>
|
|
||||||
{
|
|
||||||
acc.AddModelError(error.PropertyName, error.ErrorMessage);
|
|
||||||
return acc;
|
|
||||||
});
|
|
||||||
|
|
||||||
return ValidationProblem(modelState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPut("Cancel/{id}")]
|
||||||
|
public async Task<IActionResult> CancelOrder([FromHeader(Name = "x-request-id")] Guid requestId, int id)
|
||||||
|
{
|
||||||
|
CancelOrderCommand cancelOrderCommand = new(id);
|
||||||
|
|
||||||
|
var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(cancelOrderCommand, requestId);
|
||||||
|
|
||||||
|
var commandResult = await mediator.Send(requestCancelOrder);
|
||||||
|
|
||||||
|
if (!commandResult)
|
||||||
|
{
|
||||||
|
return Problem(detail: "Cancel order failed to process.", statusCode: 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("Ship/{id}")]
|
||||||
|
public async Task<IActionResult> ShipOrder([FromHeader(Name = "x-request-id")] Guid requestId, int id)
|
||||||
|
{
|
||||||
|
ShipOrderCommand shipOrderCommand = new(id);
|
||||||
|
|
||||||
|
var requestShipOrder = new IdentifiedCommand<ShipOrderCommand, bool>(shipOrderCommand, requestId);
|
||||||
|
|
||||||
|
var commandResult = await mediator.Send(requestShipOrder);
|
||||||
|
|
||||||
|
if (!commandResult)
|
||||||
|
{
|
||||||
|
return Problem(detail: "Ship order failed to process.", statusCode: 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ namespace HelloShop.OrderingService.Extensions
|
|||||||
options.AddBehavior(typeof(LoggingBehavior<,>));
|
options.AddBehavior(typeof(LoggingBehavior<,>));
|
||||||
options.AddBehavior(typeof(ValidatorBehavior<,>));
|
options.AddBehavior(typeof(ValidatorBehavior<,>));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AddModelMapper().AddModelValidator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebApplication MapApplicationEndpoints(this WebApplication app)
|
public static WebApplication MapApplicationEndpoints(this WebApplication app)
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using FluentValidation;
|
||||||
|
using HelloShop.OrderingService.Commands.Orders;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.Validations.Commands
|
||||||
|
{
|
||||||
|
public class CancelOrderCommandValidator : AbstractValidator<CancelOrderCommand>
|
||||||
|
{
|
||||||
|
public CancelOrderCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.OrderNumber).GreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using FluentValidation;
|
||||||
|
using HelloShop.OrderingService.Commands.Orders;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.Validations.Commands
|
||||||
|
{
|
||||||
|
public class ShipOrderCommandValidator : AbstractValidator<ShipOrderCommand>
|
||||||
|
{
|
||||||
|
public ShipOrderCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(order => order.OrderNumber).GreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user