Compare commits

..

1 Commits

14 changed files with 25 additions and 257 deletions

View File

@@ -1,15 +0,0 @@
name: Build e Deploy
on:
push:
branches: [ "main", "master" ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Chamar Webhook do Coolify
run: |
curl -X GET "${{ secrets.DUCK_WEBHOOK }}" \
-H "Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}"

28
pom.xml
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -10,7 +10,7 @@
</parent> </parent>
<groupId>br.com.stackpanel</groupId> <groupId>br.com.stackpanel</groupId>
<artifactId>duck-api</artifactId> <artifactId>duck-api</artifactId>
<version>0.3.3-BETA</version> <version>0.0.1-SNAPSHOT</version>
<name/> <name/>
<description/> <description/>
<url/> <url/>
@@ -29,31 +29,7 @@
<properties> <properties>
<java.version>21</java.version> <java.version>21</java.version>
</properties> </properties>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies> <dependencies>
<dependency>
<groupId>com.github.eduardomcb</groupId>
<artifactId>discord-webhook</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>

View File

@@ -1,23 +0,0 @@
package br.com.stackpanel.duck_api.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static br.com.stackpanel.duck_api.entity.enums.QueueNames.QUEUE_NOTIFICATION_CAD;
import static br.com.stackpanel.duck_api.entity.enums.QueueNames.QUEUE_NOTIFICATION_ERROR;
@Configuration
public class RabbitConfig {
@Bean
public Queue queueNotificationError() {
return new Queue(QUEUE_NOTIFICATION_ERROR.label, true);
}
@Bean
public Queue queueNotificationCad() {
return new Queue(QUEUE_NOTIFICATION_CAD.label, true);
}
}

View File

@@ -1,53 +0,0 @@
package br.com.stackpanel.duck_api.config;
import br.com.stackpanel.duck_api.entity.enums.TypeError;
import br.com.stackpanel.duck_api.service.impl.ProductServiceImpl;
import com.eduardomcb.discord.webhook.WebhookClient;
import com.eduardomcb.discord.webhook.WebhookManager;
import com.eduardomcb.discord.webhook.models.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class WebHookConfig {
private final String LOG_ERROR = "https://discord.com/api/webhooks/1510367769839800541/ikEQ4mnp8GBtSltnn5qwhOBawlBC8EkVfff-zFlDhBpyuTBH_-7DM0LWhEoHXNl3sHSu";
private final String LOG_PRECO = "https://discord.com/api/webhooks/1510368038589829140/BIkXq8-n3ZxJBv5vEYrqvu7Agf-v-dz1P1RoLHmJ7J9IkJQhVWbA_hQyWKagC2ZPzPcV";
private final String LOG_CADASTRO = "https://discord.com/api/webhooks/1510368356354490438/mYfx6EKfQk-MdOxbRj0rcvr49Z4MGfDHfeeHeneDWoMuTLGYIpjBpUzwi1vZwf5VA-pW";
private final Logger log = LoggerFactory.getLogger(ProductServiceImpl.class);
public void SendWebHook(String message, TypeError typeError){
Message msgwh = new Message().setContent(message);
String urlWebHook = switch (typeError) {
case LOG_ERROR -> LOG_ERROR;
case LOG_PRECO -> LOG_PRECO;
case LOG_CADASTRO -> LOG_CADASTRO;
};
WebhookManager webhookManager = new WebhookManager()
.setChannelUrl(urlWebHook)
.setMessage(msgwh);
webhookManager.setListener(
new WebhookClient.Callback() {
@Override
public void onSuccess(String response) {
log.info("Mensagem enviada com sucesso!");
}
@Override
public void onFailure(int statusCode, String errorMessage) {
log.error("Ouve uma falha ao enviar mensagem error: '{}' status: '{}'", errorMessage, statusCode);
}
}
);
webhookManager.exec();
}
}

View File

@@ -35,13 +35,10 @@ public class Product {
@Column(name = "nomplt") @Column(name = "nomplt")
private String nomePlataforma; private String nomePlataforma;
@Column(name = "lnkprd")
private String linkProduto;
public Product() { public Product() {
} }
public Product(Long codigoProduto, String nomeProduto, String descricaoProdutos, BigDecimal precoProdutoAtualizado, BigDecimal precoProdutoPrecoAnterior, LocalDateTime ultimaDataConsultaProduto, LocalDateTime dataEnvioProduto, String nomePlataforma, String linkProduto) { public Product(Long codigoProduto, String nomeProduto, String descricaoProdutos, BigDecimal precoProdutoAtualizado, BigDecimal precoProdutoPrecoAnterior, LocalDateTime ultimaDataConsultaProduto, LocalDateTime dataEnvioProduto, String nomePlataforma) {
this.codigoProduto = codigoProduto; this.codigoProduto = codigoProduto;
this.nomeProduto = nomeProduto; this.nomeProduto = nomeProduto;
this.descricaoProdutos = descricaoProdutos; this.descricaoProdutos = descricaoProdutos;
@@ -50,15 +47,6 @@ public class Product {
this.ultimaDataConsultaProduto = ultimaDataConsultaProduto; this.ultimaDataConsultaProduto = ultimaDataConsultaProduto;
this.dataEnvioProduto = dataEnvioProduto; this.dataEnvioProduto = dataEnvioProduto;
this.nomePlataforma = nomePlataforma; this.nomePlataforma = nomePlataforma;
this.linkProduto = linkProduto;
}
public String getLinkProduto() {
return linkProduto;
}
public void setLinkProduto(String linkProduto) {
this.linkProduto = linkProduto;
} }
public String getDescricaoProdutos() { public String getDescricaoProdutos() {

View File

@@ -26,12 +26,10 @@ public class ProductDTO {
@NotNull @NotNull
private String nomePlataforma; private String nomePlataforma;
private String linkProduto;
public ProductDTO() { public ProductDTO() {
} }
public ProductDTO(Long codigoProduto, String nomeProduto, String descricaoProduto, BigDecimal precoProdutoAtualizado, BigDecimal precoProdutoPrecoAnterior, LocalDateTime ultimaDataConsultaProduto, LocalDateTime dataEnvioProduto, String nomePlataforma, String linkProduto) { public ProductDTO(Long codigoProduto, String nomeProduto, String descricaoProduto, BigDecimal precoProdutoAtualizado, BigDecimal precoProdutoPrecoAnterior, LocalDateTime ultimaDataConsultaProduto, LocalDateTime dataEnvioProduto, String nomePlataforma) {
this.codigoProduto = codigoProduto; this.codigoProduto = codigoProduto;
this.nomeProduto = nomeProduto; this.nomeProduto = nomeProduto;
this.descricaoProduto = descricaoProduto; this.descricaoProduto = descricaoProduto;
@@ -40,15 +38,6 @@ public class ProductDTO {
this.ultimaDataConsultaProduto = ultimaDataConsultaProduto; this.ultimaDataConsultaProduto = ultimaDataConsultaProduto;
this.dataEnvioProduto = dataEnvioProduto; this.dataEnvioProduto = dataEnvioProduto;
this.nomePlataforma = nomePlataforma; this.nomePlataforma = nomePlataforma;
this.linkProduto = linkProduto;
}
public String getLinkProduto() {
return linkProduto;
}
public void setLinkProduto(String linkProduto) {
this.linkProduto = linkProduto;
} }
public String getDescricaoProduto() { public String getDescricaoProduto() {

View File

@@ -1,15 +0,0 @@
package br.com.stackpanel.duck_api.entity.enums;
public enum QueueNames {
QUEUE_NOTIFICATION_ERROR("QUEUE_NOTIFICATION_ERROR"),
QUEUE_NOTIFICATION_CAD("QUEUE_NOTIFICATION_CAD");
public final String label;
private QueueNames(String label) {
this.label = label;
}
}

View File

@@ -1,8 +0,0 @@
package br.com.stackpanel.duck_api.entity.enums;
public enum TypeError {
LOG_ERROR,
LOG_PRECO,
LOG_CADASTRO;
}

View File

@@ -21,8 +21,7 @@ public class ProductMapper {
product.getPrecoProdutoPrecoAnterior(), product.getPrecoProdutoPrecoAnterior(),
product.getUltimaDataConsultaProduto(), product.getUltimaDataConsultaProduto(),
product.getDataEnvioProduto(), product.getDataEnvioProduto(),
product.getNomePlataforma(), product.getNomePlataforma()
product.getLinkProduto()
); );
} }
@@ -40,8 +39,7 @@ public class ProductMapper {
dto.getPrecoProdutoPrecoAnterior(), dto.getPrecoProdutoPrecoAnterior(),
dto.getUltimaDataConsultaProduto(), dto.getUltimaDataConsultaProduto(),
dto.getDataEnvioProduto(), dto.getDataEnvioProduto(),
dto.getNomePlataforma(), dto.getNomePlataforma()
dto.getLinkProduto()
); );
} }

View File

@@ -1,7 +0,0 @@
package br.com.stackpanel.duck_api.service;
public interface RabbitPublisherService {
void sendNotificationCad(String notificationCad);
}

View File

@@ -1,9 +1,7 @@
package br.com.stackpanel.duck_api.service.impl; package br.com.stackpanel.duck_api.service.impl;
import br.com.stackpanel.duck_api.config.WebHookConfig;
import br.com.stackpanel.duck_api.entity.Product; import br.com.stackpanel.duck_api.entity.Product;
import br.com.stackpanel.duck_api.entity.dto.ProductDTO; import br.com.stackpanel.duck_api.entity.dto.ProductDTO;
import br.com.stackpanel.duck_api.entity.enums.TypeError;
import br.com.stackpanel.duck_api.entity.mapper.ProductMapper; import br.com.stackpanel.duck_api.entity.mapper.ProductMapper;
import br.com.stackpanel.duck_api.exception.ProductInvalidException; import br.com.stackpanel.duck_api.exception.ProductInvalidException;
import br.com.stackpanel.duck_api.repository.ProductRepository; import br.com.stackpanel.duck_api.repository.ProductRepository;
@@ -12,8 +10,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
@Service @Service
@@ -23,12 +19,10 @@ public class ProductServiceImpl implements ProductService {
private final ProductRepository productRepository; private final ProductRepository productRepository;
private final ProductMapper productMapper; private final ProductMapper productMapper;
private final WebHookConfig webHookConfig;
public ProductServiceImpl(ProductRepository productRepository, ProductMapper productMapper, WebHookConfig webHookConfig) { public ProductServiceImpl(ProductRepository productRepository, ProductMapper productMapper) {
this.productRepository = productRepository; this.productRepository = productRepository;
this.productMapper = productMapper; this.productMapper = productMapper;
this.webHookConfig = webHookConfig;
} }
@Override @Override
@@ -40,8 +34,6 @@ public class ProductServiceImpl implements ProductService {
} catch (Exception e) { } catch (Exception e) {
log.error("Ocorreu um erro ao buscar produtos. Erro original: {}", e.getMessage()); log.error("Ocorreu um erro ao buscar produtos. Erro original: {}", e.getMessage());
webHookConfig.SendWebHook("<@&1510364267956928713> Ocorreu um erro ao buscar produtos. Erro original: " + e.getMessage(), TypeError.LOG_ERROR);
throw new RuntimeException("Ocorreu um erro interno ao buscar produtos."); throw new RuntimeException("Ocorreu um erro interno ao buscar produtos.");
} }
@@ -50,56 +42,38 @@ public class ProductServiceImpl implements ProductService {
@Override @Override
public ProductDTO salvarProduto(ProductDTO dto) { public ProductDTO salvarProduto(ProductDTO dto) {
try { try {
Product produtoExistente = productRepository.buscarProdutosFiltro(
null, dto.getNomeProduto(), null, null
);
Product produto; Product produtoExistente = productRepository.buscarProdutosFiltro(null, dto.getNomeProduto(), null, null);
if (produtoExistente != null) { if (produtoExistente != null) {
log.warn("Produto '{}' com ID: '{}' ja existe no sistema.", dto.getNomeProduto(), produtoExistente.getCodigoProduto()); log.warn("Produto '{}' ja existe no sistema.", dto.getNomeProduto());
BigDecimal precoAtual = produtoExistente.getPrecoProdutoAtualizado(); if (produtoExistente.getPrecoProdutoAtualizado().equals(dto.getPrecoProdutoAtualizado())) {
BigDecimal precoNovo = dto.getPrecoProdutoAtualizado(); log.info("Produto '{}' ja existe no sistema com o mesmo preco.", dto.getNomeProduto());
return dto;
} else if (dto.getPrecoProdutoAtualizado()
.compareTo(produtoExistente.getPrecoProdutoAtualizado()) < 0) {
log.info("Produto '{}' teve preco atualizado para {}. Preco anterior: {}",
dto.getNomeProduto(), dto.getPrecoProdutoAtualizado(),
produtoExistente.getPrecoProdutoAtualizado());
dto.setPrecoProdutoPrecoAnterior(produtoExistente.getPrecoProdutoAtualizado());
if (precoAtual.equals(precoNovo)) {
log.info("Produto '{}' ID: '{}' ja existe com o mesmo preco.", dto.getNomeProduto(), produtoExistente.getCodigoProduto());
return productMapper.toProductDTO(produtoExistente);
} else if (precoNovo.compareTo(precoAtual) < 0) {
log.info("Produto '{}' ID: '{}' teve preco atualizado: {} -> {}",
dto.getNomeProduto(), produtoExistente.getCodigoProduto(),precoAtual, precoNovo);
webHookConfig.SendWebHook("<@&1510364303776153803> O Produto " + dto.getNomeProduto() + " teve preco atualizado: " + precoAtual + " para " + precoNovo, TypeError.LOG_PRECO);
dto.setCodigoProduto(produtoExistente.getCodigoProduto());
dto.setPrecoProdutoPrecoAnterior(precoAtual);
produto = productRepository.save(productMapper.toProductEntity(dto));
} else {
log.info("Produto '{}' ID: '{}' ja existe com preco menor. Nenhuma alteracao feita.", dto.getNomeProduto(), produtoExistente.getCodigoProduto());
return productMapper.toProductDTO(produtoExistente);
} }
} else {
webHookConfig.SendWebHook("<@&1510364549717688460> O Produto " + dto.getNomeProduto() + " foi cadastrado com sucesso.", TypeError.LOG_CADASTRO);
produto = productRepository.save(productMapper.toProductEntity(dto));
} }
log.info("Produto salvo com sucesso. ID: {}, Nome: '{}'", produto.getCodigoProduto(), produto.getNomeProduto()); Product produto = productRepository.save(productMapper.toProductEntity(dto));
return productMapper.toProductDTO(produto);
log.info("Produto salvo com sucesso. ID: {}", produto.getCodigoProduto());
return productMapper.toProductDTO(produto);
} catch (DataIntegrityViolationException e) { } catch (DataIntegrityViolationException e) {
String message = e.getMostSpecificCause().getMessage(); String message = e.getMostSpecificCause().getMessage();
log.error("Erro de integridade ao salvar o produto '{}'. Motivo: {}", dto.getNomeProduto(), message);
webHookConfig.SendWebHook("<@&1510364267956928713> Erro de integridade ao salvar o produto: " + dto.getNomeProduto() + " Motivo: {}" + message, TypeError.LOG_ERROR); log.error("Erro de integridade ao salvar o produto '{}'. Motivo {}", dto.getNomeProduto(), message);
throw new ProductInvalidException("Os dados do produto sao invalidos ou ja existem no sistema."); throw new ProductInvalidException("Os dados do produto sao invalidos ou ja existem no sistema.");
} catch (Exception e) { } catch (Exception e) {
log.error("Erro inesperado ao salvar produto '{}'. Erro: {}", dto.getNomeProduto(), e.getMessage()); log.error("Erro inesperado ao salvar produto '{}'. Erro original {}", dto.getNomeProduto(), e.getMessage());
webHookConfig.SendWebHook("<@&1510364267956928713> Erro inesperado ao salvar produto: " + dto.getNomeProduto() + " Erro: " + e.getMessage(), TypeError.LOG_ERROR);
throw new RuntimeException("Ocorreu um erro interno ao salvar o produto."); throw new RuntimeException("Ocorreu um erro interno ao salvar o produto.");
} }

View File

@@ -1,24 +0,0 @@
package br.com.stackpanel.duck_api.service.impl;
import br.com.stackpanel.duck_api.service.RabbitPublisherService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import static br.com.stackpanel.duck_api.entity.enums.QueueNames.QUEUE_NOTIFICATION_ERROR;
public class RabbitPublisherServiceImpl implements RabbitPublisherService {
private static final Logger log = LoggerFactory.getLogger(RabbitPublisherServiceImpl.class);
private final RabbitTemplate rabbitTemplate;
public RabbitPublisherServiceImpl(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
@Override
public void sendNotificationCad(String notificationCad) {
log.info("Sending notification Cad: {}", notificationCad);
rabbitTemplate.convertAndSend(QUEUE_NOTIFICATION_ERROR.label, notificationCad);
}
}

View File

@@ -6,9 +6,3 @@ spring.datasource.username=${db_user}
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true spring.jpa.show-sql=true
spring.rabbitmq.host=rabbit.stackpanel.com.br
spring.rabbitmq.port=5672
spring.rabbitmq.username=${rabbit_username}
spring.rabbitmq.password=${rabbit_password}

View File

@@ -4,9 +4,3 @@ spring.datasource.url=${db_host:jdbc:postgresql://db.stackpanel.com.br:5432/nexu
spring.datasource.password=${db_pass} spring.datasource.password=${db_pass}
spring.datasource.username=${db_user} spring.datasource.username=${db_user}
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.driver-class-name=org.postgresql.Driver
spring.rabbitmq.host=rabbit.stackpanel.com.br
spring.rabbitmq.port=5672
spring.rabbitmq.username=${rabbit_username}
spring.rabbitmq.password=${rabbit_password}