refactor(lottery): improve code structure and readability
Some checks failed
CI / Get Changed Files (pull_request) Successful in 7s
CI / eslint (pull_request) Successful in 38s
CI / Docker frontend validation (pull_request) Successful in 50s
CI / oxlint (pull_request) Successful in 27s
CI / prettier (pull_request) Failing after 28s
CI / Docker backend validation (pull_request) Successful in 1m23s
CI / Checkstyle Main (pull_request) Successful in 1m20s
CI / test-build (pull_request) Successful in 40s
Some checks failed
CI / Get Changed Files (pull_request) Successful in 7s
CI / eslint (pull_request) Successful in 38s
CI / Docker frontend validation (pull_request) Successful in 50s
CI / oxlint (pull_request) Successful in 27s
CI / prettier (pull_request) Failing after 28s
CI / Docker backend validation (pull_request) Successful in 1m23s
CI / Checkstyle Main (pull_request) Successful in 1m20s
CI / test-build (pull_request) Successful in 40s
This commit is contained in:
parent
a3f34e960b
commit
7aefe67aa0
5 changed files with 200 additions and 140 deletions
|
@ -1,91 +1,91 @@
|
||||||
package de.szut.casino.lootboxes;
|
package de.szut.casino.lootboxes;
|
||||||
|
|
||||||
import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException;
|
import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException;
|
||||||
import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException;
|
import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException;
|
||||||
import de.szut.casino.user.UserEntity;
|
import de.szut.casino.user.UserEntity;
|
||||||
import de.szut.casino.user.UserService;
|
import de.szut.casino.user.UserService;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class LootBoxController {
|
public class LootBoxController {
|
||||||
private final LootBoxRepository lootBoxRepository;
|
private final LootBoxRepository lootBoxRepository;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final LootBoxService lootBoxService;
|
private final LootBoxService lootBoxService;
|
||||||
|
|
||||||
public LootBoxController(LootBoxRepository lootBoxRepository, UserService userService, LootBoxService lootBoxService) {
|
public LootBoxController(LootBoxRepository lootBoxRepository, UserService userService, LootBoxService lootBoxService) {
|
||||||
this.lootBoxRepository = lootBoxRepository;
|
this.lootBoxRepository = lootBoxRepository;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.lootBoxService = lootBoxService;
|
this.lootBoxService = lootBoxService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/lootboxes")
|
@GetMapping("/lootboxes")
|
||||||
public List<LootBoxEntity> getAllLootBoxes() {
|
public List<LootBoxEntity> getAllLootBoxes() {
|
||||||
return lootBoxRepository.findAll();
|
return lootBoxRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/lootboxes/{id}")
|
@PostMapping("/lootboxes/{id}")
|
||||||
public ResponseEntity<Object> purchaseLootBox(@PathVariable Long id) {
|
public ResponseEntity<Object> purchaseLootBox(@PathVariable Long id) {
|
||||||
Optional<LootBoxEntity> optionalLootBox = lootBoxRepository.findById(id);
|
Optional<LootBoxEntity> optionalLootBox = lootBoxRepository.findById(id);
|
||||||
if (optionalLootBox.isEmpty()) {
|
if (optionalLootBox.isEmpty()) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
LootBoxEntity lootBox = optionalLootBox.get();
|
LootBoxEntity lootBox = optionalLootBox.get();
|
||||||
|
|
||||||
Optional<UserEntity> optionalUser = userService.getCurrentUser();
|
Optional<UserEntity> optionalUser = userService.getCurrentUser();
|
||||||
if (optionalUser.isEmpty()) {
|
if (optionalUser.isEmpty()) {
|
||||||
throw new UserNotFoundException();
|
throw new UserNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
UserEntity user = optionalUser.get();
|
UserEntity user = optionalUser.get();
|
||||||
|
|
||||||
if (lootBoxService.hasSufficientBalance(user, lootBox.getPrice())) {
|
if (lootBoxService.hasSufficientBalance(user, lootBox.getPrice())) {
|
||||||
throw new InsufficientFundsException();
|
throw new InsufficientFundsException();
|
||||||
}
|
}
|
||||||
|
|
||||||
RewardEntity reward = lootBoxService.determineReward(lootBox);
|
RewardEntity reward = lootBoxService.determineReward(lootBox);
|
||||||
lootBoxService.handleBalance(user, lootBox, reward);
|
lootBoxService.handleBalance(user, lootBox, reward);
|
||||||
|
|
||||||
return ResponseEntity.ok(reward);
|
return ResponseEntity.ok(reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/lootboxes")
|
@PostMapping("/lootboxes")
|
||||||
public ResponseEntity<Object> createLootbox(@RequestBody @Valid CreateLootBoxDto createLootBoxDto) {
|
public ResponseEntity<Object> createLootbox(@RequestBody @Valid CreateLootBoxDto createLootBoxDto) {
|
||||||
List<RewardEntity> rewardEntities = new ArrayList<>();
|
List<RewardEntity> rewardEntities = new ArrayList<>();
|
||||||
|
|
||||||
for (CreateRewardDto createRewardDto : createLootBoxDto.getRewards()) {
|
for (CreateRewardDto createRewardDto : createLootBoxDto.getRewards()) {
|
||||||
rewardEntities.add(new RewardEntity(createRewardDto.getValue(), createRewardDto.getProbability()));
|
rewardEntities.add(new RewardEntity(createRewardDto.getValue(), createRewardDto.getProbability()));
|
||||||
}
|
}
|
||||||
|
|
||||||
LootBoxEntity lootBoxEntity = new LootBoxEntity(
|
LootBoxEntity lootBoxEntity = new LootBoxEntity(
|
||||||
createLootBoxDto.getName(),
|
createLootBoxDto.getName(),
|
||||||
createLootBoxDto.getPrice(),
|
createLootBoxDto.getPrice(),
|
||||||
rewardEntities
|
rewardEntities
|
||||||
);
|
);
|
||||||
|
|
||||||
this.lootBoxRepository.save(lootBoxEntity);
|
this.lootBoxRepository.save(lootBoxEntity);
|
||||||
|
|
||||||
return ResponseEntity.ok(lootBoxEntity);
|
return ResponseEntity.ok(lootBoxEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/lootboxes/{id}")
|
@DeleteMapping("/lootboxes/{id}")
|
||||||
public ResponseEntity<Object> deleteLootbox(@PathVariable Long id) {
|
public ResponseEntity<Object> deleteLootbox(@PathVariable Long id) {
|
||||||
Optional<LootBoxEntity> optionalLootBox = lootBoxRepository.findById(id);
|
Optional<LootBoxEntity> optionalLootBox = lootBoxRepository.findById(id);
|
||||||
if (optionalLootBox.isEmpty()) {
|
if (optionalLootBox.isEmpty()) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
LootBoxEntity lootBox = optionalLootBox.get();
|
LootBoxEntity lootBox = optionalLootBox.get();
|
||||||
lootBoxRepository.delete(lootBox);
|
lootBoxRepository.delete(lootBox);
|
||||||
|
|
||||||
return ResponseEntity.ok(Collections.singletonMap("message", "successfully deleted lootbox"));
|
return ResponseEntity.ok(Collections.singletonMap("message", "successfully deleted lootbox"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
package de.szut.casino.lootboxes;
|
package de.szut.casino.lootboxes;
|
||||||
|
|
||||||
import de.szut.casino.user.UserEntity;
|
import de.szut.casino.user.UserEntity;
|
||||||
import de.szut.casino.user.UserRepository;
|
import de.szut.casino.user.UserRepository;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LootBoxService {
|
public class LootBoxService {
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
public LootBoxService(UserRepository userRepository) {
|
public LootBoxService(UserRepository userRepository) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSufficientBalance(UserEntity user, BigDecimal price) {
|
public boolean hasSufficientBalance(UserEntity user, BigDecimal price) {
|
||||||
return user.getBalance().compareTo(price) < 0;
|
return user.getBalance().compareTo(price) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RewardEntity determineReward(LootBoxEntity lootBox) {
|
public RewardEntity determineReward(LootBoxEntity lootBox) {
|
||||||
double randomValue = Math.random();
|
double randomValue = Math.random();
|
||||||
BigDecimal cumulativeProbability = BigDecimal.ZERO;
|
BigDecimal cumulativeProbability = BigDecimal.ZERO;
|
||||||
|
|
||||||
for (RewardEntity reward : lootBox.getRewards()) {
|
for (RewardEntity reward : lootBox.getRewards()) {
|
||||||
cumulativeProbability = cumulativeProbability.add(reward.getProbability());
|
cumulativeProbability = cumulativeProbability.add(reward.getProbability());
|
||||||
if (randomValue <= cumulativeProbability.doubleValue()) {
|
if (randomValue <= cumulativeProbability.doubleValue()) {
|
||||||
return reward;
|
return reward;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lootBox.getRewards().getLast();
|
return lootBox.getRewards().getLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleBalance(UserEntity user, LootBoxEntity lootBox, RewardEntity reward) {
|
public void handleBalance(UserEntity user, LootBoxEntity lootBox, RewardEntity reward) {
|
||||||
user.setBalance(user.getBalance().subtract(lootBox.getPrice()));
|
user.setBalance(user.getBalance().subtract(lootBox.getPrice()));
|
||||||
user.setBalance(user.getBalance().add(reward.getValue()));
|
user.setBalance(user.getBalance().add(reward.getValue()));
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,11 +51,19 @@ export default class BlackjackComponent implements OnInit {
|
||||||
debtAmount = signal(0);
|
debtAmount = signal(0);
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Initial user load from server
|
||||||
this.userService.getCurrentUser().subscribe((user) => {
|
this.userService.getCurrentUser().subscribe((user) => {
|
||||||
if (user) {
|
if (user) {
|
||||||
this.balance.set(user.balance);
|
this.balance.set(user.balance);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Subscribe to user updates for real-time balance changes
|
||||||
|
this.userService.currentUser$.subscribe((user) => {
|
||||||
|
if (user) {
|
||||||
|
this.balance.set(user.balance);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateGameState(game: BlackjackGame) {
|
private updateGameState(game: BlackjackGame) {
|
||||||
|
@ -84,9 +92,19 @@ export default class BlackjackComponent implements OnInit {
|
||||||
if (isGameOver) {
|
if (isGameOver) {
|
||||||
console.log('Game is over, state:', game.state);
|
console.log('Game is over, state:', game.state);
|
||||||
this.userService.refreshCurrentUser();
|
this.userService.refreshCurrentUser();
|
||||||
timer(1500).subscribe(() => {
|
|
||||||
this.showGameResult.set(true);
|
// Get the latest balance before showing the result dialog
|
||||||
console.log('Game result dialog shown after delay');
|
timer(1000).subscribe(() => {
|
||||||
|
this.userService.getCurrentUser().subscribe((user) => {
|
||||||
|
if (user) {
|
||||||
|
this.balance.set(user.balance);
|
||||||
|
// Show the result dialog after updating the balance
|
||||||
|
timer(500).subscribe(() => {
|
||||||
|
this.showGameResult.set(true);
|
||||||
|
console.log('Game result dialog shown after delay');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,12 +183,20 @@ export default class BlackjackComponent implements OnInit {
|
||||||
this.blackjackService.doubleDown(this.currentGameId()!).subscribe({
|
this.blackjackService.doubleDown(this.currentGameId()!).subscribe({
|
||||||
next: (game) => {
|
next: (game) => {
|
||||||
this.updateGameState(game);
|
this.updateGameState(game);
|
||||||
this.userService.getCurrentUser().subscribe((user) => {
|
|
||||||
if (user && user.balance < 0) {
|
// Wait a bit to ensure the backend has finished processing
|
||||||
this.debtAmount.set(Math.abs(user.balance));
|
timer(1000).subscribe(() => {
|
||||||
this.showDebtDialog.set(true);
|
this.userService.getCurrentUser().subscribe((user) => {
|
||||||
}
|
if (user) {
|
||||||
|
this.balance.set(user.balance);
|
||||||
|
if (user.balance < 0) {
|
||||||
|
this.debtAmount.set(Math.abs(user.balance));
|
||||||
|
this.showDebtDialog.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.isActionInProgress.set(false);
|
this.isActionInProgress.set(false);
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
|
@ -184,7 +210,13 @@ export default class BlackjackComponent implements OnInit {
|
||||||
onCloseGameResult(): void {
|
onCloseGameResult(): void {
|
||||||
console.log('Closing game result dialog');
|
console.log('Closing game result dialog');
|
||||||
this.showGameResult.set(false);
|
this.showGameResult.set(false);
|
||||||
this.userService.refreshCurrentUser();
|
|
||||||
|
// Refresh the balance when dialog is closed and update local state
|
||||||
|
this.userService.getCurrentUser().subscribe((user) => {
|
||||||
|
if (user) {
|
||||||
|
this.balance.set(user.balance);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onCloseDebtDialog(): void {
|
onCloseDebtDialog(): void {
|
||||||
|
|
|
@ -13,6 +13,19 @@ export class UserService {
|
||||||
private http: HttpClient = inject(HttpClient);
|
private http: HttpClient = inject(HttpClient);
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Initialize with the current user from AuthService if available
|
||||||
|
const currentUser = this.authService.getUser();
|
||||||
|
if (currentUser) {
|
||||||
|
this.currentUserSubject.next(currentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to auth service user updates
|
||||||
|
this.authService.userSubject.subscribe(user => {
|
||||||
|
this.currentUserSubject.next(user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public getCurrentUser(): Observable<User | null> {
|
public getCurrentUser(): Observable<User | null> {
|
||||||
return this.http.get<User | null>('/backend/users/me').pipe(
|
return this.http.get<User | null>('/backend/users/me').pipe(
|
||||||
catchError(() => EMPTY),
|
catchError(() => EMPTY),
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { RouterModule } from '@angular/router';
|
||||||
import { AuthService } from '@service/auth.service';
|
import { AuthService } from '@service/auth.service';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component';
|
import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component';
|
||||||
|
import { UserService } from '@service/user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-navbar',
|
selector: 'app-navbar',
|
||||||
|
@ -21,9 +22,11 @@ import { AnimatedNumberComponent } from '@blackjack/components/animated-number/a
|
||||||
export class NavbarComponent implements OnInit, OnDestroy {
|
export class NavbarComponent implements OnInit, OnDestroy {
|
||||||
isMenuOpen = false;
|
isMenuOpen = false;
|
||||||
private authService: AuthService = inject(AuthService);
|
private authService: AuthService = inject(AuthService);
|
||||||
|
private userService: UserService = inject(UserService);
|
||||||
isLoggedIn = this.authService.isLoggedIn();
|
isLoggedIn = this.authService.isLoggedIn();
|
||||||
|
|
||||||
private authSubscription!: Subscription;
|
private authSubscription!: Subscription;
|
||||||
|
private userSubscription!: Subscription;
|
||||||
public balance = signal(0);
|
public balance = signal(0);
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@ -32,10 +35,22 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||||
this.balance.set(user?.balance ?? 0);
|
this.balance.set(user?.balance ?? 0);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Also subscribe to UserService for real-time balance updates
|
||||||
|
this.userSubscription = this.userService.currentUser$.subscribe({
|
||||||
|
next: (user) => {
|
||||||
|
if (user) {
|
||||||
|
this.balance.set(user.balance);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.authSubscription.unsubscribe();
|
this.authSubscription.unsubscribe();
|
||||||
|
if (this.userSubscription) {
|
||||||
|
this.userSubscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
|
|
Reference in a new issue