Skip to content
Open
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
103 changes: 103 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?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"
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ibk</groupId>
<artifactId>reto</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>reto</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.0</version>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>
20 changes: 20 additions & 0 deletions src/main/java/com/ibk/reto/RetoApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.ibk.reto;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
@SpringBootApplication
public class RetoApplication {

public static void main(String[] args) {
SpringApplication.run(RetoApplication.class, args);
}

}
22 changes: 22 additions & 0 deletions src/main/java/com/ibk/reto/configuration/JacksonConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.ibk.reto.configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper().registerModule(new JavaTimeModule());
}

}
31 changes: 31 additions & 0 deletions src/main/java/com/ibk/reto/controller/TransactionController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.ibk.reto.controller;

import com.ibk.reto.model.Transaction;
import com.ibk.reto.service.TransactionService;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
@RestController
@RequestMapping("/transactions")
public class TransactionController {
private final TransactionService service;
public TransactionController(TransactionService service) {
this.service = service;
}
@PostMapping
public Transaction createTransaction(@RequestBody Transaction transaction) {
return service.createTransaction(transaction);
}
@GetMapping("/{id}")
public Transaction getTransaction(@PathVariable UUID id) {
return service.getTransactionById(id);
}
}
45 changes: 45 additions & 0 deletions src/main/java/com/ibk/reto/kafka/KafkaConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.ibk.reto.kafka;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.ibk.reto.model.Transaction;
import com.ibk.reto.model.TransactionEstatus;
import com.ibk.reto.service.TransactionService;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
@Service
public class KafkaConsumer {
private final KafkaProducer kafkaProducer;
private final ObjectMapper objectMapper;

public KafkaConsumer(KafkaProducer kafkaProducer) {
this.kafkaProducer = kafkaProducer;
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule());
}

@KafkaListener(topics = "transaction_created", groupId = "antifraud-group")
public void processTransaction(ConsumerRecord<String, String> record) {
try {
Transaction transaction = objectMapper.readValue(record.value(), Transaction.class);

// Lógica antifraude: rechazar transacciones > 1000
TransactionEstatus status = transaction.getValue() > 1000 ? TransactionEstatus.RECHAZADO : TransactionEstatus.APROBADO;

// Enviar evento de actualización de estado a transaction service
kafkaProducer.sendTransactionStatus(transaction.getTransactionExternalId(), status);

} catch (Exception e) {
throw new RuntimeException("Error processing Kafka message in AntiFraud Service", e);
}
}
}
58 changes: 58 additions & 0 deletions src/main/java/com/ibk/reto/kafka/KafkaProducer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.ibk.reto.kafka;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.ibk.reto.model.Transaction;
import com.ibk.reto.model.TransactionEstatus;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.UUID;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
@Service
public class KafkaProducer {

private final KafkaTemplate<String, String> kafkaTemplate;
private final ObjectMapper objectMapper;

public KafkaProducer(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule());
}

public void sendTransaction(Transaction transaction) {
try {
String message = objectMapper.writeValueAsString(transaction);
kafkaTemplate.send("transaction_created", message);
} catch (Exception e) {
throw new RuntimeException("Error sending Kafka message", e);
}
}

public void sendTransactionStatus(UUID transactionId, TransactionEstatus status) {
try {
String message = objectMapper.writeValueAsString(new TransactionStatusUpdate(transactionId, status));
kafkaTemplate.send("transaction_status_updated", message);
} catch (Exception e) {
throw new RuntimeException("Error sending Kafka message", e);
}
}

private static class TransactionStatusUpdate {
public UUID transactionId;
public TransactionEstatus status;

public TransactionStatusUpdate(UUID transactionId, TransactionEstatus status) {
this.transactionId = transactionId;
this.status = status;
}
}
}
46 changes: 46 additions & 0 deletions src/main/java/com/ibk/reto/kafka/TransactionKafkaConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.ibk.reto.kafka;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.ibk.reto.model.TransactionEstatus;
import com.ibk.reto.service.TransactionService;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

import java.util.UUID;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
@Service
public class TransactionKafkaConsumer {
private final TransactionService transactionService;
private final ObjectMapper objectMapper;

public TransactionKafkaConsumer(TransactionService transactionService) {
this.transactionService = transactionService;
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule());
}

@KafkaListener(topics = "transaction_status_updated", groupId = "transaction-group")
public void updateTransactionStatus(ConsumerRecord<String, String> record) {
try {
JsonNode jsonNode = objectMapper.readTree(record.value());
UUID transactionId = UUID.fromString(jsonNode.get("transactionId").asText());
TransactionEstatus status = TransactionEstatus.valueOf(jsonNode.get("status").asText());

// Actualizar estado en la base de datos
transactionService.updateTransactionStatus(transactionId, status);

} catch (Exception e) {
throw new RuntimeException("Error processing Kafka message in Transaction Service", e);
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/ibk/reto/model/Transaction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.ibk.reto.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.UUID;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
@Entity
@Data
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID transactionExternalId;

private UUID accountExternalIdDebit;
private UUID accountExternalIdCredit;
private Integer tranferTypeId;
private Double value;

@Enumerated(EnumType.STRING)
private TransactionEstatus transactionStatus;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt = LocalDateTime.now();
}
12 changes: 12 additions & 0 deletions src/main/java/com/ibk/reto/model/TransactionEstatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ibk.reto.model;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
public enum TransactionEstatus {
PENDIENTE, APROBADO, RECHAZADO
}
16 changes: 16 additions & 0 deletions src/main/java/com/ibk/reto/repository/TransactionRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.ibk.reto.repository;

import com.ibk.reto.model.Transaction;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.UUID;

/**
* Reto Tecnico IBK.
*
* @author Alvaro Cubas Huarca
* @version 1.0
* @since 2025-03-23
*/
public interface TransactionRepository extends JpaRepository<Transaction, UUID> {
}
Loading