diff --git a/backend/requests/lootboxes.http b/backend/requests/lootboxes.http new file mode 100644 index 0000000..9059570 --- /dev/null +++ b/backend/requests/lootboxes.http @@ -0,0 +1,9 @@ +GET http://localhost:8080/lootboxes +Authorization: Bearer {{token}} +Content-Type: application/json + +### + +POST http://localhost:8080/lootboxes/2 +Authorization: Bearer {{token}} +Content-Type: application/json \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/CasinoApplication.java b/backend/src/main/java/de/szut/casino/CasinoApplication.java index 8c99f02..1d7e861 100644 --- a/backend/src/main/java/de/szut/casino/CasinoApplication.java +++ b/backend/src/main/java/de/szut/casino/CasinoApplication.java @@ -1,10 +1,20 @@ package de.szut.casino; +import de.szut.casino.lootboxes.LootBoxEntity; +import de.szut.casino.lootboxes.LootBoxRepository; +import de.szut.casino.lootboxes.RewardEntity; +import de.szut.casino.lootboxes.RewardRepository; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + @SpringBootApplication public class CasinoApplication { @@ -16,4 +26,65 @@ public class CasinoApplication { public static RestTemplate restTemplate() { return new RestTemplate(); } + + @Bean + public CommandLineRunner initData(LootBoxRepository lootBoxRepository, RewardRepository rewardRepository) { + return _ -> { + if (lootBoxRepository.count() == 0) { + LootBoxEntity basicLootBox = new LootBoxEntity(); + basicLootBox.setName("Basic LootBox"); + basicLootBox.setPrice(new BigDecimal("2")); + basicLootBox.setRewards(new ArrayList<>()); // Initialize the list + + LootBoxEntity premiumLootBox = new LootBoxEntity(); + premiumLootBox.setName("Premium LootBox"); + premiumLootBox.setPrice(new BigDecimal("5")); + premiumLootBox.setRewards(new ArrayList<>()); // Initialize the list + + lootBoxRepository.saveAll(Arrays.asList(basicLootBox, premiumLootBox)); + + RewardEntity commonReward = new RewardEntity(); + commonReward.setValue(new BigDecimal("0.50")); + commonReward.setProbability(new BigDecimal("0.7")); + + RewardEntity rareReward = new RewardEntity(); + rareReward.setValue(new BigDecimal("2.00")); + rareReward.setProbability(new BigDecimal("0.25")); + + RewardEntity epicReward = new RewardEntity(); + epicReward.setValue(new BigDecimal("5.00")); + epicReward.setProbability(new BigDecimal("0.5")); + + RewardEntity premiumCommon = new RewardEntity(); + premiumCommon.setValue(new BigDecimal("2.00")); + premiumCommon.setProbability(new BigDecimal("0.6")); + + RewardEntity premiumRare = new RewardEntity(); + premiumRare.setValue(new BigDecimal("5.00")); + premiumRare.setProbability(new BigDecimal("0.3")); + + RewardEntity legendaryReward = new RewardEntity(); + legendaryReward.setValue(new BigDecimal("15.00")); + legendaryReward.setProbability(new BigDecimal("0.10")); + + rewardRepository.saveAll(Arrays.asList( + commonReward, rareReward, epicReward, + premiumCommon, premiumRare, legendaryReward + )); + + basicLootBox.getRewards().add(commonReward); + basicLootBox.getRewards().add(premiumRare); + + premiumLootBox.getRewards().add(premiumCommon); + premiumLootBox.getRewards().add(premiumRare); + premiumLootBox.getRewards().add(legendaryReward); + + lootBoxRepository.saveAll(Arrays.asList(basicLootBox, premiumLootBox)); + + System.out.println("Initial LootBoxes and rewards created successfully"); + } else { + System.out.println("LootBoxes already exist, skipping initialization"); + } + }; + } } diff --git a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxController.java b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxController.java new file mode 100644 index 0000000..51a17e1 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxController.java @@ -0,0 +1,58 @@ +package de.szut.casino.lootboxes; + +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; +import de.szut.casino.user.UserService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@RestController +public class LootBoxController { + private final LootBoxRepository lootBoxRepository; + private final UserService userService; + private final LootBoxService lootBoxService; + + public LootBoxController(LootBoxRepository lootBoxRepository, UserRepository userRepository, UserService userService, LootBoxService lootBoxService) { + this.lootBoxRepository = lootBoxRepository; + this.userService = userService; + this.lootBoxService = lootBoxService; + } + + @GetMapping("/lootboxes") + public List getAllLootBoxes() { + return lootBoxRepository.findAll(); + } + + @PostMapping("/lootboxes/{id}") + public ResponseEntity purchaseLootBox(@PathVariable Long id, @RequestHeader("Authorization") String token) { + Optional optionalLootBox = lootBoxRepository.findById(id); + if (optionalLootBox.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + LootBoxEntity lootBox = optionalLootBox.get(); + + Optional optionalUser = userService.getCurrentUser(token); + if (optionalUser.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + UserEntity user = optionalUser.get(); + + if (lootBoxService.hasSufficientBalance(user, lootBox.getPrice())) { + Map errorResponse = new HashMap<>(); + errorResponse.put("error", "Insufficient balance"); + return ResponseEntity.badRequest().body(errorResponse); + } + + RewardEntity reward = lootBoxService.determineReward(lootBox); + lootBoxService.handleBalance(user, lootBox, reward); + + return ResponseEntity.ok(reward); + } +} diff --git a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxEntity.java b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxEntity.java new file mode 100644 index 0000000..49eb532 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxEntity.java @@ -0,0 +1,40 @@ +package de.szut.casino.lootboxes; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import de.szut.casino.blackjack.CardEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.SQLRestriction; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class LootBoxEntity { + @Id + @GeneratedValue + private Long id; + + private String name; + + @Column(precision = 19, scale = 2) + private BigDecimal price; + + @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinTable( + name = "lootbox_reward", + joinColumns = @JoinColumn(name = "lootbox_id"), + inverseJoinColumns = @JoinColumn(name = "reward_id") + ) + private List rewards = new ArrayList<>(); +} diff --git a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxRepository.java b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxRepository.java new file mode 100644 index 0000000..472388e --- /dev/null +++ b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxRepository.java @@ -0,0 +1,8 @@ +package de.szut.casino.lootboxes; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; + +@Service +public interface LootBoxRepository extends JpaRepository { +} diff --git a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxService.java b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxService.java new file mode 100644 index 0000000..d80370e --- /dev/null +++ b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxService.java @@ -0,0 +1,40 @@ +package de.szut.casino.lootboxes; + +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class LootBoxService { + private final UserRepository userRepository; + + public LootBoxService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public boolean hasSufficientBalance(UserEntity user, BigDecimal price) { + return user.getBalance().compareTo(price) < 0; + } + + public RewardEntity determineReward(LootBoxEntity lootBox) { + double randomValue = Math.random(); + BigDecimal cumulativeProbability = BigDecimal.ZERO; + + for (RewardEntity reward : lootBox.getRewards()) { + cumulativeProbability = cumulativeProbability.add(reward.getProbability()); + if (randomValue <= cumulativeProbability.doubleValue()) { + return reward; + } + } + + return lootBox.getRewards().getLast(); + } + + public void handleBalance(UserEntity user, LootBoxEntity lootBox, RewardEntity reward) { + user.setBalance(user.getBalance().subtract(lootBox.getPrice())); + user.setBalance(user.getBalance().add(reward.getValue())); + userRepository.save(user); + } +} diff --git a/backend/src/main/java/de/szut/casino/lootboxes/RewardEntity.java b/backend/src/main/java/de/szut/casino/lootboxes/RewardEntity.java new file mode 100644 index 0000000..ce0c155 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/lootboxes/RewardEntity.java @@ -0,0 +1,30 @@ +package de.szut.casino.lootboxes; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@Entity +public class RewardEntity { + @Id + @GeneratedValue + private Long id; + + @Column(precision = 19, scale = 2) + private BigDecimal value; + + @Column(precision = 5, scale = 2) + private BigDecimal probability; + + @ManyToMany(mappedBy = "rewards") + @JsonBackReference + private List lootBoxes = new ArrayList<>(); +} diff --git a/backend/src/main/java/de/szut/casino/lootboxes/RewardRepository.java b/backend/src/main/java/de/szut/casino/lootboxes/RewardRepository.java new file mode 100644 index 0000000..7878000 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/lootboxes/RewardRepository.java @@ -0,0 +1,8 @@ +package de.szut.casino.lootboxes; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; + +@Service +public interface RewardRepository extends JpaRepository { +}