Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ public PedidoAutorizadoIntegrationEvent(Guid clienteId, Guid pedidoId, IDictiona
Itens = itens;
}
}

}
16 changes: 16 additions & 0 deletions src/services/JSE.Catalogo.API/Configuration/MessageBusConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using JSE.Catalogo.API.Services;
using JSE.Core.Utils;
using JSE.MessageBus;

namespace JSE.Catalogo.API.Configuration
{
public static class MessageBusConfig
{
public static void AddMessageBusConfiguration(this IServiceCollection services,
IConfiguration configuration)
{
services.AddMessageBus(configuration.GetMessageQueueConnection("MessageBus"))
.AddHostedService<CatalogoIntegrationHandler>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ public CatalogoController(IProdutoRepository produtoRepository)
_produtoRepository = produtoRepository;
}

[AllowAnonymous]
[HttpGet("catalogo/produtos")]
public async Task<IEnumerable<Produto>> Index()
public async Task<PagedResult<Produto>> Index([FromQuery] int ps = 8, [FromQuery] int page = 1, [FromQuery] string q = null)
{
return await _produtoRepository.ObterTodos();
return await _produtoRepository.ObterTodos(ps, page, q);
}

[HttpGet("catalogo/produtos/{id}")]
Expand Down
35 changes: 28 additions & 7 deletions src/services/JSE.Catalogo.API/Data/Repository/ProdutoRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using JSE.Catalogo.API.Models;
using Dapper;
using JSE.Catalogo.API.Models;
using JSE.Core.Data;
using Microsoft.EntityFrameworkCore;

Expand All @@ -7,19 +8,39 @@ namespace JSE.Catalogo.API.Data.Repository
public class ProdutoRepository : IProdutoRepository
{
private readonly CatalogoContext _context;

public ProdutoRepository(CatalogoContext context)
{
_context = context;
}

public IUnitOfWork UnitOfWork => _context;

public async Task<IEnumerable<Produto>> ObterTodos()
public async Task<PagedResult<Produto>> ObterTodos(int pageSize, int pageIndex, string query = null)
{
return await _context.Produtos.AsNoTracking().ToListAsync();
}
var sql = @$"SELECT * FROM Produtos
WHERE (@Nome IS NULL OR Nome LIKE '%' + @Nome + '%')
ORDER BY [Nome]
OFFSET {pageSize * (pageIndex - 1)} ROWS
FETCH NEXT {pageSize} ROWS ONLY
SELECT COUNT(Id) FROM Produtos
WHERE (@Nome IS NULL OR Nome LIKE '%' + @Nome + '%')";

var multi = await _context.Database.GetDbConnection()
.QueryMultipleAsync(sql, new { Nome = query });

var produtos = multi.Read<Produto>();
var total = multi.Read<int>().FirstOrDefault();

return new PagedResult<Produto>()
{
List = produtos,
TotalResults = total,
PageIndex = pageIndex,
PageSize = pageSize,
Query = query
};
}

public async Task<List<Produto>> ObterProdutosPorId(string ids)
{
Expand All @@ -34,14 +55,14 @@ public async Task<List<Produto>> ObterProdutosPorId(string ids)
.Where(p => idsValue.Contains(p.Id) && p.Ativo).ToListAsync();
}

public async Task<Produto> ObterPorId(Guid id)
public async Task<Produto> ObterPorId(Guid id)
{
return await _context.Produtos.FindAsync(id);
}

public void Adicionar(Produto produto)
{
_context.Produtos.Add(produto);
_context.Produtos.Add(produto);
}

public void Atualizar(Produto produto)
Expand Down
2 changes: 2 additions & 0 deletions src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
Expand All @@ -28,6 +29,7 @@

<ItemGroup>
<ProjectReference Include="..\..\building blocks\JSE.Core\JSE.Core.csproj" />
<ProjectReference Include="..\..\building blocks\JSE.MessageBus\JSE.MessageBus.csproj" />
<ProjectReference Include="..\..\building blocks\JSE.WebAPI.Core\JSE.WebAPI.Core.csproj" />
<ProjectReference Include="..\JSE.Identidade.API\JSE.Identidade.API.csproj" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/services/JSE.Catalogo.API/Models/IProdutoRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace JSE.Catalogo.API.Models
{
public interface IProdutoRepository : IRepository<Produto>
{
Task<IEnumerable<Produto>> ObterTodos();
Task<PagedResult<Produto>> ObterTodos(int pageSize, int pageIndex, string query = null);
Task<Produto> ObterPorId(Guid id);
Task<List<Produto>> ObterProdutosPorId(string ids);
void Adicionar(Produto produto);
Expand Down
11 changes: 11 additions & 0 deletions src/services/JSE.Catalogo.API/Models/PagedResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace JSE.Catalogo.API.Models
{
public class PagedResult<T> where T : class
{
public IEnumerable<T> List { get; set; }
public int TotalResults { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
public string Query { get; set; }
}
}
13 changes: 12 additions & 1 deletion src/services/JSE.Catalogo.API/Models/Produto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ namespace JSE.Catalogo.API.Models
public class Produto : Entity, IAggregateRoot
{
public string Nome { get; set; }
public string Descricao { get; set; }
public string Descricao { get; set; }
public bool Ativo { get; set; }
public decimal Valor { get; set; }
public DateTime DataCadastro { get; set; }
public string Imagem { get; set; }
public int QuantidadeEstoque { get; set; }

public void RetirarEstoque(int quantidade)
{
if (QuantidadeEstoque >= quantidade)
QuantidadeEstoque -= quantidade;
}

public bool EstaDisponivel(int quantidade)
{
return Ativo && QuantidadeEstoque >= quantidade;
}
}
}
1 change: 1 addition & 0 deletions src/services/JSE.Catalogo.API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
.AddUserSecrets(typeof(Program).Assembly).Build();

builder.Services.AddApiConfiguration(configuration);
builder.Services.AddMessageBusConfiguration(configuration);
builder.Services.AddJwtConfiguration(configuration);
builder.Services.AddSwaggerConfiguration();
builder.Services.AddEndpointsApiExplorer();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using JSE.Catalogo.API.Models;
using JSE.Core.DomainObjects;
using JSE.Core.Messages.Integration;
using JSE.MessageBus;

namespace JSE.Catalogo.API.Services
{
public class CatalogoIntegrationHandler : BackgroundService
{
private readonly IMessageBus _bus;
private readonly IServiceProvider _serviceProvider;

public CatalogoIntegrationHandler(IServiceProvider serviceProvider, IMessageBus bus)
{
_serviceProvider = serviceProvider;
_bus = bus;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
SetSubscribers();
return Task.CompletedTask;
}

private void SetSubscribers()
{
_bus.SubscribeAsync<PedidoAutorizadoIntegrationEvent>("PedidoAutorizado", async request =>
await BaixarEstoque(request));
}

private async Task BaixarEstoque(PedidoAutorizadoIntegrationEvent message)
{
using (var scope = _serviceProvider.CreateScope())
{
var produtosComEstoque = new List<Produto>();
var produtoRepository = scope.ServiceProvider.GetRequiredService<IProdutoRepository>();

var idsProdutos = string.Join(",", message.Itens.Select(c => c.Key));
var produtos = await produtoRepository.ObterProdutosPorId(idsProdutos);

if (produtos.Count != message.Itens.Count)
{
CancelarPedidoSemEstoque(message);
return;
}

foreach (var produto in produtos)
{
var quantidadeProduto = message.Itens.FirstOrDefault(p => p.Key == produto.Id).Value;

if (produto.EstaDisponivel(quantidadeProduto))
{
produto.RetirarEstoque(quantidadeProduto);
produtosComEstoque.Add(produto);
}
}

if (produtosComEstoque.Count != message.Itens.Count)
{
CancelarPedidoSemEstoque(message);
return;
}

foreach (var produto in produtosComEstoque)
{
produtoRepository.Atualizar(produto);
}

if (!await produtoRepository.UnitOfWork.Commit())
{
throw new DomainException($"Problemas ao atualizar estoque do pedido {message.PedidoId}");
}

var pedidoBaixado = new PedidoBaixadoEstoqueIntegrationEvent(message.ClienteId, message.PedidoId);
await _bus.PublishAsync(pedidoBaixado);
}
}

public async void CancelarPedidoSemEstoque(PedidoAutorizadoIntegrationEvent message)
{
var pedidoCancelado = new PedidoCanceladoIntegrationEvent(message.ClienteId, message.PedidoId);
await _bus.PublishAsync(pedidoCancelado);
}
}
}
11 changes: 8 additions & 3 deletions src/services/JSE.Catalogo.API/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
{
"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=JeffStoreEnterprise;User Id=sa;Password=Asd123!!;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},

"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=JeffStoreEnterprise;User Id=sa;Password=Asd123!!;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=True;"
},

"MessageQueueConnection": {
"MessageBus": "host=localhost:5672;publisherConfirms=true;timeout=10"
},

"AppSettings": {
"Secret": "F9F52344-59C3-4EAC-90E6-CB47935038BE",
"ExpirationHours": 2,
Expand Down
69 changes: 69 additions & 0 deletions src/services/JSE.Pedido.API/Services/PedidoIntegrationHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using JSE.Core.DomainObjects;
using JSE.Core.Messages.Integration;
using JSE.MessageBus;
using JSE.Pedidos.Domain.Pedidos;

namespace JSE.Pedidos.API.Services
{
public class PedidoIntegrationHandler : BackgroundService
{
private readonly IMessageBus _bus;
private readonly IServiceProvider _serviceProvider;

public PedidoIntegrationHandler(IServiceProvider serviceProvider, IMessageBus bus)
{
_serviceProvider = serviceProvider;
_bus = bus;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
SetSubscribers();
return Task.CompletedTask;
}

private void SetSubscribers()
{
_bus.SubscribeAsync<PedidoCanceladoIntegrationEvent>("PedidoCancelado",
async request => await CancelarPedido(request));

_bus.SubscribeAsync<PedidoPagoIntegrationEvent>("PedidoPago",
async request => await FinalizarPedido(request));
}

private async Task CancelarPedido(PedidoCanceladoIntegrationEvent message)
{
using (var scope = _serviceProvider.CreateScope())
{
var pedidoRepository = scope.ServiceProvider.GetRequiredService<IPedidoRepository>();

var pedido = await pedidoRepository.ObterPorId(message.PedidoId);
pedido.CancelarPedido();

pedidoRepository.Atualizar(pedido);

if (!await pedidoRepository.UnitOfWork.Commit())
{
throw new DomainException($"Problemas ao cancelar o pedido {message.PedidoId}");
}
}
}

private async Task FinalizarPedido(PedidoPagoIntegrationEvent message)
{
using (var scope = _serviceProvider.CreateScope())
{
var pedidoRepository = scope.ServiceProvider.GetRequiredService<IPedidoRepository>();

var pedido = await pedidoRepository.ObterPorId(message.PedidoId);
pedido.FinalizarPedido();

pedidoRepository.Atualizar(pedido);

if (!await pedidoRepository.UnitOfWork.Commit())
{
throw new DomainException($"Problemas ao finalizar o pedido {message.PedidoId}");
}
}
}
}
}
10 changes: 10 additions & 0 deletions src/services/JSE.Pedido.Domain/Pedidos/Pedido.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ public void AutorizarPedido()
PedidoStatus = PedidoStatus.Autorizado;
}

public void CancelarPedido()
{
PedidoStatus = PedidoStatus.Cancelado;
}

public void FinalizarPedido()
{
PedidoStatus = PedidoStatus.Pago;
}

public void AtribuirVoucher(Voucher voucher)
{
VoucherUtilizado = true;
Expand Down
Loading