From 81765e197d3450b9f1ab124ac75bdd321d1f744b Mon Sep 17 00:00:00 2001 From: VANSHPAR Date: Wed, 13 Aug 2025 20:56:50 +0530 Subject: [PATCH] Completed all tasks successfully --- application.yml | 42 ++++++++++ db/midasdb.mv.db | Bin 0 -> 40960 bytes pom.xml | 62 +++++++++++++- .../midascore/configurations/Appconfig.java | 12 +++ .../midascore/configurations/KafkaConfig.java | 62 ++++++++++++++ .../consumer/KafkaConsumerService.java | 15 ++++ .../controller/BalanceController.java | 31 +++++++ .../midascore/entity/TransactionRecord.java | 40 +++++++++ .../com/jpmc/midascore/entity/UserRecord.java | 11 +++ .../midascore/foundation/Transaction.java | 5 ++ .../com/jpmc/midascore/models/Incentive.java | 10 +++ .../repository/TransactionRepository.java | 10 +++ .../midascore/repository/UserRepository.java | 2 + .../midascore/service/TransactionService.java | 76 ++++++++++++++++++ .../java/com/jpmc/midascore/TaskOneTests.java | 2 + .../com/jpmc/midascore/TaskThreeTests.java | 4 + .../com/jpmc/midascore/UserPopulator.java | 7 ++ 17 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 db/midasdb.mv.db create mode 100644 src/main/java/com/jpmc/midascore/configurations/Appconfig.java create mode 100644 src/main/java/com/jpmc/midascore/configurations/KafkaConfig.java create mode 100644 src/main/java/com/jpmc/midascore/consumer/KafkaConsumerService.java create mode 100644 src/main/java/com/jpmc/midascore/controller/BalanceController.java create mode 100644 src/main/java/com/jpmc/midascore/entity/TransactionRecord.java create mode 100644 src/main/java/com/jpmc/midascore/models/Incentive.java create mode 100644 src/main/java/com/jpmc/midascore/repository/TransactionRepository.java create mode 100644 src/main/java/com/jpmc/midascore/service/TransactionService.java diff --git a/application.yml b/application.yml index e69de29b..f8880a1a 100644 --- a/application.yml +++ b/application.yml @@ -0,0 +1,42 @@ +server: + port: 33400 + + +spring: + kafka: + group-id: midas-core-group + consumer: + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer + properties: + spring.json.trusted.packages: "*" + producer: + key-serializer: org.apache.kafka.common.serialization.StringSerializer + value-serializer: org.springframework.kafka.support.serializer.JsonSerializer + + datasource: + url: jdbc:h2:file:./db/midasdb + driver-class-name: org.h2.Driver + username: admin + password: password + jpa: + database-platform: org.hibernate.dialect.H2Dialect + hibernate: + ddl-auto: update + show-sql: true + + h2: + console: + enabled: true + path: /db-console + settings: + web-allow-others: false + + + +kafka: + topic: + transaction: transactions + +general: + kafka-topic: transactions diff --git a/db/midasdb.mv.db b/db/midasdb.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..f54a7ce28633aadab6486267e71c651931c1ef03 GIT binary patch literal 40960 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 Midas Core - 17 + 22 + + + org.springframework.data + spring-data-jpa + 3.2.5 + + + + org.springframework.boot + spring-boot-starter-web + 3.2.5 + + + + org.springframework.kafka + spring-kafka + 3.1.4 + + + + com.h2database + h2 + 2.2.224 + test + + + + org.springframework.boot + spring-boot-starter-test + 3.2.5 + test + + + + org.springframework.kafka + spring-kafka-test + 3.1.4 + test + + + + org.testcontainers + kafka + 1.19.1 + test + + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-data-jpa + @@ -25,6 +84,7 @@ org.springframework.boot spring-boot-maven-plugin + diff --git a/src/main/java/com/jpmc/midascore/configurations/Appconfig.java b/src/main/java/com/jpmc/midascore/configurations/Appconfig.java new file mode 100644 index 00000000..61dc567d --- /dev/null +++ b/src/main/java/com/jpmc/midascore/configurations/Appconfig.java @@ -0,0 +1,12 @@ +package com.jpmc.midascore.configurations; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; +@Configuration +public class Appconfig { + @Bean + public RestTemplate restTemplate(){ + return new RestTemplate(); + } +} diff --git a/src/main/java/com/jpmc/midascore/configurations/KafkaConfig.java b/src/main/java/com/jpmc/midascore/configurations/KafkaConfig.java new file mode 100644 index 00000000..89adaf5f --- /dev/null +++ b/src/main/java/com/jpmc/midascore/configurations/KafkaConfig.java @@ -0,0 +1,62 @@ +package com.jpmc.midascore.configurations; + + +import com.jpmc.midascore.foundation.Transaction; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.*; +import org.springframework.kafka.support.serializer.JsonDeserializer; +import org.springframework.kafka.support.serializer.JsonSerializer; + +import java.util.HashMap; +import java.util.Map; + + +@Configuration +@EnableKafka +public class KafkaConfig { + + + @Bean + public ProducerFactory producerFactory() { + Map config = new HashMap<>(); + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"localhost:9092"); + config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + return new DefaultKafkaProducerFactory<>(config); + } + + @Bean + public KafkaTemplate kafkaTemplate() { + return new KafkaTemplate<>(producerFactory()); + } + + @Bean + public ConsumerFactory consumerFactory() { + Map config = new HashMap<>(); + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"localhost:9092"); + config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); + config.put(JsonDeserializer.TRUSTED_PACKAGES,"*"); + config.put(ConsumerConfig.GROUP_ID_CONFIG, "midas-core-group"); + return new DefaultKafkaConsumerFactory<>(config,new StringDeserializer(),new JsonDeserializer<>(Transaction.class)); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } + + +} diff --git a/src/main/java/com/jpmc/midascore/consumer/KafkaConsumerService.java b/src/main/java/com/jpmc/midascore/consumer/KafkaConsumerService.java new file mode 100644 index 00000000..6bf13111 --- /dev/null +++ b/src/main/java/com/jpmc/midascore/consumer/KafkaConsumerService.java @@ -0,0 +1,15 @@ +package com.jpmc.midascore.consumer; + +import com.jpmc.midascore.foundation.Transaction; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + +@Service +public class KafkaConsumerService { + @KafkaListener(topics = "${kafka.topic.transaction}", + groupId = "midas-core-group", + containerFactory = "kafkaListenerContainerFactory") + public void listen(Transaction transaction) { + System.out.println("Transaction from topic "+transaction); + } +} diff --git a/src/main/java/com/jpmc/midascore/controller/BalanceController.java b/src/main/java/com/jpmc/midascore/controller/BalanceController.java new file mode 100644 index 00000000..641f2e11 --- /dev/null +++ b/src/main/java/com/jpmc/midascore/controller/BalanceController.java @@ -0,0 +1,31 @@ +package com.jpmc.midascore.controller; + +import com.jpmc.midascore.entity.UserRecord; +import com.jpmc.midascore.foundation.Balance; +import com.jpmc.midascore.repository.UserRepository; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@RestController + +public class BalanceController { + + private final UserRepository userRepository; + + public BalanceController(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @GetMapping("/balance") + public Balance getBalance(@RequestParam("userId") Long userId) { + Optional user=userRepository.findById(userId); + + float balance= 0.0F; + if(user.isPresent()){ + balance=user.get().getBalance(); + } + return new Balance(balance); + + } +} diff --git a/src/main/java/com/jpmc/midascore/entity/TransactionRecord.java b/src/main/java/com/jpmc/midascore/entity/TransactionRecord.java new file mode 100644 index 00000000..4ff81280 --- /dev/null +++ b/src/main/java/com/jpmc/midascore/entity/TransactionRecord.java @@ -0,0 +1,40 @@ +package com.jpmc.midascore.entity; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TransactionRecord { + + public TransactionRecord(UserRecord sender, UserRecord recipient, float amount) { + this.Sender = sender; + this.Recipient = recipient; + this.amount = amount; + + } + + @Id + @GeneratedValue() + private long id; + + @ManyToOne + @JoinColumn(name = "sender_id") + private UserRecord Sender; + + @ManyToOne + @JoinColumn(name = "recipent_id") + private UserRecord Recipient; + + private float amount; + + + + +} diff --git a/src/main/java/com/jpmc/midascore/entity/UserRecord.java b/src/main/java/com/jpmc/midascore/entity/UserRecord.java index f9606fff..9b9a0597 100644 --- a/src/main/java/com/jpmc/midascore/entity/UserRecord.java +++ b/src/main/java/com/jpmc/midascore/entity/UserRecord.java @@ -1,6 +1,10 @@ package com.jpmc.midascore.entity; import jakarta.persistence.*; +import org.apache.catalina.LifecycleState; + +import java.math.BigDecimal; +import java.util.List; @Entity public class UserRecord { @@ -15,6 +19,13 @@ public class UserRecord { @Column(nullable = false) private float balance; + @OneToMany(mappedBy = "Sender") + private List senderTransactions; + + @OneToMany(mappedBy = "Recipient") + private List recipientTransactions ; + + protected UserRecord() { } diff --git a/src/main/java/com/jpmc/midascore/foundation/Transaction.java b/src/main/java/com/jpmc/midascore/foundation/Transaction.java index 953376b5..e848bc96 100644 --- a/src/main/java/com/jpmc/midascore/foundation/Transaction.java +++ b/src/main/java/com/jpmc/midascore/foundation/Transaction.java @@ -1,12 +1,17 @@ package com.jpmc.midascore.foundation; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter @JsonIgnoreProperties(ignoreUnknown = true) public class Transaction { private long senderId; private long recipientId; private float amount; + private float incentive; public Transaction() { } diff --git a/src/main/java/com/jpmc/midascore/models/Incentive.java b/src/main/java/com/jpmc/midascore/models/Incentive.java new file mode 100644 index 00000000..79bf5115 --- /dev/null +++ b/src/main/java/com/jpmc/midascore/models/Incentive.java @@ -0,0 +1,10 @@ +package com.jpmc.midascore.models; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Incentive { + private float amount; +} diff --git a/src/main/java/com/jpmc/midascore/repository/TransactionRepository.java b/src/main/java/com/jpmc/midascore/repository/TransactionRepository.java new file mode 100644 index 00000000..b00e040d --- /dev/null +++ b/src/main/java/com/jpmc/midascore/repository/TransactionRepository.java @@ -0,0 +1,10 @@ +package com.jpmc.midascore.repository; + +import com.jpmc.midascore.entity.TransactionRecord; +import com.jpmc.midascore.entity.UserRecord; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TransactionRepository extends CrudRepository { +} diff --git a/src/main/java/com/jpmc/midascore/repository/UserRepository.java b/src/main/java/com/jpmc/midascore/repository/UserRepository.java index 937275b6..ae7a5fc8 100644 --- a/src/main/java/com/jpmc/midascore/repository/UserRepository.java +++ b/src/main/java/com/jpmc/midascore/repository/UserRepository.java @@ -5,4 +5,6 @@ public interface UserRepository extends CrudRepository { UserRecord findById(long id); + + UserRecord findByName(String name); } diff --git a/src/main/java/com/jpmc/midascore/service/TransactionService.java b/src/main/java/com/jpmc/midascore/service/TransactionService.java new file mode 100644 index 00000000..f9a62ec5 --- /dev/null +++ b/src/main/java/com/jpmc/midascore/service/TransactionService.java @@ -0,0 +1,76 @@ +package com.jpmc.midascore.service; + +import com.jpmc.midascore.entity.TransactionRecord; +import com.jpmc.midascore.entity.UserRecord; +import com.jpmc.midascore.foundation.Transaction; +import com.jpmc.midascore.models.Incentive; +import com.jpmc.midascore.repository.TransactionRepository; +import com.jpmc.midascore.repository.UserRepository; +import jakarta.transaction.Transactional; +import jdk.jfr.RecordingState; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; + +@Service +public class TransactionService { + + private final TransactionRepository transactionRepository; + private final UserRepository userRepository; + private final RestTemplate restTemplate; + public TransactionService(TransactionRepository transactionRepository, UserRepository userRepository, RestTemplate restTemplate) { + this.transactionRepository = transactionRepository; + this.userRepository = userRepository; + this.restTemplate = restTemplate; + } + + @KafkaListener(topics = "${general.kafka-topic}", + groupId = "midas-core-group" + ) + @Transactional + public void processTransaction(Transaction transaction) { + + + Incentive incentive = restTemplate.postForObject("http://localhost:8080/incentive", + transaction, + Incentive.class); + + if(incentive!=null){ + transaction.setIncentive(incentive.getAmount()); + } + else transaction.setIncentive(0); + UserRecord sender=userRepository.findById(transaction.getSenderId()); + UserRecord recipient=userRepository.findById(transaction.getRecipientId()); + + if(sender==null || recipient==null){ + System.out.println("Invalid sender or recipient record"); + return; + } + + float senderAmount= sender.getBalance(); + float recipientAmount=recipient.getBalance(); + float amount=(transaction.getAmount()); + + if(senderAmount