From 313bd13ef9841c8f5f8672df813c926276e7cb8b Mon Sep 17 00:00:00 2001 From: Mykhailo Svishchov Date: Wed, 4 Mar 2026 21:42:35 +0200 Subject: [PATCH] replaced everything with ws --- ADMIN_SETUP.md | 114 ++ APPLICATION_OVERVIEW.md | 1024 ++++++++++++ BACKUP_SETUP.md | 327 ++++ BACKUP_TROUBLESHOOTING.md | 492 ++++++ DEPLOYMENT_GUIDE.md | 765 +++++++++ DOCKER_LOGGING_SETUP.md | 202 +++ Dockerfile | 19 +- Dockerfile.inferno | 14 +- EXTERNAL_API.md | 88 + LOGGING_GUIDE.md | 341 ++++ PHPMYADMIN_QUICK_START.md | 94 ++ PHPMYADMIN_SETUP.md | 355 ++++ QUICK_REFERENCE.md | 217 +++ README.md | 61 +- ROLLING_UPDATE_GUIDE.md | 356 +++++ VPS_DEPLOYMENT_NOTES.md | 208 +++ VPS_DEPLOYMENT_SUMMARY.md | 188 +++ docker-compose.inferno.yml | 35 +- docker-compose.prod.yml | 193 +++ docker-compose.yml | 13 +- lottery-config.properties.template | 62 + nginx.conf.template | 128 ++ nginx/conf.d/{honey.conf => lottery.conf} | 18 +- nginx/nginx.conf | 1 + pom.xml | 49 +- scripts/backup-database.sh | 216 +++ scripts/create-secret-file-from-template.sh | 30 + scripts/create-secret-file.sh | 3 +- scripts/diagnose-backup-permissions.sh | 227 +++ scripts/load-db-password.sh | 38 + scripts/restore-database.sh | 183 +++ scripts/rolling-update.sh | 628 ++++++++ scripts/setup-logging.sh | 119 ++ .../honey/config/TelegramProperties.java | 13 - .../com/honey/honey/config/WebConfig.java | 24 - .../honey/controller/UserController.java | 51 - .../exception/GlobalExceptionHandler.java | 27 - .../honey/health/DatabaseHealthIndicator.java | 39 - .../honey/logging/GrafanaLoggingConfig.java | 55 - .../honey/repository/UserARepository.java | 13 - .../honey/repository/UserBRepository.java | 10 - .../honey/repository/UserDRepository.java | 48 - .../com/honey/honey/service/UserService.java | 317 ---- .../lottery/LotteryBackendApplication.java} | 12 +- .../lottery/config/AdminSecurityConfig.java | 95 ++ .../lottery}/config/ConfigLoader.java | 6 +- .../lottery}/config/CorsConfig.java | 2 +- .../lottery/lottery/config/LocaleConfig.java | 61 + .../lottery/config/TelegramProperties.java | 35 + .../com/lottery/lottery/config/WebConfig.java | 44 + .../config/WebSocketAuthInterceptor.java | 106 ++ .../lottery/config/WebSocketConfig.java | 62 + .../config/WebSocketSubscriptionListener.java | 178 +++ .../controller/AdminAnalyticsController.java | 206 +++ .../controller/AdminBotConfigController.java | 96 ++ .../AdminConfigurationsController.java | 49 + .../controller/AdminDashboardController.java | 194 +++ .../AdminFeatureSwitchController.java | 36 + .../controller/AdminLoginController.java | 51 + .../controller/AdminMasterController.java | 26 + .../AdminNotificationController.java | 42 + .../controller/AdminPaymentController.java | 147 ++ .../controller/AdminPayoutController.java | 204 +++ .../controller/AdminPromotionController.java | 139 ++ .../controller/AdminRoomController.java | 57 + .../AdminSupportTicketController.java | 316 ++++ .../controller/AdminUserController.java | 277 ++++ .../lottery}/controller/AuthController.java | 31 +- .../controller/DepositWebhookController.java | 56 + .../lottery/controller/GameController.java | 99 ++ .../controller/GameWebSocketController.java | 184 +++ .../controller/NotifyBroadcastController.java | 56 + .../lottery/controller/PaymentController.java | 237 +++ .../lottery/controller/PayoutController.java | 66 + .../lottery}/controller/PingController.java | 3 +- .../controller/PromotionController.java | 43 + .../controller/QuickAnswerController.java | 134 ++ .../controller/RemoteBetController.java | 191 +++ .../lottery/controller/SupportController.java | 104 ++ .../lottery/controller/TaskController.java | 100 ++ .../controller/TelegramWebhookController.java | 868 ++++++++++ .../controller/TransactionController.java | 50 + .../controller/UserCheckController.java | 105 ++ .../lottery/controller/UserController.java | 146 ++ .../lottery/dto/AdminBotConfigDto.java | 34 + .../lottery/dto/AdminBotConfigRequest.java | 37 + .../dto/AdminConfigurationsRequest.java | 19 + .../lottery/dto/AdminGameRoundDto.java | 28 + .../lottery/dto/AdminLoginRequest.java | 10 + .../lottery/dto/AdminLoginResponse.java | 13 + .../lottery/lottery/dto/AdminMasterDto.java | 31 + .../lottery/lottery/dto/AdminPaymentDto.java | 27 + .../lottery/lottery/dto/AdminPayoutDto.java | 28 + .../lottery/dto/AdminPromotionDto.java | 23 + .../lottery/dto/AdminPromotionRequest.java | 25 + .../lottery/dto/AdminPromotionRewardDto.java | 22 + .../dto/AdminPromotionRewardRequest.java | 18 + .../lottery/dto/AdminPromotionUserDto.java | 21 + .../dto/AdminPromotionUserPointsRequest.java | 18 + .../lottery/dto/AdminRoomDetailDto.java | 26 + .../lottery/dto/AdminRoomOnlineUserDto.java | 29 + .../lottery/dto/AdminRoomParticipantDto.java | 17 + .../lottery/dto/AdminRoomSummaryDto.java | 20 + .../lottery/dto/AdminRoomViewerDto.java | 15 + .../lottery/dto/AdminRoomWinnerDto.java | 17 + .../lottery/dto/AdminSupportMessageDto.java | 22 + .../dto/AdminSupportTicketDetailDto.java | 25 + .../lottery/dto/AdminSupportTicketDto.java | 26 + .../lottery/dto/AdminTaskClaimDto.java | 21 + .../lottery/dto/AdminTransactionDto.java | 22 + .../lottery/dto/AdminUserDetailDto.java | 55 + .../com/lottery/lottery/dto/AdminUserDto.java | 42 + .../lottery/dto/BalanceAdjustmentRequest.java | 36 + .../dto/BalanceAdjustmentResponse.java | 20 + .../lottery/lottery/dto/BalanceUpdateDto.java | 19 + .../lottery/dto/BotRegisterRequest.java | 37 + .../lottery/dto/BotRegisterResponse.java | 25 + .../lottery/dto/ClaimTaskResponse.java} | 12 +- .../lottery/dto/CompletedRoundDto.java | 26 + .../dto/CreateCryptoWithdrawalRequest.java | 23 + .../lottery/dto/CreateMessageRequest.java | 22 + .../lottery/dto/CreatePaymentRequest.java | 13 + .../lottery/dto/CreatePayoutRequest.java | 14 + .../lottery}/dto/CreateSessionRequest.java | 2 +- .../lottery}/dto/CreateSessionResponse.java | 2 +- .../lottery/dto/CreateTicketRequest.java | 26 + .../dto/CryptoDepositMethodsResponse.java | 52 + .../lottery/dto/CryptoWithdrawalResponse.java | 19 + .../lottery/dto/DailyBonusStatusDto.java | 19 + .../lottery/dto/DepositAddressApiRequest.java | 43 + .../lottery/dto/DepositAddressRequest.java | 13 + .../lottery/dto/DepositAddressResponse.java | 43 + .../lottery/dto/DepositAddressResultDto.java | 20 + .../lottery/dto/DepositMethodsDto.java | 32 + .../lottery/lottery/dto/ErrorResponse.java | 16 + .../dto/ExternalDepositWebhookRequest.java | 18 + .../lottery/dto/GameHistoryEntryDto.java | 29 + .../lottery/lottery/dto/GameRoomStateDto.java | 63 + .../lottery/lottery/dto/JoinRoundRequest.java | 24 + .../lottery/lottery/dto/JoinRoundResult.java | 17 + .../com/lottery/lottery/dto/MessageDto.java | 24 + .../lottery/dto/NotifyBroadcastRequest.java | 21 + .../lottery/lottery/dto/ParticipantDto.java | 23 + .../lottery/dto/PaymentInvoiceResponse.java | 19 + .../lottery/dto/PaymentWebhookRequest.java | 16 + .../lottery/dto/PayoutHistoryEntryDto.java | 20 + .../lottery/lottery/dto/PayoutResponse.java | 24 + .../lottery/dto/PromotionDetailDto.java | 29 + .../dto/PromotionLeaderboardEntryDto.java | 20 + .../lottery/dto/PromotionListItemDto.java | 22 + .../lottery/dto/QuickAnswerCreateRequest.java | 13 + .../lottery/lottery/dto/QuickAnswerDto.java | 18 + .../lottery/dto/RecentBonusClaimDto.java | 25 + .../com/lottery/lottery/dto/ReferralDto.java | 19 + .../lottery/lottery/dto/ReferralLevelDto.java | 25 + .../lottery/lottery/dto/RoomUpdateDto.java | 49 + .../dto/SupportTicketReplyRequest.java | 18 + .../java/com/lottery/lottery/dto/TaskDto.java | 26 + .../lottery/dto/TelegramApiResponse.java | 19 + .../lottery/dto/TelegramSendResult.java | 17 + .../lottery/lottery/dto/TicketDetailDto.java | 26 + .../com/lottery/lottery/dto/TicketDto.java | 25 + .../lottery/lottery/dto/TransactionDto.java | 44 + .../com/lottery/lottery/dto/UserCheckDto.java | 26 + .../lottery/lottery/dto/UserDepositDto.java | 23 + .../java/com/lottery/lottery/dto/UserDto.java | 26 + .../lottery/lottery/dto/UserProgressDto.java | 15 + .../lottery/dto/UserWithdrawalDto.java | 26 + .../com/lottery/lottery/dto/WinnerDto.java | 35 + .../lottery/dto/WithdrawalApiRequest.java | 55 + .../lottery/dto/WithdrawalApiResponse.java | 62 + .../dto/WithdrawalInfoApiResponse.java | 71 + .../dto/WithdrawalMethodDetailsDto.java | 30 + .../dto/WithdrawalMethodsApiResponse.java | 62 + .../lottery/dto/WithdrawalMethodsDto.java | 31 + .../exception/BannedUserException.java | 12 + .../exception/BetDecisionException.java | 16 + .../lottery}/exception/ErrorResponse.java | 3 +- .../lottery/exception/GameException.java | 26 + .../exception/GlobalExceptionHandler.java | 115 ++ .../InsufficientBalanceException.java | 8 + .../exception/InvalidBetAmountException.java | 30 + .../exception/RoomNotJoinableException.java | 14 + .../exception/UnauthorizedException.java | 3 +- .../exception/UserAlreadyJoinedException.java | 12 + .../health/DatabaseHealthIndicator.java | 130 ++ .../lottery/logging/GrafanaLoggingConfig.java | 30 + .../java/com/lottery/lottery/model/Admin.java | 48 + .../lottery/lottery/model/Configuration.java | 21 + .../lottery/model/CryptoDepositConfig.java | 30 + .../lottery/model/CryptoDepositMethod.java | 39 + .../lottery/model/CryptoWithdrawalMethod.java | 39 + .../lottery/lottery/model/FeatureSwitch.java | 32 + .../lottery/model/FlexibleBotConfig.java | 34 + .../com/lottery/lottery/model/GamePhase.java | 13 + .../com/lottery/lottery/model/GameRoom.java | 58 + .../com/lottery/lottery/model/GameRound.java | 68 + .../lottery/model/GameRoundParticipant.java | 40 + .../lottery/model/LotteryBotConfig.java | 75 + .../lottery/model/NotificationAudit.java | 35 + .../com/lottery/lottery/model/Payment.java | 67 + .../com/lottery/lottery/model/Payout.java | 109 ++ .../com/lottery/lottery/model/Promotion.java | 72 + .../lottery/model/PromotionReward.java | 48 + .../lottery/lottery/model/PromotionUser.java | 67 + .../lottery/lottery/model/QuickAnswer.java | 42 + .../lottery/lottery/model/SafeBotUser.java | 18 + .../lottery}/model/Session.java | 2 +- .../lottery/lottery/model/SupportMessage.java | 40 + .../lottery/lottery/model/SupportTicket.java | 57 + .../java/com/lottery/lottery/model/Task.java | 43 + .../lottery/lottery/model/Transaction.java | 61 + .../lottery}/model/UserA.java | 9 +- .../lottery}/model/UserB.java | 17 +- .../lottery}/model/UserD.java | 7 +- .../lottery/model/UserDailyBonusClaim.java | 42 + .../lottery/lottery/model/UserTaskClaim.java | 42 + .../lottery/repository/AdminRepository.java | 13 + .../repository/ConfigurationRepository.java | 9 + .../CryptoDepositConfigRepository.java | 9 + .../CryptoDepositMethodRepository.java | 13 + .../CryptoWithdrawalMethodRepository.java | 13 + .../repository/FeatureSwitchRepository.java | 9 + .../FlexibleBotConfigRepository.java | 13 + .../repository/GameRoomRepository.java | 30 + .../GameRoundParticipantRepository.java | 53 + .../repository/GameRoundRepository.java | 61 + .../LotteryBotConfigRepository.java | 24 + .../NotificationAuditRepository.java | 14 + .../lottery/repository/PaymentRepository.java | 91 ++ .../lottery/repository/PayoutRepository.java | 111 ++ .../repository/PromotionRepository.java | 30 + .../repository/PromotionRewardRepository.java | 15 + .../repository/PromotionUserRepository.java | 32 + .../repository/QuickAnswerRepository.java | 13 + .../repository/SafeBotUserRepository.java | 13 + .../repository/SessionRepository.java | 16 +- .../repository/SupportMessageRepository.java | 32 + .../repository/SupportTicketRepository.java | 51 + .../lottery/repository/TaskRepository.java | 14 + .../repository/TransactionRepository.java | 62 + .../lottery/repository/UserARepository.java | 54 + .../lottery/repository/UserBRepository.java | 25 + .../lottery/repository/UserDRepository.java | 114 ++ .../UserDailyBonusClaimRepository.java | 31 + .../repository/UserTaskClaimRepository.java | 20 + .../lottery}/security/AuthInterceptor.java | 33 +- .../security/RateLimitInterceptor.java | 98 ++ .../lottery}/security/UserContext.java | 4 +- .../security/UserRateLimitInterceptor.java | 113 ++ .../security/admin/AdminDetailsService.java | 39 + .../admin/JwtAuthenticationFilter.java | 64 + .../lottery/security/admin/JwtUtil.java | 81 + .../service/AdminBotConfigService.java | 210 +++ .../lottery/service/AdminMasterService.java | 79 + .../service/AdminPromotionService.java | 180 +++ .../lottery/lottery/service/AdminService.java | 43 + .../lottery/service/AdminUserService.java | 864 ++++++++++ .../lottery/service/AvatarService.java | 693 ++++++++ .../lottery/service/BetDecisionService.java | 17 + .../lottery/service/BotBetContext.java | 33 + .../lottery/service/BotBetHistoryService.java | 83 + .../lottery/service/BotConfigService.java | 145 ++ .../lottery/service/ConfigurationService.java | 53 + .../lottery}/service/CountryCodeService.java | 2 +- .../lottery/service/CryptoDepositService.java | 216 +++ .../service/CryptoWithdrawalService.java | 332 ++++ .../lottery/service/DataCleanupService.java | 88 + .../lottery/service/FeatureSwitchService.java | 139 ++ .../lottery/service/GameHistoryService.java | 86 + .../lottery/service/GameRoomService.java | 1424 +++++++++++++++++ .../lottery/service/LocalizationService.java | 84 + .../service/LotteryBotSchedulerService.java | 223 +++ .../service/NotificationBroadcastService.java | 183 +++ .../lottery/service/PaymentService.java | 449 ++++++ .../lottery/service/PayoutService.java | 607 +++++++ .../service/PersonaBetDecisionService.java | 137 ++ .../lottery/service/PromotionService.java | 121 ++ .../service/PublicPromotionService.java | 135 ++ .../service/ReferralCommissionService.java | 289 ++++ .../service/RoomConnectionService.java | 309 ++++ .../service/SessionCleanupService.java | 4 +- .../lottery}/service/SessionService.java | 40 +- .../lottery/service/SupportTicketService.java | 225 +++ .../lottery/lottery/service/TaskService.java | 654 ++++++++ .../lottery}/service/TelegramAuthService.java | 27 +- .../service/TelegramBotApiService.java | 161 ++ .../lottery/service/TelegramService.java | 106 ++ .../lottery/service/TransactionService.java | 234 +++ .../lottery/lottery/service/UserService.java | 478 ++++++ .../service/WithdrawalStatusSyncService.java | 127 ++ .../lottery}/util/IpUtils.java | 2 +- .../lottery/util/TelegramTokenRedactor.java | 26 + .../lottery}/util/TimeProvider.java | 3 +- src/main/resources/application.yml | 103 +- src/main/resources/assets/bot_start.mp4 | Bin 0 -> 788556 bytes src/main/resources/assets/winspin_5.mp4 | Bin 0 -> 970941 bytes .../migration/V10__add_indexes_to_payouts.sql | 7 + .../migration/V11__create_payments_table.sql | 22 + .../V12__create_transactions_table.sql | 16 + ...13__update_task_reward_type_to_tickets.sql | 10 + ...14__update_other_task_title_to_tickets.sql | 9 + .../V15__add_avatar_fields_to_users_a.sql | 12 + ...6__create_support_tickets_and_messages.sql | 33 + ...dd_indexes_for_performance_and_cleanup.sql | 12 + .../migration/V18__remove_unused_indexes.sql | 9 + .../migration/V19__add_daily_bonus_task.sql | 8 + .../db/migration/V1__create_users_table.sql | 9 - ...rded_tables.sql => V1__initial_schema.sql} | 85 +- ...__create_user_daily_bonus_claims_table.sql | 14 + .../V21__add_rounds_played_to_users_b.sql | 6 + ...d_composite_index_payments_user_status.sql | 21 + .../db/migration/V23__create_admins_table.sql | 13 + .../V24__add_admin_panel_indexes.sql | 55 + .../migration/V25__add_user_id_to_admins.sql | 7 + .../db/migration/V26__update_follow_tasks.sql | 17 + .../V27__update_invite_30_friends_reward.sql | 8 + .../V28__add_top_up_balance_tasks.sql | 41 + .../migration/V29__set_initial_balance_a.sql | 5 + .../db/migration/V2__create_game_tables.sql | 55 + .../migration/V2__create_sessions_table.sql | 14 - .../V30__create_quick_answers_table.sql | 13 + ...add_usd_amount_to_payments_and_payouts.sql | 7 + ...e_other_tasks_requirements_and_rewards.sql | 26 + .../V33__fix_other_task_duplicate_10000.sql | 9 + .../V34__add_referral_commission_indexes.sql | 5 + ...add_total_win_after_deposit_to_users_b.sql | 4 + .../V36__create_feature_switches.sql | 10 + .../V37__create_crypto_deposit_tables.sql | 24 + .../V38__seed_crypto_deposit_methods.sql | 40 + .../migration/V3__rename_tickets_to_bet.sql | 10 + .../migration/V40__usd_amount_to_decimal.sql | 13 + .../V41__create_crypto_withdrawal_tables.sql | 22 + ...42__withdrawal_methods_pid_and_icon_id.sql | 16 + .../migration/V43__payouts_crypto_columns.sql | 8 + .../db/migration/V44__payouts_payment_id.sql | 12 + .../migration/V45__payouts_crypto_indexes.sql | 5 + ...__payouts_type_status_updated_at_index.sql | 3 + .../V47__payouts_drop_redundant_index.sql | 3 + .../V48__feature_switches_payment_payout.sql | 4 + ...te_tasks_rewards_and_add_deposit_tasks.sql | 43 + ...d_composite_index_for_completed_rounds.sql | 10 + .../V50__set_initial_balance_a_5_tickets.sql | 4 + ...feature_switches_referral_tasks_50_100.sql | 5 + .../db/migration/V52__bot_config_tables.sql | 14 + .../V53__admin_users_list_sort_indexes.sql | 15 + .../db/migration/V54__lottery_bot_configs.sql | 20 + ...__feature_switch_lottery_bot_scheduler.sql | 4 + .../V56__create_promotions_tables.sql | 39 + .../V57__seed_first_net_win_promotion.sql | 20 + .../V58__add_promotions_total_reward.sql | 9 + .../V59__create_notifications_audit.sql | 10 + .../V5__add_screen_name_to_users_d.sql | 12 + .../V60__update_promotion_1_rewards.sql | 15 + ...notifications_audit_user_created_index.sql | 3 + ...__system_settings_bot_max_participants.sql | 9 + ...ame_rounds_room_phase_started_at_index.sql | 4 + ...ture_switch_manual_pay_for_all_payouts.sql | 4 + .../db/migration/V66__payouts_add_txhash.sql | 2 + ...dmin_user_deposits_withdrawals_indexes.sql | 9 + ...8__payouts_user_type_crypto_name_index.sql | 2 + .../V69__users_b_withdrawals_disabled.sql | 2 + .../db/migration/V6__create_tasks_tables.sql | 41 + ...t_win_max_bet_and_ref_count_promotions.sql | 33 + .../V7__add_follow_and_other_tasks.sql | 14 + .../db/migration/V8__create_payouts_table.sql | 18 + .../migration/V9__add_quantity_to_payouts.sql | 7 + src/main/resources/logback-spring.xml | 103 ++ src/main/resources/messages.properties | 201 +++ src/main/resources/messages_de.properties | 144 ++ src/main/resources/messages_es.properties | 144 ++ src/main/resources/messages_fr.properties | 144 ++ src/main/resources/messages_id.properties | 144 ++ src/main/resources/messages_it.properties | 144 ++ src/main/resources/messages_nl.properties | 144 ++ src/main/resources/messages_pl.properties | 144 ++ src/main/resources/messages_ru.properties | 145 ++ src/main/resources/messages_tr.properties | 145 ++ 378 files changed, 29072 insertions(+), 824 deletions(-) create mode 100644 ADMIN_SETUP.md create mode 100644 APPLICATION_OVERVIEW.md create mode 100644 BACKUP_SETUP.md create mode 100644 BACKUP_TROUBLESHOOTING.md create mode 100644 DEPLOYMENT_GUIDE.md create mode 100644 DOCKER_LOGGING_SETUP.md create mode 100644 EXTERNAL_API.md create mode 100644 LOGGING_GUIDE.md create mode 100644 PHPMYADMIN_QUICK_START.md create mode 100644 PHPMYADMIN_SETUP.md create mode 100644 QUICK_REFERENCE.md create mode 100644 ROLLING_UPDATE_GUIDE.md create mode 100644 VPS_DEPLOYMENT_NOTES.md create mode 100644 VPS_DEPLOYMENT_SUMMARY.md create mode 100644 docker-compose.prod.yml create mode 100644 lottery-config.properties.template create mode 100644 nginx.conf.template rename nginx/conf.d/{honey.conf => lottery.conf} (79%) create mode 100644 scripts/backup-database.sh create mode 100644 scripts/create-secret-file-from-template.sh create mode 100644 scripts/diagnose-backup-permissions.sh create mode 100644 scripts/load-db-password.sh create mode 100644 scripts/restore-database.sh create mode 100644 scripts/rolling-update.sh create mode 100644 scripts/setup-logging.sh delete mode 100644 src/main/java/com/honey/honey/config/TelegramProperties.java delete mode 100644 src/main/java/com/honey/honey/config/WebConfig.java delete mode 100644 src/main/java/com/honey/honey/controller/UserController.java delete mode 100644 src/main/java/com/honey/honey/exception/GlobalExceptionHandler.java delete mode 100644 src/main/java/com/honey/honey/health/DatabaseHealthIndicator.java delete mode 100644 src/main/java/com/honey/honey/logging/GrafanaLoggingConfig.java delete mode 100644 src/main/java/com/honey/honey/repository/UserARepository.java delete mode 100644 src/main/java/com/honey/honey/repository/UserBRepository.java delete mode 100644 src/main/java/com/honey/honey/repository/UserDRepository.java delete mode 100644 src/main/java/com/honey/honey/service/UserService.java rename src/main/java/com/{honey/honey/HoneyBackendApplication.java => lottery/lottery/LotteryBackendApplication.java} (59%) create mode 100644 src/main/java/com/lottery/lottery/config/AdminSecurityConfig.java rename src/main/java/com/{honey/honey => lottery/lottery}/config/ConfigLoader.java (96%) rename src/main/java/com/{honey/honey => lottery/lottery}/config/CorsConfig.java (97%) create mode 100644 src/main/java/com/lottery/lottery/config/LocaleConfig.java create mode 100644 src/main/java/com/lottery/lottery/config/TelegramProperties.java create mode 100644 src/main/java/com/lottery/lottery/config/WebConfig.java create mode 100644 src/main/java/com/lottery/lottery/config/WebSocketAuthInterceptor.java create mode 100644 src/main/java/com/lottery/lottery/config/WebSocketConfig.java create mode 100644 src/main/java/com/lottery/lottery/config/WebSocketSubscriptionListener.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminAnalyticsController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminBotConfigController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminConfigurationsController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminDashboardController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminFeatureSwitchController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminLoginController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminMasterController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminNotificationController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminPaymentController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminPayoutController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminPromotionController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminRoomController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminSupportTicketController.java create mode 100644 src/main/java/com/lottery/lottery/controller/AdminUserController.java rename src/main/java/com/{honey/honey => lottery/lottery}/controller/AuthController.java (68%) create mode 100644 src/main/java/com/lottery/lottery/controller/DepositWebhookController.java create mode 100644 src/main/java/com/lottery/lottery/controller/GameController.java create mode 100644 src/main/java/com/lottery/lottery/controller/GameWebSocketController.java create mode 100644 src/main/java/com/lottery/lottery/controller/NotifyBroadcastController.java create mode 100644 src/main/java/com/lottery/lottery/controller/PaymentController.java create mode 100644 src/main/java/com/lottery/lottery/controller/PayoutController.java rename src/main/java/com/{honey/honey => lottery/lottery}/controller/PingController.java (90%) create mode 100644 src/main/java/com/lottery/lottery/controller/PromotionController.java create mode 100644 src/main/java/com/lottery/lottery/controller/QuickAnswerController.java create mode 100644 src/main/java/com/lottery/lottery/controller/RemoteBetController.java create mode 100644 src/main/java/com/lottery/lottery/controller/SupportController.java create mode 100644 src/main/java/com/lottery/lottery/controller/TaskController.java create mode 100644 src/main/java/com/lottery/lottery/controller/TelegramWebhookController.java create mode 100644 src/main/java/com/lottery/lottery/controller/TransactionController.java create mode 100644 src/main/java/com/lottery/lottery/controller/UserCheckController.java create mode 100644 src/main/java/com/lottery/lottery/controller/UserController.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminBotConfigDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminBotConfigRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminConfigurationsRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminGameRoundDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminLoginRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminLoginResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminMasterDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPaymentDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPayoutDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPromotionDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPromotionRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPromotionRewardDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPromotionRewardRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPromotionUserDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminPromotionUserPointsRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminRoomDetailDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminRoomOnlineUserDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminRoomParticipantDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminRoomSummaryDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminRoomViewerDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminRoomWinnerDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminSupportMessageDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminSupportTicketDetailDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminSupportTicketDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminTaskClaimDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminTransactionDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminUserDetailDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/AdminUserDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/BalanceAdjustmentRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/BalanceAdjustmentResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/BalanceUpdateDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/BotRegisterRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/BotRegisterResponse.java rename src/main/java/com/{honey/honey/dto/UserDto.java => lottery/lottery/dto/ClaimTaskResponse.java} (55%) create mode 100644 src/main/java/com/lottery/lottery/dto/CompletedRoundDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/CreateCryptoWithdrawalRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/CreateMessageRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/CreatePaymentRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/CreatePayoutRequest.java rename src/main/java/com/{honey/honey => lottery/lottery}/dto/CreateSessionRequest.java (74%) rename src/main/java/com/{honey/honey => lottery/lottery}/dto/CreateSessionResponse.java (89%) create mode 100644 src/main/java/com/lottery/lottery/dto/CreateTicketRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/CryptoDepositMethodsResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/CryptoWithdrawalResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/DailyBonusStatusDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/DepositAddressApiRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/DepositAddressRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/DepositAddressResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/DepositAddressResultDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/DepositMethodsDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/ErrorResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/ExternalDepositWebhookRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/GameHistoryEntryDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/GameRoomStateDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/JoinRoundRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/JoinRoundResult.java create mode 100644 src/main/java/com/lottery/lottery/dto/MessageDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/NotifyBroadcastRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/ParticipantDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/PaymentInvoiceResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/PaymentWebhookRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/PayoutHistoryEntryDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/PayoutResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/PromotionDetailDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/PromotionLeaderboardEntryDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/PromotionListItemDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/QuickAnswerCreateRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/QuickAnswerDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/RecentBonusClaimDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/ReferralDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/ReferralLevelDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/RoomUpdateDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/SupportTicketReplyRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/TaskDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/TelegramApiResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/TelegramSendResult.java create mode 100644 src/main/java/com/lottery/lottery/dto/TicketDetailDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/TicketDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/TransactionDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/UserCheckDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/UserDepositDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/UserDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/UserProgressDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/UserWithdrawalDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/WinnerDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/WithdrawalApiRequest.java create mode 100644 src/main/java/com/lottery/lottery/dto/WithdrawalApiResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/WithdrawalInfoApiResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/WithdrawalMethodDetailsDto.java create mode 100644 src/main/java/com/lottery/lottery/dto/WithdrawalMethodsApiResponse.java create mode 100644 src/main/java/com/lottery/lottery/dto/WithdrawalMethodsDto.java create mode 100644 src/main/java/com/lottery/lottery/exception/BannedUserException.java create mode 100644 src/main/java/com/lottery/lottery/exception/BetDecisionException.java rename src/main/java/com/{honey/honey => lottery/lottery}/exception/ErrorResponse.java (84%) create mode 100644 src/main/java/com/lottery/lottery/exception/GameException.java create mode 100644 src/main/java/com/lottery/lottery/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/lottery/lottery/exception/InsufficientBalanceException.java create mode 100644 src/main/java/com/lottery/lottery/exception/InvalidBetAmountException.java create mode 100644 src/main/java/com/lottery/lottery/exception/RoomNotJoinableException.java rename src/main/java/com/{honey/honey => lottery/lottery}/exception/UnauthorizedException.java (78%) create mode 100644 src/main/java/com/lottery/lottery/exception/UserAlreadyJoinedException.java create mode 100644 src/main/java/com/lottery/lottery/health/DatabaseHealthIndicator.java create mode 100644 src/main/java/com/lottery/lottery/logging/GrafanaLoggingConfig.java create mode 100644 src/main/java/com/lottery/lottery/model/Admin.java create mode 100644 src/main/java/com/lottery/lottery/model/Configuration.java create mode 100644 src/main/java/com/lottery/lottery/model/CryptoDepositConfig.java create mode 100644 src/main/java/com/lottery/lottery/model/CryptoDepositMethod.java create mode 100644 src/main/java/com/lottery/lottery/model/CryptoWithdrawalMethod.java create mode 100644 src/main/java/com/lottery/lottery/model/FeatureSwitch.java create mode 100644 src/main/java/com/lottery/lottery/model/FlexibleBotConfig.java create mode 100644 src/main/java/com/lottery/lottery/model/GamePhase.java create mode 100644 src/main/java/com/lottery/lottery/model/GameRoom.java create mode 100644 src/main/java/com/lottery/lottery/model/GameRound.java create mode 100644 src/main/java/com/lottery/lottery/model/GameRoundParticipant.java create mode 100644 src/main/java/com/lottery/lottery/model/LotteryBotConfig.java create mode 100644 src/main/java/com/lottery/lottery/model/NotificationAudit.java create mode 100644 src/main/java/com/lottery/lottery/model/Payment.java create mode 100644 src/main/java/com/lottery/lottery/model/Payout.java create mode 100644 src/main/java/com/lottery/lottery/model/Promotion.java create mode 100644 src/main/java/com/lottery/lottery/model/PromotionReward.java create mode 100644 src/main/java/com/lottery/lottery/model/PromotionUser.java create mode 100644 src/main/java/com/lottery/lottery/model/QuickAnswer.java create mode 100644 src/main/java/com/lottery/lottery/model/SafeBotUser.java rename src/main/java/com/{honey/honey => lottery/lottery}/model/Session.java (95%) create mode 100644 src/main/java/com/lottery/lottery/model/SupportMessage.java create mode 100644 src/main/java/com/lottery/lottery/model/SupportTicket.java create mode 100644 src/main/java/com/lottery/lottery/model/Task.java create mode 100644 src/main/java/com/lottery/lottery/model/Transaction.java rename src/main/java/com/{honey/honey => lottery/lottery}/model/UserA.java (87%) rename src/main/java/com/{honey/honey => lottery/lottery}/model/UserB.java (57%) rename src/main/java/com/{honey/honey => lottery/lottery}/model/UserD.java (94%) create mode 100644 src/main/java/com/lottery/lottery/model/UserDailyBonusClaim.java create mode 100644 src/main/java/com/lottery/lottery/model/UserTaskClaim.java create mode 100644 src/main/java/com/lottery/lottery/repository/AdminRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/ConfigurationRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/CryptoDepositConfigRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/CryptoDepositMethodRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/CryptoWithdrawalMethodRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/FeatureSwitchRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/FlexibleBotConfigRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/GameRoomRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/GameRoundParticipantRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/GameRoundRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/LotteryBotConfigRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/NotificationAuditRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/PaymentRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/PayoutRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/PromotionRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/PromotionRewardRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/PromotionUserRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/QuickAnswerRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/SafeBotUserRepository.java rename src/main/java/com/{honey/honey => lottery/lottery}/repository/SessionRepository.java (77%) create mode 100644 src/main/java/com/lottery/lottery/repository/SupportMessageRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/SupportTicketRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/TaskRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/TransactionRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/UserARepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/UserBRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/UserDRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/UserDailyBonusClaimRepository.java create mode 100644 src/main/java/com/lottery/lottery/repository/UserTaskClaimRepository.java rename src/main/java/com/{honey/honey => lottery/lottery}/security/AuthInterceptor.java (63%) create mode 100644 src/main/java/com/lottery/lottery/security/RateLimitInterceptor.java rename src/main/java/com/{honey/honey => lottery/lottery}/security/UserContext.java (80%) create mode 100644 src/main/java/com/lottery/lottery/security/UserRateLimitInterceptor.java create mode 100644 src/main/java/com/lottery/lottery/security/admin/AdminDetailsService.java create mode 100644 src/main/java/com/lottery/lottery/security/admin/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/lottery/lottery/security/admin/JwtUtil.java create mode 100644 src/main/java/com/lottery/lottery/service/AdminBotConfigService.java create mode 100644 src/main/java/com/lottery/lottery/service/AdminMasterService.java create mode 100644 src/main/java/com/lottery/lottery/service/AdminPromotionService.java create mode 100644 src/main/java/com/lottery/lottery/service/AdminService.java create mode 100644 src/main/java/com/lottery/lottery/service/AdminUserService.java create mode 100644 src/main/java/com/lottery/lottery/service/AvatarService.java create mode 100644 src/main/java/com/lottery/lottery/service/BetDecisionService.java create mode 100644 src/main/java/com/lottery/lottery/service/BotBetContext.java create mode 100644 src/main/java/com/lottery/lottery/service/BotBetHistoryService.java create mode 100644 src/main/java/com/lottery/lottery/service/BotConfigService.java create mode 100644 src/main/java/com/lottery/lottery/service/ConfigurationService.java rename src/main/java/com/{honey/honey => lottery/lottery}/service/CountryCodeService.java (99%) create mode 100644 src/main/java/com/lottery/lottery/service/CryptoDepositService.java create mode 100644 src/main/java/com/lottery/lottery/service/CryptoWithdrawalService.java create mode 100644 src/main/java/com/lottery/lottery/service/DataCleanupService.java create mode 100644 src/main/java/com/lottery/lottery/service/FeatureSwitchService.java create mode 100644 src/main/java/com/lottery/lottery/service/GameHistoryService.java create mode 100644 src/main/java/com/lottery/lottery/service/GameRoomService.java create mode 100644 src/main/java/com/lottery/lottery/service/LocalizationService.java create mode 100644 src/main/java/com/lottery/lottery/service/LotteryBotSchedulerService.java create mode 100644 src/main/java/com/lottery/lottery/service/NotificationBroadcastService.java create mode 100644 src/main/java/com/lottery/lottery/service/PaymentService.java create mode 100644 src/main/java/com/lottery/lottery/service/PayoutService.java create mode 100644 src/main/java/com/lottery/lottery/service/PersonaBetDecisionService.java create mode 100644 src/main/java/com/lottery/lottery/service/PromotionService.java create mode 100644 src/main/java/com/lottery/lottery/service/PublicPromotionService.java create mode 100644 src/main/java/com/lottery/lottery/service/ReferralCommissionService.java create mode 100644 src/main/java/com/lottery/lottery/service/RoomConnectionService.java rename src/main/java/com/{honey/honey => lottery/lottery}/service/SessionCleanupService.java (95%) rename src/main/java/com/{honey/honey => lottery/lottery}/service/SessionService.java (80%) create mode 100644 src/main/java/com/lottery/lottery/service/SupportTicketService.java create mode 100644 src/main/java/com/lottery/lottery/service/TaskService.java rename src/main/java/com/{honey/honey => lottery/lottery}/service/TelegramAuthService.java (83%) create mode 100644 src/main/java/com/lottery/lottery/service/TelegramBotApiService.java create mode 100644 src/main/java/com/lottery/lottery/service/TelegramService.java create mode 100644 src/main/java/com/lottery/lottery/service/TransactionService.java create mode 100644 src/main/java/com/lottery/lottery/service/UserService.java create mode 100644 src/main/java/com/lottery/lottery/service/WithdrawalStatusSyncService.java rename src/main/java/com/{honey/honey => lottery/lottery}/util/IpUtils.java (99%) create mode 100644 src/main/java/com/lottery/lottery/util/TelegramTokenRedactor.java rename src/main/java/com/{honey/honey => lottery/lottery}/util/TimeProvider.java (94%) create mode 100644 src/main/resources/assets/bot_start.mp4 create mode 100644 src/main/resources/assets/winspin_5.mp4 create mode 100644 src/main/resources/db/migration/V10__add_indexes_to_payouts.sql create mode 100644 src/main/resources/db/migration/V11__create_payments_table.sql create mode 100644 src/main/resources/db/migration/V12__create_transactions_table.sql create mode 100644 src/main/resources/db/migration/V13__update_task_reward_type_to_tickets.sql create mode 100644 src/main/resources/db/migration/V14__update_other_task_title_to_tickets.sql create mode 100644 src/main/resources/db/migration/V15__add_avatar_fields_to_users_a.sql create mode 100644 src/main/resources/db/migration/V16__create_support_tickets_and_messages.sql create mode 100644 src/main/resources/db/migration/V17__add_indexes_for_performance_and_cleanup.sql create mode 100644 src/main/resources/db/migration/V18__remove_unused_indexes.sql create mode 100644 src/main/resources/db/migration/V19__add_daily_bonus_task.sql delete mode 100644 src/main/resources/db/migration/V1__create_users_table.sql rename src/main/resources/db/migration/{V3__replace_users_with_sharded_tables.sql => V1__initial_schema.sql} (60%) create mode 100644 src/main/resources/db/migration/V20__create_user_daily_bonus_claims_table.sql create mode 100644 src/main/resources/db/migration/V21__add_rounds_played_to_users_b.sql create mode 100644 src/main/resources/db/migration/V22__add_composite_index_payments_user_status.sql create mode 100644 src/main/resources/db/migration/V23__create_admins_table.sql create mode 100644 src/main/resources/db/migration/V24__add_admin_panel_indexes.sql create mode 100644 src/main/resources/db/migration/V25__add_user_id_to_admins.sql create mode 100644 src/main/resources/db/migration/V26__update_follow_tasks.sql create mode 100644 src/main/resources/db/migration/V27__update_invite_30_friends_reward.sql create mode 100644 src/main/resources/db/migration/V28__add_top_up_balance_tasks.sql create mode 100644 src/main/resources/db/migration/V29__set_initial_balance_a.sql create mode 100644 src/main/resources/db/migration/V2__create_game_tables.sql delete mode 100644 src/main/resources/db/migration/V2__create_sessions_table.sql create mode 100644 src/main/resources/db/migration/V30__create_quick_answers_table.sql create mode 100644 src/main/resources/db/migration/V31__add_usd_amount_to_payments_and_payouts.sql create mode 100644 src/main/resources/db/migration/V32__update_other_tasks_requirements_and_rewards.sql create mode 100644 src/main/resources/db/migration/V33__fix_other_task_duplicate_10000.sql create mode 100644 src/main/resources/db/migration/V34__add_referral_commission_indexes.sql create mode 100644 src/main/resources/db/migration/V35__add_total_win_after_deposit_to_users_b.sql create mode 100644 src/main/resources/db/migration/V36__create_feature_switches.sql create mode 100644 src/main/resources/db/migration/V37__create_crypto_deposit_tables.sql create mode 100644 src/main/resources/db/migration/V38__seed_crypto_deposit_methods.sql create mode 100644 src/main/resources/db/migration/V3__rename_tickets_to_bet.sql create mode 100644 src/main/resources/db/migration/V40__usd_amount_to_decimal.sql create mode 100644 src/main/resources/db/migration/V41__create_crypto_withdrawal_tables.sql create mode 100644 src/main/resources/db/migration/V42__withdrawal_methods_pid_and_icon_id.sql create mode 100644 src/main/resources/db/migration/V43__payouts_crypto_columns.sql create mode 100644 src/main/resources/db/migration/V44__payouts_payment_id.sql create mode 100644 src/main/resources/db/migration/V45__payouts_crypto_indexes.sql create mode 100644 src/main/resources/db/migration/V46__payouts_type_status_updated_at_index.sql create mode 100644 src/main/resources/db/migration/V47__payouts_drop_redundant_index.sql create mode 100644 src/main/resources/db/migration/V48__feature_switches_payment_payout.sql create mode 100644 src/main/resources/db/migration/V49__update_tasks_rewards_and_add_deposit_tasks.sql create mode 100644 src/main/resources/db/migration/V4__add_composite_index_for_completed_rounds.sql create mode 100644 src/main/resources/db/migration/V50__set_initial_balance_a_5_tickets.sql create mode 100644 src/main/resources/db/migration/V51__feature_switches_referral_tasks_50_100.sql create mode 100644 src/main/resources/db/migration/V52__bot_config_tables.sql create mode 100644 src/main/resources/db/migration/V53__admin_users_list_sort_indexes.sql create mode 100644 src/main/resources/db/migration/V54__lottery_bot_configs.sql create mode 100644 src/main/resources/db/migration/V55__feature_switch_lottery_bot_scheduler.sql create mode 100644 src/main/resources/db/migration/V56__create_promotions_tables.sql create mode 100644 src/main/resources/db/migration/V57__seed_first_net_win_promotion.sql create mode 100644 src/main/resources/db/migration/V58__add_promotions_total_reward.sql create mode 100644 src/main/resources/db/migration/V59__create_notifications_audit.sql create mode 100644 src/main/resources/db/migration/V5__add_screen_name_to_users_d.sql create mode 100644 src/main/resources/db/migration/V60__update_promotion_1_rewards.sql create mode 100644 src/main/resources/db/migration/V62__notifications_audit_user_created_index.sql create mode 100644 src/main/resources/db/migration/V63__system_settings_bot_max_participants.sql create mode 100644 src/main/resources/db/migration/V64__game_rounds_room_phase_started_at_index.sql create mode 100644 src/main/resources/db/migration/V65__feature_switch_manual_pay_for_all_payouts.sql create mode 100644 src/main/resources/db/migration/V66__payouts_add_txhash.sql create mode 100644 src/main/resources/db/migration/V67__admin_user_deposits_withdrawals_indexes.sql create mode 100644 src/main/resources/db/migration/V68__payouts_user_type_crypto_name_index.sql create mode 100644 src/main/resources/db/migration/V69__users_b_withdrawals_disabled.sql create mode 100644 src/main/resources/db/migration/V6__create_tasks_tables.sql create mode 100644 src/main/resources/db/migration/V70__seed_net_win_max_bet_and_ref_count_promotions.sql create mode 100644 src/main/resources/db/migration/V7__add_follow_and_other_tasks.sql create mode 100644 src/main/resources/db/migration/V8__create_payouts_table.sql create mode 100644 src/main/resources/db/migration/V9__add_quantity_to_payouts.sql create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/messages.properties create mode 100644 src/main/resources/messages_de.properties create mode 100644 src/main/resources/messages_es.properties create mode 100644 src/main/resources/messages_fr.properties create mode 100644 src/main/resources/messages_id.properties create mode 100644 src/main/resources/messages_it.properties create mode 100644 src/main/resources/messages_nl.properties create mode 100644 src/main/resources/messages_pl.properties create mode 100644 src/main/resources/messages_ru.properties create mode 100644 src/main/resources/messages_tr.properties diff --git a/ADMIN_SETUP.md b/ADMIN_SETUP.md new file mode 100644 index 0000000..a7e5996 --- /dev/null +++ b/ADMIN_SETUP.md @@ -0,0 +1,114 @@ +# Admin User Setup Guide + +This guide explains how to create an admin user in the database. + +## Prerequisites + +- Access to the MySQL database +- Spring Boot application running (to generate password hash) + +## Method 1: Using Spring Boot Application + +1. Create a simple test class or use the Spring Boot shell to generate a password hash: + +```java +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); +String hashedPassword = encoder.encode("your-secure-password"); +System.out.println(hashedPassword); +``` + +2. Connect to your MySQL database and run: + +```sql +-- Insert a new admin user into the admins table +INSERT INTO admins ( + username, + password_hash, + role +) VALUES ( + 'admin', + '$2a$10$YourGeneratedHashHere', + 'ROLE_ADMIN' +); +``` + +## Method 2: Using Online BCrypt Generator + +1. Use an online BCrypt generator (e.g., https://bcrypt-generator.com/) +2. Enter your desired password +3. Copy the generated hash +4. Use it in the SQL UPDATE/INSERT statement above + +## Method 3: Using Command Line (if bcrypt-cli is installed) + +```bash +bcrypt-cli hash "your-password" 10 +``` + +## Security Best Practices + +1. **Use Strong Passwords**: Minimum 12 characters with mix of letters, numbers, and symbols +2. **Change Default Credentials**: Never use default usernames/passwords in production +3. **Limit Admin Users**: Only create admin accounts for trusted personnel +4. **Regular Audits**: Periodically review admin users and their activity +5. **JWT Secret**: Ensure `APP_ADMIN_JWT_SECRET` in application.yml is set to a secure random string (minimum 32 characters) + +## Generate JWT Secret + +You can generate a secure JWT secret using: + +```bash +# Using OpenSSL +openssl rand -base64 32 + +# Or using Node.js +node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" +``` + +Then set it in your environment variable or application.yml: + +```yaml +app: + admin: + jwt: + secret: ${APP_ADMIN_JWT_SECRET:your-generated-secret-here} +``` + +## Testing Admin Login + +After setting up an admin user, test the login: + +```bash +curl -X POST https://win-spin.live/api/admin/login \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"your-password"}' +``` + +You should receive a response with a JWT token: + +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "username": "admin" +} +``` + +## Troubleshooting + +### "Invalid credentials" error +- Verify the password hash was generated correctly +- Check that the `username` in the `admins` table matches exactly (case-sensitive) +- Ensure the admin has `role = 'ROLE_ADMIN'` in the `admins` table + +### "Access Denied" after login +- Verify the JWT token is being sent in the Authorization header: `Bearer ` +- Check backend logs for authentication errors +- Verify CORS configuration includes your admin domain + +### Password hash format +- BCrypt hashes should start with `$2a$`, `$2b$`, or `$2y$` +- The hash should be 60 characters long +- Example format: `$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy` + diff --git a/APPLICATION_OVERVIEW.md b/APPLICATION_OVERVIEW.md new file mode 100644 index 0000000..c602d61 --- /dev/null +++ b/APPLICATION_OVERVIEW.md @@ -0,0 +1,1024 @@ +# Lottery Application - Complete Overview + +## Table of Contents +1. [Application Overview](#application-overview) +2. [Architecture](#architecture) +3. [Core Features](#core-features) +4. [Game Mechanics](#game-mechanics) +5. [Referral System](#referral-system) +6. [Tasks System](#tasks-system) +7. [Payment System](#payment-system) +8. [User Management](#user-management) +9. [Feature Switches](#feature-switches) +10. [Lottery Bot System](#lottery-bot-system) +11. [Database Schema](#database-schema) +12. [API Endpoints](#api-endpoints) + +--- + +## Application Overview + +The Lottery Application is a Telegram Mini App that provides a real-time lottery game experience. Users can participate in lottery rounds across multiple game rooms, complete tasks to earn rewards, refer friends to earn commissions, and manage their balance through Telegram Stars payments. + +**Technology Stack:** +- **Backend:** Java 17, Spring Boot 3.2.0, MySQL, WebSocket (STOMP) +- **Frontend:** React, Vite, WebSocket client +- **Authentication:** Telegram Mini App initData validation, JWT for admin panel +- **Payment:** Crypto deposits (external API), optional Telegram Stars (legacy); feature switch for deposits +- **Payout:** Crypto withdrawals (external API), STARS/GIFT (Telegram); feature switch for withdrawals +- **Infrastructure:** Docker, Nginx, VPS deployment + +**Domain:** `win-spin.live` + +--- + +## Architecture + +### Backend Architecture +- **Spring Boot REST API** with WebSocket support for real-time game updates +- **Dual Authentication System:** + - Telegram initData validation for app users (via `AuthInterceptor`) + - JWT-based authentication for admin panel (via Spring Security) +- **Session Management:** Bearer token-based sessions stored in `sessions` table +- **Real-time Communication:** WebSocket (STOMP) for game state broadcasts + +### Frontend Architecture +- **Main App (lottery-fe):** React SPA (Vite) deployed at `win-spin.live`. Path `/` shows a home/landing screen; `/auth` (or other paths) loads the authenticated app. Screens: Main (game), Game History, FAQ, Support, Support Chat, Referral, Transaction History, Store, Payment Options/Confirmation/Error, Tasks, Payout, Stars Payout Confirmation, Daily Bonus. Balance and user state from `GET /api/users/current`; game join and room state via WebSocket. i18n and Telegram WebApp integration. +- **Admin Panel (lottery-admin):** React SPA (Vite, React Router, TanStack Query) at `win-spin.live/{secret-path}/`. Routes: Login, Dashboard (stats), Rooms (list + RoomDetail), Feature Switchers, Configurations, Bots, Users (list + UserDetail), Payments, Payouts, Support Tickets (list + TicketDetail, Quick Answers), Masters, Analytics (User, Game, Financial, Referral). Role-based visibility: ROLE_ADMIN (all), ROLE_PAYOUT_SUPPORT (payouts), ROLE_TICKETS_SUPPORT (tickets + quick answers). +- **WebSocket Client (lottery-fe):** Real-time game state via STOMP; subscribe to `/topic/room/{roomNumber}/state`, send join via `/app/game/join`. + +### Database +- **MySQL** with Flyway migrations +- **Normalized Schema:** User data split across multiple tables (A, B, D) +- **Performance:** Indexed for high concurrency (1000+ concurrent users) + +--- + +## Core Features + +### 1. Real-Time Lottery Game +- **3 Game Rooms** with different bet limits: + - Room 1: 1-50 tickets + - Room 2: 10-500 tickets + - Room 3: 100-5000 tickets +- **Game Phases:** + - `WAITING`: Waiting for at least 2 players + - `COUNTDOWN`: 5-second countdown after 2nd player joins + - `SPINNING`: 5-second spinning animation + - `RESOLUTION`: Winner announcement +- **Winner Selection:** Random selection based on ticket distribution (weighted lottery) +- **House Commission:** 20% of total pool (80% distributed to winner) + +### 2. User Balance System +- **Balance A:** Main balance (used for betting, can be withdrawn) +- **Balance B:** Secondary balance (bonuses, rewards) +- **Currency:** Internal "tickets" system (1 ticket = 1,000,000 in bigint format) +- **Deposit Conversion:** 1 USD = 1,000 tickets (crypto deposits); legacy Telegram Stars conversion no longer used for new deposits +- **Withdrawal Limit:** Only winnings since last deposit can be withdrawn; tracked in `total_win_after_deposit` (reset on deposit, reduced when payout created) + +### 3. Telegram Integration +- **Telegram Mini App:** Full integration with Telegram WebView +- **Telegram Stars Payments:** Deposit via Telegram Stars +- **Telegram Bot:** Handles webhooks, channel membership verification +- **Avatar Management:** Downloads and stores user avatars from Telegram + +### 4. Multi-Language Support +- **i18n:** Full internationalization support +- **Languages:** Multiple language codes stored per user +- **Localization Service:** Dynamic message translation + +### 5. Support System +- **Support Tickets:** Users can create tickets +- **Support Messages:** Threaded conversations per ticket +- **Status Tracking:** OPENED/CLOSED status + +### 6. Admin Panel +- **Secret Path Access:** Accessible via `win-spin.live/{secret-path}/` +- **Role-Based Access:** `ROLE_ADMIN` (full access), `ROLE_PAYOUT_SUPPORT` (payouts only), `ROLE_TICKETS_SUPPORT` (support tickets and quick answers). Admins table stores `role` per account. +- **Dashboard:** Stats overview (total/new users, active players 24h/7d/30d, revenue and payouts in Stars and crypto USD, net revenue, rounds count, open support tickets). Served by `GET /api/admin/dashboard/stats`. +- **Rooms:** List all game rooms; room detail by room number (participants, phase, totals); repair round for stuck rooms (`POST /api/admin/rooms/{roomNumber}/repair`). +- **Configurations:** App configuration view for admins (`GET /api/admin/configurations`). +- **Bots:** Lottery bot configs management—create/update/delete bot configs (linked to real users), shuffle bot order. Scheduler (when `lottery_bot_scheduler_enabled` is on) auto-joins bots into joinable rounds per time window and persona. +- **User Management:** Paginated users list; user detail (profile, balance, transactions, game rounds, tasks); ban user; manual balance adjustment. +- **Feature Switchers:** Runtime toggles for remote_bet_enabled, payment_enabled, payout_enabled, lottery_bot_scheduler_enabled. +- **Payments:** List payments with filters (userId, status); crypto and legacy records. +- **Payouts:** List payouts with filters (userId, status, type); manual complete/cancel for PROCESSING or WAITING (CRYPTO status synced by cron). +- **Support Tickets:** List tickets, ticket detail with messages; close ticket. **Quick Answers:** Per-admin templates for fast replies (CRUD); access for ROLE_ADMIN and ROLE_TICKETS_SUPPORT. +- **Masters:** Referral masters view. +- **Analytics:** Revenue time series (crypto USD) and activity metrics (new users, rounds, revenue by period) for charts; range 7d/30d/90d/1y/all. +- **JWT Authentication:** Separate admin authentication system. +- **Admin Table:** Separate `admins` table; optional `user_id` linking to `db_users_a` for support messages. + +--- + +## Game Mechanics + +### Game Flow + +1. **User Joins Room:** + - User selects a room (1, 2, or 3) + - User places a bet (within room limits) + - Balance A is deducted immediately + - User becomes a participant + +2. **Round Start:** + - When 2nd player joins, countdown starts (5 seconds) + - During countdown, more players can join + - After countdown, spinning phase begins (5 seconds) + +3. **Winner Selection:** + - Winner selected randomly based on ticket distribution + - Probability = (user_tickets / total_tickets) + - House takes 20% commission + - Winner receives 80% of total pool + +4. **Round Resolution:** + - Winner's balance credited + - Referral commissions processed + - Round marked as completed + - New round starts automatically + +### Bet Limits Per Room + +| Room | Min Bet | Max Bet | Max Bet Per User | +|------|---------|---------|------------------| +| 1 | 1 | 50 | 50 | +| 2 | 10 | 500 | 500 | +| 3 | 100 | 5000 | 5000 | + +**Note:** All amounts stored in bigint format (1 ticket = 1,000,000) + +### Game Phases + +- **WAITING:** Room waiting for players (minimum 2 required) +- **COUNTDOWN:** 5-second countdown after 2nd player joins +- **SPINNING:** 5-second spinning animation (no new bets allowed) +- **RESOLUTION:** Winner announced, payouts processed + +### Real-Time Updates + +- **WebSocket Broadcasts:** All room state changes broadcast to connected clients +- **State Synchronization:** Clients receive full room state on connection +- **Participant Updates:** Real-time participant list and bet totals + +--- + +## Referral System + +### 3-Tier Referral Structure + +The referral system tracks up to 5 levels of referrers, but commissions are paid to the first 3 levels: + +**Referral Chain:** +- `referer_id_1`: Direct referrer (Level 1) +- `referer_id_2`: Referrer's referrer (Level 2) +- `referer_id_3`: Referrer's referrer's referrer (Level 3) +- `referer_id_4`: Level 4 (tracked but no commission) +- `referer_id_5`: Level 5 (tracked but no commission) + +### Commission Rates + +#### When Referral Wins: +- **All Levels:** 1% of referral's net profit +- **Net Profit Calculation:** (total_pool - house_commission) - referral_bet + +#### When Referral Loses: +- **Level 1:** 4% of referral's bet +- **Level 2:** 2% of referral's bet +- **Level 3:** 1% of referral's bet + +### Referral Statistics + +Each user tracks: +- **referals_1 to referals_5:** Count of direct referrals at each level +- **from_referals_1 to from_referals_5:** Total commissions earned from each level +- **to_referer_1 to to_referer_5:** Total commissions paid to referrers + +### Third Bet Bonus + +- Special bonus given to referrers when their referral makes their 3rd bet +- Uses `rounds_played` column (not transaction count) +- Prevents issues with transaction cleanup cron jobs + +--- + +## Tasks System + +### Task Types + +1. **Referral Tasks (`referral`):** + - Invite 1, 3, 7, 15, 30, 50, 100 friends + - Rewards: 2, 5, 15, 25, 40, 60, 150 Stars respectively + - One-time rewards (can only claim each tier once) + +2. **Follow Task (`follow`):** + - Follow the news channel + - Reward: 5 Stars + - Verified via Telegram Bot API + +3. **Deposit Task (`other`):** + - Top up balance: $5 + - Reward: 100 Stars + - Based on `deposit_total` field + +4. **Daily Bonus (`daily`):** + - Claim 1 free ticket every 24 hours + - Reward: 1 ticket + - Tracked in `user_daily_bonus_claims` table + +### Task Completion Logic + +- **Referral Tasks:** Counts `referals_1` from `db_users_d` +- **Follow Task:** Verified via Telegram Bot API channel membership check +- **Deposit Task:** Checks `deposit_total >= requirement` +- **Daily Bonus:** Checks if last claim was > 24 hours ago + +### Task Rewards + +- Rewards credited to **Balance A** (main balance) +- Transaction recorded in `transactions` table with type `TASK_BONUS` +- Task marked as claimed in `user_task_claims` table + +--- + +## Payment System + +### Crypto Deposits (Primary) + +Deposits are handled via an external crypto API (e.g. spin-passim.tech). The backend syncs deposit methods periodically and provides deposit addresses on demand; balance is credited when the 3rd party sends a completion webhook. + +**Deposit Flow:** +1. User enters USD amount on Store screen (min 2, max 10,000 USD; max 2 decimal places). +2. User selects a crypto method on Payment Options (methods from `crypto_deposit_methods`, synced every 10 min from external API). +3. Backend calls external API `POST api/v1/deposit-address` (pid, amountUsd, userData); no payment record is created yet. +4. User is shown wallet address and exact amount in coins to send (Payment Confirmation screen). +5. When the 3rd party detects the deposit, it calls `POST /api/deposit_webhook/{token}` with `user_id` and `usd_amount`. +6. Backend creates a COMPLETED payment record, credits balance (1 USD = 1,000 tickets in DB units: 1 ticket = 1,000,000), updates deposit_total/deposit_count, resets total_win_after_deposit, and creates a DEPOSIT transaction. + +**Payment Record (crypto):** +- `usd_amount`: decimal (e.g. 1.25 USD) +- `tickets_amount`: bigint (1 USD = 1,000 tickets → 1,000,000,000 in DB) +- `stars_amount`: 0 for crypto +- `status`: PENDING (if invoice created but not used), COMPLETED, FAILED, CANCELLED + +**Feature Switch:** When `payment_enabled` is off, deposit-related endpoints return 503 and the Store shows "deposits temporarily unavailable". + +### Telegram Stars (Legacy) + +- Creating new payments via Stars (invoice link) is no longer supported; the app uses crypto-only for new deposits. +- Telegram payment webhook is still processed for backward compatibility if a legacy payment is completed. +- Legacy conversion: Stars × 0.9 × 1,000,000 for tickets (no longer used for new flows). + +### Payout System + +**Payout Types:** +- **CRYPTO:** Primary. User selects withdrawal method (e.g. TON, LTC, TRX), enters wallet and amount in tickets. Backend calls external API `POST api/v1/withdrawal`; on success creates payout with `payment_id`, deducts balance, and a cron job syncs status from `GET api/v1/withdrawals-info/{payment_id}`. +- **STARS:** Convert tickets to Telegram Stars (fixed amounts; admin may process manually). +- **GIFT:** Convert to Telegram gifts (HEART, BEAR, etc.); admin may process manually. + +**Crypto Payout Flow:** +1. User opens Payout screen; backend returns withdrawal methods from `crypto_withdrawal_methods` (synced every 30 min from external API). +2. User selects method, enters wallet and amount (tickets). Frontend may call `GET withdrawal-method-details?pid=` for rate_usd and fee to show "You will receive". +3. User submits; backend acquires per-user lock, calls external API POST withdrawal, then creates payout (type CRYPTO) with payment_id, crypto_name, amount_coins, commission_coins, amount_to_send, wallet, status PROCESSING, and deducts balance. +4. Cron job (every minute) fetches PROCESSING/WAITING CRYPTO payouts with payment_id, calls withdrawals-info API, and updates status: -1→PROCESSING, 0→WAITING, 1→COMPLETED (marks completed, updates withdraw_total/count), 2→CANCELLED (refunds balance, creates cancellation transaction). + +**Payout Statuses:** +- `PROCESSING`: Created or being processed by external provider +- `WAITING`: Awaiting confirmation (from API status 0) +- `COMPLETED`: Resolved; user withdraw_total/count updated +- `CANCELLED`: Refunded to balance_a + +**Withdrawal Rules:** +- At least one deposit required before any withdrawal. +- Withdrawal amount cannot exceed `total_win_after_deposit` (winnings since last deposit). +- Crypto withdrawal amount must have at most 2 decimal places (in ticket units). +- One withdrawal at a time per user (in-memory lock). + +**Feature Switch:** When `payout_enabled` is off, withdrawal-related endpoints return 503 and the Payout screen shows "withdrawals temporarily unavailable". + +--- + +## User Management + +### User Data Structure + +User data is split across 3 tables for normalization: + +**`db_users_a` (User Authentication & Profile):** +- Basic user info (Telegram ID, name, language, country) +- Registration and login timestamps +- Ban status +- Avatar URL + +**`db_users_b` (User Balance & Financial):** +- Balance A (main balance) +- Balance B (bonus balance) +- Deposit/withdraw totals and counts +- Rounds played counter +- **total_win_after_deposit:** Total winnings since last deposit (bigint). Reset to 0 on each deposit; incremented when user wins a round; reduced when user creates a payout. Used to enforce withdrawal limit (only this amount can be withdrawn). + +**`db_users_d` (User Referral Data):** +- Referral chain (5 levels) +- Referral statistics +- Commission tracking + +### User Sessions + +- **Session Management:** Bearer token-based +- **Multi-Device Support:** Up to 5 active sessions per user +- **Session Expiration:** Configurable TTL +- **Automatic Cleanup:** Cron job removes expired sessions + +### User Authentication + +- **Telegram initData Validation:** Validates Telegram Mini App authentication +- **Session Creation:** Creates session token on first login +- **Session Validation:** `AuthInterceptor` validates all API requests + +- **Current User API:** Returns `paymentEnabled` and `payoutEnabled` from feature switches (used by frontend to show/hide or disable Store and Payout). + +--- + +## Feature Switches + +Runtime toggles stored in `feature_switches` table. Changes take effect immediately without restart. Configurable from the admin panel. + +| Key | Description | +|-----|-------------| +| `remote_bet_enabled` | When on, 3rd party can register users to rounds via GET `/api/remotebet/{token}?user_id=&room=&amount=`. When off, endpoint returns 503. | +| `payment_enabled` | When on, deposits (Store, Payment Options, deposit-address, create invoice) are allowed. When off, related API endpoints return 503 and Store shows "deposits temporarily unavailable". Default: 1. | +| `payout_enabled` | When on, withdrawals (Payout screen, withdrawal-methods, withdrawal-method-details, crypto-withdrawal) are allowed. When off, related endpoints return 503 and Payout shows "withdrawals temporarily unavailable". Default: 1. | +| `lottery_bot_scheduler_enabled` | When on, the lottery bot scheduler auto-joins configured bots (from `lottery_bot_configs`) into joinable rounds within their time windows and persona. When off, scheduler skips registration. Default: 1. | + +--- + +## Lottery Bot System + +Bots are **real users** (from `db_users_a`/`db_users_b`) with a config row in `lottery_bot_configs`. The scheduler runs periodically and, when `lottery_bot_scheduler_enabled` is on, registers these users into active rounds via the same join flow as real players (balance deducted, etc.). + +### Bot Config (per user) + +- **Rooms:** Flags `room_1`, `room_2`, `room_3` (which rooms the bot can play). +- **Time window (UTC):** `time_utc_start`, `time_utc_end`—bot only plays within this window. +- **Bet range:** `bet_min`, `bet_max` in bigint (1 ticket = 1,000,000). +- **Persona:** `conservative`, `aggressive`, or `balanced`—used by the bet decision service to choose bet size and timing. +- **Active:** Toggle to enable/disable this bot without deleting the config. + +One config per user; `user_id` is unique. Admin can create/update/delete configs and shuffle bot order via the Bots page. + +### Scheduler + +- When the feature switch is on, a scheduled job evaluates active configs, checks time windows and room state, and places bets for bots using the bet decision service (persona-based logic). +- Bots participate in the same game flow as human players (rounds, referral logic, etc.). + +--- + +## Database Schema + +### Core Tables + +#### `sessions` +User session management for Bearer token authentication. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `session_id_hash` | VARCHAR(255) | Hashed session ID (unique) | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `created_at` | TIMESTAMP | Session creation time | +| `expires_at` | TIMESTAMP | Session expiration time | + +**Indexes:** +- `idx_session_hash` on `session_id_hash` +- `idx_expires_at` on `expires_at` +- `idx_user_id` on `user_id` +- `idx_user_created` on `(user_id, created_at)` + +#### `db_users_a` +User authentication and profile information. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key, auto-increment | +| `screen_name` | VARCHAR(75) | User's screen name | +| `telegram_id` | BIGINT | Telegram user ID (unique) | +| `telegram_name` | VARCHAR(33) | Telegram username | +| `is_premium` | INT | Telegram Premium status (0/1) | +| `language_code` | VARCHAR(2) | User's language code | +| `country_code` | VARCHAR(2) | User's country code | +| `device_code` | VARCHAR(5) | Device type code | +| `ip` | VARBINARY(16) | User's IP address (IPv4/IPv6) | +| `date_reg` | INT | Registration timestamp (Unix) | +| `date_login` | INT | Last login timestamp (Unix) | +| `banned` | INT | Ban status (0/1) | +| `avatar_url` | VARCHAR(500) | Avatar URL with cache busting | +| `last_telegram_file_id` | VARCHAR(255) | Telegram file ID for avatar | + +**Indexes:** +- Primary key on `id` +- Unique key on `telegram_id` +- Index on `telegram_name` +- Index on `ip` + +#### `db_users_b` +User balance and financial information. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key (matches `db_users_a.id`) | +| `balance_a` | BIGINT UNSIGNED | Main balance (tickets in bigint format) | +| `balance_b` | BIGINT UNSIGNED | Bonus balance (tickets in bigint format) | +| `deposit_total` | BIGINT | Total deposits (in bigint format) | +| `deposit_count` | INT | Number of deposits | +| `withdraw_total` | BIGINT | Total withdrawals (in bigint format) | +| `withdraw_count` | INT | Number of withdrawals | +| `rounds_played` | INT | Number of rounds user participated in | +| `total_win_after_deposit` | BIGINT | Winnings since last deposit (1 ticket = 1,000,000). Reset on deposit; increased on win; decreased when payout created. Withdrawal limit. | + +**Indexes:** +- Primary key on `id` + +**Note:** `balance_a` and `balance_b` store values in bigint format where 1 ticket = 1,000,000 + +#### `db_users_d` +User referral data and statistics. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key (matches `db_users_a.id`) | +| `referer_id_1` | INT | Level 1 referrer ID | +| `referer_id_2` | INT | Level 2 referrer ID | +| `referer_id_3` | INT | Level 3 referrer ID | +| `referer_id_4` | INT | Level 4 referrer ID | +| `referer_id_5` | INT | Level 5 referrer ID | +| `master_id` | INT | Master referrer ID | +| `referals_1` | INT | Count of level 1 referrals | +| `referals_2` | INT | Count of level 2 referrals | +| `referals_3` | INT | Count of level 3 referrals | +| `referals_4` | INT | Count of level 4 referrals | +| `referals_5` | INT | Count of level 5 referrals | +| `from_referals_1` | BIGINT | Commissions earned from level 1 | +| `from_referals_2` | BIGINT | Commissions earned from level 2 | +| `from_referals_3` | BIGINT | Commissions earned from level 3 | +| `from_referals_4` | BIGINT | Commissions earned from level 4 | +| `from_referals_5` | BIGINT | Commissions earned from level 5 | +| `to_referer_1` | BIGINT | Commissions paid to level 1 referrer | +| `to_referer_2` | BIGINT | Commissions paid to level 2 referrer | +| `to_referer_3` | BIGINT | Commissions paid to level 3 referrer | +| `to_referer_4` | BIGINT | Commissions paid to level 4 referrer | +| `to_referer_5` | BIGINT | Commissions paid to level 5 referrer | + +**Indexes:** +- Primary key on `id` +- Indexes on `referer_id_1` through `referer_id_5` +- Index on `master_id` + +### Game Tables + +#### `game_rooms` +Active game rooms (Room 1, 2, 3). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key, auto-increment | +| `room_number` | INT | Room number (1, 2, or 3), unique | +| `current_phase` | VARCHAR(20) | Current game phase (WAITING, COUNTDOWN, SPINNING, RESOLUTION) | +| `countdown_end_at` | TIMESTAMP | Countdown end time (NULL if not counting down) | +| `total_bet` | BIGINT UNSIGNED | Total tickets bet in current round | +| `registered_players` | INT | Number of players in current round | +| `created_at` | TIMESTAMP | Room creation time | +| `updated_at` | TIMESTAMP | Last update time | + +**Indexes:** +- Primary key on `id` +- Unique key on `room_number` +- Index on `current_phase` +- Index on `countdown_end_at` + +#### `game_rounds` +Completed game rounds history. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `room_id` | INT | Foreign key to `game_rooms.id` | +| `phase` | VARCHAR(20) | Final phase (usually RESOLUTION) | +| `total_bet` | BIGINT UNSIGNED | Total tickets/bet in the round | +| `winner_user_id` | INT | Winner's user ID (NULL if no winner) | +| `winner_bet` | BIGINT UNSIGNED | Winner's ticket/bet count | +| `commission` | BIGINT UNSIGNED | House commission (20% of total) | +| `payout` | BIGINT UNSIGNED | Winner's payout (80% of total) | +| `started_at` | TIMESTAMP | Round start time | +| `countdown_started_at` | TIMESTAMP | Countdown start time | +| `countdown_ended_at` | TIMESTAMP | Countdown end time | +| `resolved_at` | TIMESTAMP | Resolution time | +| `created_at` | TIMESTAMP | Record creation time | + +**Indexes:** +- Primary key on `id` +- Index on `room_id` +- Index on `winner_user_id` +- Index on `resolved_at` +- Foreign key to `game_rooms(id)` + +#### `game_round_participants` +Participants in each game round. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `round_id` | BIGINT | Foreign key to `game_rounds.id` | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `tickets` | BIGINT UNSIGNED | User's total tickets in this round | +| `joined_at` | TIMESTAMP | When user joined the round | + +**Indexes:** +- Primary key on `id` +- Index on `round_id` +- Index on `user_id` +- Composite index on `(round_id, user_id)` +- Foreign keys to `game_rounds(id)` and `db_users_a(id)` + +### Task Tables + +#### `tasks` +Available tasks for users to complete. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key, auto-increment | +| `type` | VARCHAR(20) | Task type: `referral`, `follow`, `other`, `daily` | +| `requirement` | INT | Requirement value (e.g., number of referrals, deposit amount) | +| `reward_amount` | BIGINT | Reward amount in bigint format | +| `reward_type` | VARCHAR(20) | Reward type: `Stars`, `Tickets`, `Power` | +| `display_order` | INT | Display order in UI | +| `title` | VARCHAR(255) | Task title | +| `description` | TEXT | Task description | + +**Indexes:** +- Primary key on `id` +- Index on `type` +- Index on `display_order` + +**Default Tasks:** +- **Referral:** Invite 1, 3, 7, 15, 30, 50, 100 friends (rewards: 2, 5, 15, 25, 40, 60, 150 Stars) +- **Follow:** Follow news channel (reward: 5 Stars) +- **Deposit:** Top up $5 (reward: 100 Stars) +- **Daily:** Daily bonus (reward: 1 ticket, claimable every 24h) + +#### `user_task_claims` +Tracks which tasks users have claimed. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `task_id` | INT | Foreign key to `tasks.id` | +| `claimed_at` | TIMESTAMP | When task was claimed | + +**Indexes:** +- Primary key on `id` +- Unique composite index on `(user_id, task_id)` +- Index on `user_id` +- Index on `task_id` +- Foreign keys to `db_users_a(id)` and `tasks(id)` + +#### `user_daily_bonus_claims` +Tracks daily bonus claims (optimized table to avoid JOINs). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `avatar_url` | VARCHAR(255) | User's avatar URL (denormalized) | +| `screen_name` | VARCHAR(75) | User's screen name (denormalized) | +| `claimed_at` | TIMESTAMP | Claim time | + +**Indexes:** +- Primary key on `id` +- Index on `user_id` +- Index on `claimed_at` (DESC) +- Foreign key to `db_users_a(id)` + +### Feature Switches Table + +#### `feature_switches` +Runtime feature toggles (e.g. remote bet, deposits, withdrawals). Admin can change without restart. + +| Column | Type | Description | +|--------|------|-------------| +| `key` | VARCHAR(64) | Primary key (e.g. `remote_bet_enabled`, `payment_enabled`, `payout_enabled`) | +| `enabled` | TINYINT(1) | 0 or 1 | +| `updated_at` | TIMESTAMP | Last update | + +### Crypto Deposit Tables + +#### `crypto_deposit_config` +Single row (id=1): minimum_deposit (decimal), methods_hash for sync skip when unchanged. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key (1) | +| `methods_hash` | VARCHAR(255) | Hash from external API; skip sync when unchanged | +| `minimum_deposit` | DECIMAL(10,2) | Minimum USD (e.g. 2.50) | +| `updated_at` | TIMESTAMP | Last update | + +#### `crypto_deposit_methods` +One row per active deposit method (synced from external API every 10 min). `pid` = icon filename without extension (e.g. 235.png). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key | +| `pid` | INT | External API PID (unique) | +| `name` | VARCHAR(100) | e.g. TON, USDT | +| `network` | VARCHAR(50) | e.g. TON, TRC20 | +| `example` | VARCHAR(255) | Example address | +| `min_deposit_sum` | DECIMAL(10,2) | Min USD for this method | +| `updated_at` | TIMESTAMP | Last update | + +### Crypto Withdrawal Tables + +#### `crypto_withdrawal_methods` +One row per active withdrawal method (synced from external API every 30 min). `pid` = method id; `icon_id` = icon filename (e.g. 30.png). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key | +| `pid` | INT | Method ID (unique) | +| `name` | VARCHAR(50) | Ticker (e.g. LTC, TON) | +| `network` | VARCHAR(100) | Network name | +| `icon_id` | VARCHAR(20) | Icon filename without extension | +| `min_withdrawal` | DECIMAL(10,2) | Min withdrawal USD | +| `updated_at` | TIMESTAMP | Last update | + +### Payment Tables + +#### `payments` +Payment records (crypto and legacy Telegram Stars). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `order_id` | VARCHAR(255) | Unique order ID | +| `stars_amount` | INT | Telegram Stars (0 for crypto) | +| `usd_amount` | DECIMAL(20,2) | USD amount (crypto; e.g. 1.25) | +| `tickets_amount` | BIGINT UNSIGNED | Tickets in bigint (1 USD = 1,000 tickets → 1,000,000,000 per USD) | +| `status` | VARCHAR(20) | `PENDING`, `COMPLETED`, `FAILED`, `CANCELLED` | +| `telegram_payment_charge_id` | VARCHAR(255) | Telegram (legacy) | +| `telegram_provider_payment_charge_id` | VARCHAR(255) | Telegram (legacy) | +| `created_at` | TIMESTAMP | Creation time | +| `completed_at` | TIMESTAMP | Completion time | + +**Indexes:** +- Primary key on `id` +- Unique key on `order_id` +- Index on `user_id` +- Index on `status` +- Index on `created_at` +- Composite index on `(user_id, status)` +- Foreign key to `db_users_a(id)` + +#### `payouts` +Withdrawal/payout requests (CRYPTO, STARS, GIFT). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `username` | VARCHAR(255) | User's username (for STARS/GIFT) | +| `wallet` | VARCHAR(120) | Wallet address (CRYPTO) | +| `type` | VARCHAR(20) | `CRYPTO`, `STARS`, `GIFT` | +| `gift_name` | VARCHAR(50) | Gift type for GIFT (HEART, BEAR, etc.) | +| `crypto_name` | VARCHAR(20) | Ticker for CRYPTO (e.g. TRX, TON) | +| `total` | BIGINT UNSIGNED | Tickets in bigint format | +| `stars_amount` | INT | Stars amount (0 for CRYPTO) | +| `usd_amount` | DECIMAL(20,2) | USD equivalent (CRYPTO) | +| `amount_coins` | VARCHAR(50) | Withdrawal amount in coins (from API) | +| `commission_coins` | VARCHAR(50) | Commission in coins (from API) | +| `amount_to_send` | VARCHAR(50) | Final amount to send (from API) | +| `payment_id` | INT | Crypto API payment id (for status sync) | +| `quantity` | INT | Quantity (e.g. gifts); default 1 | +| `status` | VARCHAR(20) | `PROCESSING`, `WAITING`, `COMPLETED`, `CANCELLED` | +| `created_at` | TIMESTAMP | Request time | +| `updated_at` | TIMESTAMP | Last update (touched by cron sync) | +| `resolved_at` | TIMESTAMP | Resolution time | + +**Indexes:** +- Primary key on `id` +- Index on `user_id` +- Index on `status` +- Index on `type` +- Index on `created_at` +- Index on `updated_at` +- Composite indexes for CRYPTO status sync +- Foreign key to `db_users_a(id)` + +#### `transactions` +Transaction history (all balance changes). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `amount` | BIGINT | Amount in bigint format (positive=credit, negative=debit) | +| `type` | VARCHAR(50) | Type: `DEPOSIT`, `WITHDRAWAL`, `WIN`, `LOSS`, `TASK_BONUS`, `CANCELLATION_OF_WITHDRAWAL` | +| `task_id` | INT | Task ID for `TASK_BONUS` type (nullable) | +| `round_id` | BIGINT | Round ID for `WIN`/`LOSS` type (nullable) | +| `created_at` | TIMESTAMP | Transaction time | + +**Indexes:** +- Primary key on `id` +- Composite index on `(user_id, created_at DESC)` +- Composite index on `(user_id, type)` +- Foreign key to `db_users_a(id)` + +**Note:** Transactions older than 30 days are automatically cleaned up by cron job. + +### Support Tables + +#### `support_tickets` +User support tickets. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `subject` | VARCHAR(100) | Ticket subject | +| `status` | ENUM | Status: `OPENED`, `CLOSED` | +| `created_at` | TIMESTAMP | Ticket creation time | +| `updated_at` | TIMESTAMP | Last update time | + +**Indexes:** +- Primary key on `id` +- Index on `user_id` +- Index on `status` +- Composite index on `(user_id, status)` +- Index on `created_at` +- Foreign key to `db_users_a(id)` + +#### `support_messages` +Messages within support tickets. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | BIGINT | Primary key, auto-increment | +| `ticket_id` | BIGINT | Foreign key to `support_tickets.id` | +| `user_id` | INT | Foreign key to `db_users_a.id` | +| `message` | VARCHAR(2000) | Message content | +| `created_at` | TIMESTAMP | Message time | + +**Indexes:** +- Primary key on `id` +- Index on `ticket_id` +- Index on `user_id` +- Composite index on `(ticket_id, created_at)` +- Foreign keys to `support_tickets(id)` and `db_users_a(id)` + +### Admin Tables + +#### `admins` +Admin user accounts (separate from regular users). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key, auto-increment | +| `user_id` | INT | Optional FK to `db_users_a.id` (for support messages) | +| `username` | VARCHAR(50) | Admin username (unique) | +| `password_hash` | VARCHAR(255) | BCrypt hashed password | +| `role` | VARCHAR(20) | Role: `ROLE_ADMIN`, `ROLE_PAYOUT_SUPPORT`, `ROLE_TICKETS_SUPPORT` | +| `created_at` | TIMESTAMP | Account creation time | +| `updated_at` | TIMESTAMP | Last update time | + +**Indexes:** +- Primary key on `id` +- Unique key on `username` +- Index on `role` +- Index on `user_id` + +#### `lottery_bot_configs` +Bot behaviour config: one row per user; user must exist in `db_users_a`. + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key, auto-increment | +| `user_id` | INT | FK to `db_users_a.id` (unique) | +| `room_1` | TINYINT(1) | Can play room 1 (0/1) | +| `room_2` | TINYINT(1) | Can play room 2 (0/1) | +| `room_3` | TINYINT(1) | Can play room 3 (0/1) | +| `time_utc_start` | TIME | Start of active window (UTC) | +| `time_utc_end` | TIME | End of active window (UTC) | +| `bet_min` | BIGINT | Min bet in bigint (1 ticket = 1,000,000) | +| `bet_max` | BIGINT | Max bet in bigint | +| `persona` | VARCHAR(20) | `conservative`, `aggressive`, `balanced` | +| `active` | TINYINT(1) | 1 = enabled, 0 = disabled | +| `created_at` | TIMESTAMP | Creation time | +| `updated_at` | TIMESTAMP | Last update time | + +**Indexes:** +- Unique key on `user_id` +- Index on `(active, room_1, room_2, room_3)` + +#### `quick_answers` +Admin quick-response templates for support (per admin). + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INT | Primary key, auto-increment | +| `admin_id` | INT | FK to `admins.id` | +| `text` | TEXT | Template text | +| `created_at` | TIMESTAMP | Creation time | +| `updated_at` | TIMESTAMP | Last update time | + +**Indexes:** +- Primary key on `id` +- Index on `admin_id` +- Index on `(admin_id, created_at)` + +--- + +## API Endpoints + +### Public Endpoints + +- `POST /api/auth/tma/session` - Create session from Telegram initData +- `GET /api/check_user/{token}/{telegramId}` - Check user info (external API). Token is configured via `APP_CHECK_USER_TOKEN` (set on server only, not in repo). +- `POST /api/deposit_webhook/{token}` - 3rd party deposit completion (no auth). Body: `user_id`, `usd_amount`. Token via `APP_DEPOSIT_WEBHOOK_TOKEN`. + +### Authenticated Endpoints (Bearer Token) + +#### Game (REST) +- `GET /api/game/room/{roomNumber}/completed-rounds` - Last 10 completed rounds for a room +- `GET /api/game/history` - Get game history for current user +- **Join and room state:** Via WebSocket (see WebSocket section below), not REST. + +#### User +- `GET /api/users/current` - Get current user info (includes balance, `paymentEnabled`, `payoutEnabled` from feature switches) +- `GET /api/users/referrals` - Get referral info +- `POST /api/users/deposit` - Legacy/optional deposit flow + +#### Tasks +- `GET /api/tasks?type=` - Get available tasks (query: `type` = referral, follow, other, etc.) +- `GET /api/tasks/daily-bonus` - Daily bonus status +- `GET /api/tasks/daily-bonus/recent-claims` - Recent daily bonus claims (optional query: `timezone`) +- `POST /api/tasks/claim` - Claim task reward (body: `taskId`) + +#### Payments (deposits) +- `GET /api/payments/minimum-deposit` - Min deposit USD from config (503 if payment disabled) +- `GET /api/payments/deposit-methods` - Crypto deposit methods from DB (503 if payment disabled) +- `POST /api/payments/deposit-address` - Get crypto deposit address/amount from external API (pid, usdAmount) +- `POST /api/payments/create` - Create payment invoice (crypto: usdAmount; legacy Stars no longer supported) (503 if payment disabled) +- `POST /api/payments/cancel` - Cancel payment by orderId + +#### Payouts (withdrawals) +- `GET /api/payments/withdrawal-methods` - Crypto withdrawal methods from DB (503 if payout disabled) +- `GET /api/payments/withdrawal-method-details?pid=` - Rate/fee for method from external API (503 if payout disabled) +- `POST /api/payments/crypto-withdrawal` - Create crypto withdrawal (pid, wallet, total in tickets); creates payout and deducts balance (503 if payout disabled) +- `POST /api/payouts` - Request STARS or GIFT payout +- `GET /api/payouts/history` - Last 20 payouts for current user + +#### Support +- `GET /api/support/tickets` - Get user's tickets +- `POST /api/support/tickets` - Create ticket +- `GET /api/support/tickets/{ticketId}` - Get ticket with messages +- `POST /api/support/tickets/{ticketId}/messages` - Send message +- `POST /api/support/tickets/{ticketId}/close` - Close ticket + +#### Transactions +- `GET /api/transactions` - Get current user's transaction history + +### Admin Endpoints (JWT Token) + +All admin paths are under `/api/admin/`. Role requirements: dashboard, rooms, configurations, bots, users, payments, payouts, feature-switches, analytics require ADMIN; payouts also allow ROLE_PAYOUT_SUPPORT; tickets and quick-answers allow ROLE_TICKETS_SUPPORT. + +- `POST /api/admin/login` - Admin login +- `GET /api/admin/dashboard/stats` - Dashboard stats (users, active players, revenue/payouts in Stars and crypto USD, rounds, support tickets) +- `GET /api/admin/rooms` - List all game rooms +- `GET /api/admin/rooms/{roomNumber}` - Room detail (participants, phase, etc.) +- `POST /api/admin/rooms/{roomNumber}/repair` - Repair stuck round +- `GET /api/admin/configurations` - App configurations +- `GET /api/admin/bots` - List lottery bot configs +- `GET /api/admin/bots/{id}` - Get bot config by id +- `POST /api/admin/bots` - Create bot config +- `PUT /api/admin/bots/{id}` - Update bot config +- `DELETE /api/admin/bots/{id}` - Delete bot config +- `POST /api/admin/bots/shuffle?roomNumber=` - Shuffle bot time windows for room (query: `roomNumber` = 2 or 3) +- `GET /api/admin/users` - Paginated users list (50 per page; sort supported) +- `GET /api/admin/users/{id}` - User detail +- `GET /api/admin/users/{id}/transactions` - User transactions +- `GET /api/admin/users/{id}/game-rounds` - User game rounds +- `GET /api/admin/users/{id}/tasks` - User task claims +- `PATCH /api/admin/users/{id}/ban` - Ban/unban user (body: `banned`) +- `POST /api/admin/users/{id}/balance/adjust` - Manual balance adjustment +- `GET /api/admin/feature-switches` - List all feature switches +- `PATCH /api/admin/feature-switches/{key}` - Update feature switch (body: `enabled`) +- `GET /api/admin/payments` - Paginated payments (filter by userId, status) +- `GET /api/admin/payouts` - Paginated payouts (filter by userId, status, type) +- `POST /api/admin/payouts/{id}/complete` - Mark payout COMPLETED +- `POST /api/admin/payouts/{id}/cancel` - Mark payout CANCELLED (refund) +- `GET /api/admin/analytics/revenue` - Revenue/payout time series (query: `range` = 7d, 30d, 90d, 1y, all) +- `GET /api/admin/analytics/activity` - Activity metrics for charts +- `GET /api/admin/quick-answers` - List quick answers for current admin +- `POST /api/admin/quick-answers` - Create quick answer (body: `text`) +- `PUT /api/admin/quick-answers/{id}` - Update quick answer (own only) +- `DELETE /api/admin/quick-answers/{id}` - Delete quick answer (own only) + +### WebSocket + +- **Endpoint:** `/ws` (STOMP over SockJS or WebSocket) +- **Subscribe:** `/topic/room/{roomNumber}/state` - Room state updates (full state on subscribe; broadcasts on phase/participant changes) +- **Send (join round):** `/app/game/join` - Payload: roomNumber, amount (tickets in bigint). Join is handled via WebSocket, not REST. + +--- + +## Key Business Logic + +### Balance Format + +All monetary values in balance/transaction/payout totals are stored in **bigint format** with 6 decimal places: +- 1 Ticket = 1,000,000 (in database) +- **Crypto deposits:** 1 USD = 1,000 tickets → 1,000,000,000 in database per USD +- Legacy: 1 Telegram Star = 900,000 (0.9 conversion; no longer used for new deposits) +- Example: 50 tickets = 50,000,000 in database + +### Winner Selection Algorithm + +1. Calculate total tickets in round +2. Generate random number between 0 and total_tickets +3. Iterate through participants, accumulating ticket counts +4. First participant whose accumulated tickets >= random number wins +5. This creates a weighted lottery (more tickets = higher chance) + +### House Commission + +- **Rate:** 20% of total pool +- **Calculation:** `commission = total_tickets × 0.2` +- **Winner Payout:** `payout = total_tickets - commission` + +### Referral Commission Calculation + +**When Referral Wins:** +``` +net_profit = (total_pool - house_commission) - referral_bet +commission = net_profit × 0.01 // 1% for all levels +``` + +**When Referral Loses:** +``` +Level 1: commission = referral_bet × 0.04 // 4% +Level 2: commission = referral_bet × 0.02 // 2% +Level 3: commission = referral_bet × 0.01 // 1% +``` + +### Data Cleanup + +- **Transactions:** Cleaned up after 30 days (cron job) +- **Game Rounds:** Old rounds cleaned up periodically +- **Sessions:** Expired sessions cleaned up in batches + +### Crypto API Integration + +- **Deposits:** External API base URL and API key via `app.crypto-api.base-url` and `app.crypto-api.api-key`. Deposit methods synced every 10 min (cron); deposit address on demand (POST deposit-address). Completion via `POST /api/deposit_webhook/{token}` (token: `app.deposit-webhook.token`). +- **Withdrawals:** Withdrawal methods synced every 30 min (cron). POST withdrawal creates payout; status sync cron every minute calls GET withdrawals-info for PROCESSING/WAITING CRYPTO payouts and updates status (COMPLETED/CANCELLED trigger balance and transaction updates). + +--- + +## Security Features + +1. **Telegram Authentication:** Validates Telegram Mini App initData +2. **Session Management:** Secure Bearer token sessions +3. **Rate Limiting:** Per-user and per-endpoint rate limiting +4. **CORS Protection:** Configured for specific origins +5. **Admin Authentication:** Separate JWT-based admin system +6. **Input Validation:** All user inputs validated +7. **SQL Injection Protection:** JPA/Hibernate parameterized queries +8. **XSS Protection:** Content Security Policy headers + +--- + +## Performance Optimizations + +1. **Database Indexes:** Comprehensive indexing for fast queries +2. **Connection Pooling:** HikariCP with optimized pool size +3. **WebSocket Keep-Alive:** Efficient real-time communication +4. **Batch Operations:** Bulk cleanup operations +5. **Caching:** In-memory caching for active game rounds +6. **Nginx Caching:** Static asset caching (1 year TTL) +7. **Composite Indexes:** Optimized for common query patterns + +--- + +## Deployment + +- **Backend:** Docker container on VPS +- **Frontend:** Static files served by Nginx +- **Admin Panel:** Static files at `win-spin.live/{secret-path}/` +- **Database:** MySQL on VPS +- **SSL:** Let's Encrypt certificates +- **Domain:** `win-spin.live` + +--- + +This document provides a comprehensive overview of the Lottery Application. For specific implementation details, refer to the source code in the respective service classes and repositories. + + diff --git a/BACKUP_SETUP.md b/BACKUP_SETUP.md new file mode 100644 index 0000000..ed0b682 --- /dev/null +++ b/BACKUP_SETUP.md @@ -0,0 +1,327 @@ +# Database Backup Setup Guide + +This guide explains how to set up automated database backups from your main VPS to your backup VPS. + +## Overview + +- **Main VPS**: 37.1.206.220 (production server) +- **Backup VPS**: 5.45.77.77 (backup storage) +- **Backup Location**: `/raid/backup/acc_260182/` on backup VPS +- **Database**: MySQL 8.0 in Docker container `lottery-mysql` +- **Database Name**: `lottery_db` + +## Prerequisites + +1. SSH access to both VPS servers +2. Root or sudo access on main VPS +3. Write access to `/raid/backup/acc_260182/` on backup VPS + +## Step 1: Set Up SSH Key Authentication + +To enable passwordless transfers, set up SSH key authentication between your main VPS and backup VPS. + +### On Main VPS (37.1.206.220): + +```bash +# Generate SSH key pair (if you don't have one) +ssh-keygen -t ed25519 -C "backup@lottery-main-vps" -f ~/.ssh/backup_key + +# Copy public key to backup VPS +ssh-copy-id -i ~/.ssh/backup_key.pub root@5.45.77.77 + +# Test connection +ssh -i ~/.ssh/backup_key root@5.45.77.77 "echo 'SSH connection successful'" +``` + +**Note**: If you already have an SSH key, you can use it instead. The script uses the default SSH key (`~/.ssh/id_rsa` or `~/.ssh/id_ed25519`). + +### Alternative: Use Existing SSH Key + +If you want to use an existing SSH key, you can either: +1. Use the default key (no changes needed) +2. Configure SSH to use a specific key by editing `~/.ssh/config`: + +```bash +cat >> ~/.ssh/config << EOF +Host backup-vps + HostName 5.45.77.77 + User root + IdentityFile ~/.ssh/backup_key +EOF +``` + +Then update the backup script to use `backup-vps` as the hostname. + +## Step 2: Configure Backup Script + +The backup script is located at `scripts/backup-database.sh`. It's already configured with: +- Backup VPS: `5.45.77.77` +- Backup path: `/raid/backup/acc_260182` +- MySQL container: `lottery-mysql` +- Database: `lottery_db` + +If you need to change the backup VPS user (default: `root`), edit the script: + +```bash +nano scripts/backup-database.sh +# Change: BACKUP_VPS_USER="root" to your user +``` + +## Step 3: Make Scripts Executable + +```bash +cd /opt/app/backend/lottery-be +chmod +x scripts/backup-database.sh +chmod +x scripts/restore-database.sh +``` + +## Step 4: Test Manual Backup + +Run a test backup to ensure everything works: + +```bash +# Test backup (keeps local copy for verification) +./scripts/backup-database.sh --keep-local + +# Check backup on remote VPS +ssh root@5.45.77.77 "ls -lh /raid/backup/acc_260182/ | tail -5" +``` + +## Step 5: Set Up Automated Backups (Cron) + +Set up a cron job to run backups automatically. Recommended schedule: **daily at 2 AM**. + +### Option A: Edit Crontab Directly + +```bash +# Edit root's crontab +sudo crontab -e + +# Add this line (daily at 2 AM): +0 2 * * * /opt/app/backend/lottery-be/scripts/backup-database.sh >> /opt/app/logs/backup.log 2>&1 +``` + +### Option B: Create Cron Script + +Create a wrapper script for better logging: + +```bash +cat > /opt/app/backend/lottery-be/scripts/run-backup.sh << 'EOF' +#!/bin/bash +# Wrapper script for automated backups +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_FILE="/opt/app/logs/backup.log" + +# Ensure log directory exists +mkdir -p "$(dirname "$LOG_FILE")" + +# Run backup and log output +"${SCRIPT_DIR}/backup-database.sh" >> "$LOG_FILE" 2>&1 + +# Send email notification on failure (optional, requires mail setup) +if [ $? -ne 0 ]; then + echo "Backup failed at $(date)" | mail -s "Lottery DB Backup Failed" your-email@example.com +fi +EOF + +chmod +x /opt/app/backend/lottery-be/scripts/run-backup.sh +``` + +Then add to crontab: + +```bash +sudo crontab -e +# Add: +0 2 * * * /opt/app/backend/lottery-be/scripts/run-backup.sh +``` + +### Recommended Backup Schedules + +- **Daily at 2 AM**: `0 2 * * *` (recommended) +- **Twice daily (2 AM and 2 PM)**: `0 2,14 * * *` +- **Every 6 hours**: `0 */6 * * *` +- **Weekly (Sunday 2 AM)**: `0 2 * * 0` + +## Step 6: Verify Automated Backups + +After setting up cron, verify it's working: + +```bash +# Check cron job is scheduled +sudo crontab -l + +# Check backup logs +tail -f /opt/app/logs/backup.log + +# List recent backups on remote VPS +ssh root@5.45.77.77 "ls -lht /raid/backup/acc_260182/ | head -10" +``` + +## Backup Retention + +The backup script automatically: +- **Keeps last 30 days** of backups on remote VPS +- **Deletes local backups** after successful transfer (unless `--keep-local` is used) + +To change retention period, edit `scripts/backup-database.sh`: + +```bash +# Change this line: +ssh "${BACKUP_VPS_USER}@${BACKUP_VPS_HOST}" "find ${BACKUP_VPS_PATH} -name 'lottery_db_backup_*.sql*' -type f -mtime +30 -delete" + +# To keep 60 days, change +30 to +60 +``` + +## Restoring from Backup + +### Restore from Remote Backup + +```bash +# Restore from backup VPS +./scripts/restore-database.sh 5.45.77.77:/raid/backup/acc_260182/lottery_db_backup_20240101_020000.sql.gz +``` + +### Restore from Local Backup + +```bash +# If you kept a local backup +./scripts/restore-database.sh /opt/app/backups/lottery_db_backup_20240101_020000.sql.gz +``` + +**⚠️ WARNING**: Restore will **DROP and RECREATE** the database. All existing data will be lost! + +## Backup Script Options + +```bash +# Standard backup (compressed, no local copy) +./scripts/backup-database.sh + +# Keep local copy after transfer +./scripts/backup-database.sh --keep-local + +# Don't compress backup (faster, but larger files) +./scripts/backup-database.sh --no-compress +``` + +## Monitoring Backups + +### Check Backup Status + +```bash +# View recent backup logs +tail -50 /opt/app/logs/backup.log + +# Count backups on remote VPS +ssh root@5.45.77.77 "ls -1 /raid/backup/acc_260182/lottery_db_backup_*.sql* | wc -l" + +# List all backups with sizes +ssh root@5.45.77.77 "ls -lh /raid/backup/acc_260182/lottery_db_backup_*.sql*" +``` + +### Backup Health Check Script + +Create a simple health check: + +```bash +cat > /opt/app/backend/lottery-be/scripts/check-backup-health.sh << 'EOF' +#!/bin/bash +# Check if backups are running successfully + +BACKUP_VPS="5.45.77.77" +BACKUP_PATH="/raid/backup/acc_260182" +DAYS_THRESHOLD=2 # Alert if no backup in last 2 days + +LAST_BACKUP=$(ssh root@${BACKUP_VPS} "ls -t ${BACKUP_PATH}/lottery_db_backup_*.sql* 2>/dev/null | head -1") + +if [ -z "$LAST_BACKUP" ]; then + echo "❌ ERROR: No backups found on backup VPS!" + exit 1 +fi + +LAST_BACKUP_DATE=$(ssh root@${BACKUP_VPS} "stat -c %Y ${LAST_BACKUP}") +CURRENT_DATE=$(date +%s) +DAYS_SINCE_BACKUP=$(( (CURRENT_DATE - LAST_BACKUP_DATE) / 86400 )) + +if [ $DAYS_SINCE_BACKUP -gt $DAYS_THRESHOLD ]; then + echo "⚠️ WARNING: Last backup is $DAYS_SINCE_BACKUP days old!" + exit 1 +else + echo "✅ Backup health OK: Last backup $DAYS_SINCE_BACKUP day(s) ago" + exit 0 +fi +EOF + +chmod +x /opt/app/backend/lottery-be/scripts/check-backup-health.sh +``` + +## Troubleshooting + +### SSH Connection Issues + +```bash +# Test SSH connection +ssh -v root@5.45.77.77 "echo 'test'" + +# Check SSH key permissions +chmod 600 ~/.ssh/id_rsa +chmod 644 ~/.ssh/id_rsa.pub +``` + +### Permission Denied on Backup VPS + +```bash +# Verify write access +ssh root@5.45.77.77 "touch /raid/backup/acc_260182/test && rm /raid/backup/acc_260182/test && echo 'Write access OK'" +``` + +### MySQL Container Not Running + +```bash +# Check container status +docker ps | grep lottery-mysql + +# Start container if needed +cd /opt/app/backend/lottery-be +docker-compose -f docker-compose.prod.yml up -d db +``` + +### Backup Script Permission Denied + +```bash +# Make script executable +chmod +x scripts/backup-database.sh +``` + +## Backup File Naming + +Backups are named with timestamp: `lottery_db_backup_YYYYMMDD_HHMMSS.sql.gz` + +Example: `lottery_db_backup_20240115_020000.sql.gz` + +## Disk Space Considerations + +- **Compressed backups**: Typically 10-50% of database size +- **Uncompressed backups**: Same size as database +- **30-day retention**: Plan for ~30x daily backup size + +Monitor disk space on backup VPS: + +```bash +ssh root@5.45.77.77 "df -h /raid/backup/acc_260182" +``` + +## Security Notes + +1. **SSH Keys**: Use SSH key authentication (no passwords) +2. **Secret File**: Database password is read from `/run/secrets/lottery-config.properties` (secure) +3. **Backup Files**: Contain sensitive data - ensure backup VPS is secure +4. **Permissions**: Backup script requires root access to read secrets + +## Next Steps + +1. ✅ Set up SSH key authentication +2. ✅ Test manual backup +3. ✅ Set up cron job for automated backups +4. ✅ Monitor backup logs for first few days +5. ✅ Test restore procedure (on test environment first!) + diff --git a/BACKUP_TROUBLESHOOTING.md b/BACKUP_TROUBLESHOOTING.md new file mode 100644 index 0000000..0090a23 --- /dev/null +++ b/BACKUP_TROUBLESHOOTING.md @@ -0,0 +1,492 @@ +# Backup Script Permission Denied - Troubleshooting Guide + +## Error Message +``` +/bin/sh: 1: /opt/app/backend/lottery-be/scripts/backup-database.sh: Permission denied +``` + +This error occurs when the system cannot execute the script, even if you've already run `chmod +x`. Here's a systematic approach to find the root cause. + +--- + +## Step 1: Verify File Permissions + +### Check Current Permissions +```bash +ls -la /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +**Expected output:** +``` +-rwxr-xr-x 1 root root 5678 Jan 15 10:00 /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +**What to look for:** +- The `x` (execute) permission should be present for owner, group, or others +- If you see `-rw-r--r--` (no `x`), the file is not executable + +**Fix if needed:** +```bash +chmod +x /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +--- + +## Step 2: Check File System Mount Options + +The file system might be mounted with `noexec` flag, which prevents executing scripts. + +### Check Mount Options +```bash +mount | grep -E "(/opt|/app|/backend)" +``` + +**What to look for:** +- If you see `noexec` in the mount options, that's the problem +- Example of problematic mount: `/dev/sda1 on /opt type ext4 (rw,noexec,relatime)` + +**Fix:** +1. Check `/etc/fstab`: + ```bash + cat /etc/fstab | grep -E "(/opt|/app)" + ``` +2. If `noexec` is present, remove it and remount: + ```bash + # Edit fstab (remove noexec) + sudo nano /etc/fstab + + # Remount (if /opt is a separate partition) + sudo mount -o remount /opt + ``` +3. **Note:** If `/opt` is part of the root filesystem, you may need to reboot + +--- + +## Step 3: Check Line Endings (CRLF vs LF) + +Windows line endings (CRLF) can cause "Permission denied" errors on Linux. + +### Check Line Endings +```bash +file /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +**Expected output:** +``` +/opt/app/backend/lottery-be/scripts/backup-database.sh: Bourne-Again shell script, ASCII text executable +``` + +**If you see:** +``` +/opt/app/backend/lottery-be/scripts/backup-database.sh: ASCII text, with CRLF line terminators +``` + +**Fix:** +```bash +# Convert CRLF to LF +dos2unix /opt/app/backend/lottery-be/scripts/backup-database.sh + +# Or using sed +sed -i 's/\r$//' /opt/app/backend/lottery-be/scripts/backup-database.sh + +# Or using tr +tr -d '\r' < /opt/app/backend/lottery-be/scripts/backup-database.sh > /tmp/backup-database.sh +mv /tmp/backup-database.sh /opt/app/backend/lottery-be/scripts/backup-database.sh +chmod +x /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +--- + +## Step 4: Verify Shebang Line + +The shebang line must point to a valid interpreter. + +### Check Shebang +```bash +head -1 /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +**Expected:** +```bash +#!/bin/bash +``` + +**Verify bash exists:** +```bash +which bash +ls -la /bin/bash +``` + +**If bash doesn't exist or path is wrong:** +```bash +# Find bash location +which bash +# or +whereis bash + +# Update shebang if needed (bash is usually at /bin/bash or /usr/bin/bash) +``` + +--- + +## Step 5: Check SELinux (if enabled) + +SELinux can block script execution even with correct permissions. + +### Check if SELinux is Enabled +```bash +getenforce +``` + +**Outputs:** +- `Enforcing` - SELinux is active and blocking +- `Permissive` - SELinux is active but only logging +- `Disabled` - SELinux is off + +### Check SELinux Context +```bash +ls -Z /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +**Fix if SELinux is blocking:** +```bash +# Set correct context for shell scripts +chcon -t bin_t /opt/app/backend/lottery-be/scripts/backup-database.sh + +# Or restore default context +restorecon -v /opt/app/backend/lottery-be/scripts/backup-database.sh + +# Or temporarily set to permissive (for testing only) +setenforce 0 +``` + +--- + +## Step 6: Check AppArmor (if enabled) + +AppArmor can also block script execution. + +### Check AppArmor Status +```bash +aa-status +``` + +**If AppArmor is active and blocking:** +```bash +# Check AppArmor logs +sudo dmesg | grep -i apparmor +sudo journalctl -u apparmor | tail -20 + +# Temporarily disable for testing (not recommended for production) +sudo systemctl stop apparmor +``` + +--- + +## Step 7: Verify Cron Job User + +The cron job might be running as a different user than expected. + +### Check Cron Job +```bash +# Check root's crontab +sudo crontab -l + +# Check if cron job specifies a user +# Example: 0 2 * * * root /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +### Check Which User Runs Cron +```bash +# Check cron service logs +sudo journalctl -u cron | tail -20 + +# Or check syslog +sudo grep CRON /var/log/syslog | tail -10 +``` + +**Important:** The script requires root access (line 71-74 checks for EUID=0). Make sure cron runs as root: + +```bash +# Edit root's crontab (correct way) +sudo crontab -e + +# NOT user's crontab +# crontab -e # This runs as current user, not root +``` + +--- + +## Step 8: Test Script Execution Manually + +Test the script with the same user that cron uses. + +### Test as Root +```bash +# Test directly +sudo /opt/app/backend/lottery-be/scripts/backup-database.sh + +# Test with bash explicitly +sudo bash /opt/app/backend/lottery-be/scripts/backup-database.sh + +# Test with sh (if bash is not available) +sudo sh /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +**If manual execution works but cron doesn't:** +- The issue is likely with cron's environment or user context +- See Step 9 for cron environment issues + +--- + +## Step 9: Check Cron Environment + +Cron has a minimal environment. The script might need specific environment variables or paths. + +### Check Script Dependencies +The script uses: +- `docker` command +- `ssh` command +- `gzip` command +- `/run/secrets/lottery-config.properties` file + +### Verify Commands are in PATH +```bash +# Check if commands are accessible +which docker +which ssh +which gzip +which bash + +# If commands are not in standard PATH, update cron job: +# Add PATH to cron job: +0 2 * * * PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /opt/app/backend/lottery-be/scripts/backup-database.sh >> /opt/app/logs/backup.log 2>&1 +``` + +### Test Cron Environment +Create a test cron job to see the environment: + +```bash +# Add to crontab +* * * * * env > /tmp/cron-env.txt + +# Wait 1 minute, then check +cat /tmp/cron-env.txt +``` + +--- + +## Step 10: Check Directory Permissions + +The directory containing the script must be executable. + +### Check Directory Permissions +```bash +ls -ld /opt/app/backend/lottery-be/scripts/ +``` + +**Expected:** +``` +drwxr-xr-x 2 root root 4096 Jan 15 10:00 /opt/app/backend/lottery-be/scripts/ +``` + +**If directory is not executable:** +```bash +chmod +x /opt/app/backend/lottery-be/scripts/ +``` + +--- + +## Step 11: Check for Hidden Characters + +Hidden characters or encoding issues can break the shebang. + +### View File in Hex +```bash +head -c 20 /opt/app/backend/lottery-be/scripts/backup-database.sh | od -c +``` + +**Expected:** +``` +0000000 # ! / b i n / b a s h \n +``` + +**If you see strange characters:** +```bash +# Recreate the shebang line +sed -i '1s/.*/#!\/bin\/bash/' /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +--- + +## Step 12: Comprehensive Diagnostic Script + +Run this diagnostic script to check all common issues: + +```bash +cat > /tmp/check-backup-script.sh << 'EOF' +#!/bin/bash +echo "=== Backup Script Diagnostic ===" +echo "" + +SCRIPT="/opt/app/backend/lottery-be/scripts/backup-database.sh" + +echo "1. File exists?" +[ -f "$SCRIPT" ] && echo " ✅ Yes" || echo " ❌ No" + +echo "2. File permissions:" +ls -la "$SCRIPT" + +echo "3. File is executable?" +[ -x "$SCRIPT" ] && echo " ✅ Yes" || echo " ❌ No" + +echo "4. Shebang line:" +head -1 "$SCRIPT" + +echo "5. Bash exists?" +[ -f /bin/bash ] && echo " ✅ Yes: /bin/bash" || [ -f /usr/bin/bash ] && echo " ✅ Yes: /usr/bin/bash" || echo " ❌ No" + +echo "6. Line endings:" +file "$SCRIPT" + +echo "7. Mount options for /opt:" +mount | grep -E "(/opt|/app)" || echo " (Not a separate mount)" + +echo "8. SELinux status:" +getenforce 2>/dev/null || echo " (Not installed)" + +echo "9. Directory permissions:" +ls -ld "$(dirname "$SCRIPT")" + +echo "10. Test execution:" +bash -n "$SCRIPT" && echo " ✅ Syntax OK" || echo " ❌ Syntax error" + +echo "" +echo "=== End Diagnostic ===" +EOF + +chmod +x /tmp/check-backup-script.sh +/tmp/check-backup-script.sh +``` + +--- + +## Step 13: Alternative Solutions + +If the issue persists, try these workarounds: + +### Solution A: Use bash Explicitly in Cron +```bash +# Instead of: +0 2 * * * /opt/app/backend/lottery-be/scripts/backup-database.sh + +# Use: +0 2 * * * /bin/bash /opt/app/backend/lottery-be/scripts/backup-database.sh +``` + +### Solution B: Create Wrapper Script +```bash +cat > /opt/app/backend/lottery-be/scripts/run-backup-wrapper.sh << 'EOF' +#!/bin/bash +cd /opt/app/backend/lottery-be +exec /opt/app/backend/lottery-be/scripts/backup-database.sh "$@" +EOF + +chmod +x /opt/app/backend/lottery-be/scripts/run-backup-wrapper.sh + +# Update cron to use wrapper +0 2 * * * /opt/app/backend/lottery-be/scripts/run-backup-wrapper.sh >> /opt/app/logs/backup.log 2>&1 +``` + +### Solution C: Use systemd Timer Instead of Cron +```bash +# Create systemd service +cat > /etc/systemd/system/lottery-backup.service << 'EOF' +[Unit] +Description=Lottery Database Backup +After=network.target + +[Service] +Type=oneshot +ExecStart=/opt/app/backend/lottery-be/scripts/backup-database.sh +User=root +StandardOutput=append:/opt/app/logs/backup.log +StandardError=append:/opt/app/logs/backup.log +EOF + +# Create systemd timer +cat > /etc/systemd/system/lottery-backup.timer << 'EOF' +[Unit] +Description=Run Lottery Database Backup Daily +Requires=lottery-backup.service + +[Timer] +OnCalendar=02:00 +Persistent=true + +[Install] +WantedBy=timers.target +EOF + +# Enable and start +systemctl daemon-reload +systemctl enable lottery-backup.timer +systemctl start lottery-backup.timer +``` + +--- + +## Most Common Causes (Quick Reference) + +1. **Line endings (CRLF)** - Most common if file was edited on Windows +2. **File system mounted with `noexec`** - Check mount options +3. **Cron running as wrong user** - Must run as root (use `sudo crontab -e`) +4. **SELinux/AppArmor blocking** - Check security contexts +5. **Missing execute permission** - Run `chmod +x` again +6. **Directory not executable** - Check parent directory permissions + +--- + +## Quick Fix Checklist + +Run these commands in order: + +```bash +# 1. Fix line endings +dos2unix /opt/app/backend/lottery-be/scripts/backup-database.sh +# OR if dos2unix not available: +sed -i 's/\r$//' /opt/app/backend/lottery-be/scripts/backup-database.sh + +# 2. Ensure execute permission +chmod +x /opt/app/backend/lottery-be/scripts/backup-database.sh + +# 3. Ensure directory is executable +chmod +x /opt/app/backend/lottery-be/scripts/ + +# 4. Test execution +sudo /opt/app/backend/lottery-be/scripts/backup-database.sh --keep-local + +# 5. Verify cron job uses bash explicitly +sudo crontab -e +# Change to: 0 2 * * * /bin/bash /opt/app/backend/lottery-be/scripts/backup-database.sh >> /opt/app/logs/backup.log 2>&1 +``` + +--- + +## Still Not Working? + +If none of the above fixes work, provide the output of: + +```bash +# Run diagnostic +/tmp/check-backup-script.sh + +# Check cron logs +sudo journalctl -u cron | tail -50 + +# Check system logs +sudo dmesg | tail -20 +``` + +This will help identify the exact issue. + diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..9634328 --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -0,0 +1,765 @@ +# VPS Deployment Guide for Lottery Application + +This guide will help you deploy the Lottery application to a VPS (Ubuntu) using Docker, Docker Compose, and Nginx. + +## Prerequisites + +- Ubuntu VPS (tested on Ubuntu 20.04+) +- Root or sudo access +- Domain name pointing to your VPS IP (for HTTPS) +- Basic knowledge of Linux commands + +## Architecture Overview + +``` +Internet + ↓ +Nginx (HTTPS, Port 443) + ↓ + ├─→ Frontend (Static files from /opt/app/frontend/dist) + ├─→ Backend API (/api/* → Docker container on port 8080) + ├─→ WebSocket (/ws → Docker container) + └─→ Avatars (/avatars/* → /opt/app/data/avatars) +``` + +## Step 1: Initial VPS Setup + +### 1.1 Update System + +```bash +sudo apt update +sudo apt upgrade -y +``` + +### 1.2 Install Required Software + +```bash +# Install Docker +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh +sudo usermod -aG docker $USER + +# Docker Compose v2+ is included with Docker (as a plugin) +# Verify it's installed: +docker compose version + +# If not installed, install Docker Compose plugin: +# For Ubuntu/Debian: +sudo apt-get update +sudo apt-get install docker-compose-plugin + +# Or if you need the standalone version (older method): +# sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +# sudo chmod +x /usr/local/bin/docker-compose + +# Install Nginx +sudo apt install nginx -y + +# Install Certbot for SSL certificates +sudo apt install certbot python3-certbot-nginx -y + +# Log out and log back in for Docker group to take effect +exit +``` + +## Step 2: Create Directory Structure + +```bash +# Create main application directory +sudo mkdir -p /opt/app +sudo chown $USER:$USER /opt/app + +# Create subdirectories +mkdir -p /opt/app/backend +mkdir -p /opt/app/frontend +mkdir -p /opt/app/nginx +mkdir -p /opt/app/data/avatars +mkdir -p /opt/app/mysql/data + +# Set proper permissions +sudo chmod -R 755 /opt/app +sudo chown -R $USER:$USER /opt/app/data +``` + +## Step 3: Deploy Backend + +### 3.1 Copy Backend Files + +From your local machine, copy the backend repository to the VPS: + +```bash +# On your local machine, use scp or rsync +scp -r lottery-be/* user@your-vps-ip:/opt/app/backend/ + +# Or use git (recommended) +# On VPS: +cd /opt/app/backend +git clone . +``` + +### 3.2 Plan Database Configuration + +**Important:** MySQL runs as a Docker container (no separate MySQL installation needed). Before creating the secret file, you need to decide on your database credentials: + +1. **Database Name**: `lottery_db` (default, can be changed) +2. **Database Username**: `root` (default, can be changed) +3. **Database Password**: Choose a strong, secure password +4. **Database URL**: `jdbc:mysql://db:3306/lottery_db` + +**Understanding the Database URL (`SPRING_DATASOURCE_URL`):** + +The URL format is: `jdbc:mysql://:/` + +**For this deployment, use: `jdbc:mysql://db:3306/lottery_db`** + +Breaking it down: +- `jdbc:mysql://` - JDBC protocol for MySQL +- `db` - This is the **service name** in `docker-compose.prod.yml` (acts as hostname in Docker network) +- `3306` - Default MySQL port (internal to Docker network) +- `lottery_db` - Database name (must match `MYSQL_DATABASE` in docker-compose) + +**Why `db` as hostname?** +- In Docker Compose, services communicate using their **service names** as hostnames +- The MySQL service is named `db` in `docker-compose.prod.yml` (line 4: `services: db:`) +- Both containers are on the same Docker network (`lottery-network`) +- The backend container connects to MySQL using `db:3306` (not `localhost` or the VPS IP) +- This is an **internal Docker network connection** - MySQL is not exposed to the host + +**Quick Reference:** +- ✅ Correct: `jdbc:mysql://db:3306/lottery_db` (uses service name) +- ❌ Wrong: `jdbc:mysql://localhost:3306/lottery_db` (won't work - localhost refers to the container itself) +- ❌ Wrong: `jdbc:mysql://127.0.0.1:3306/lottery_db` (won't work - same reason) + +**Example credentials (use your own secure password!):** +- Database URL: `jdbc:mysql://db:3306/lottery_db` +- Database Name: `lottery_db` +- Username: `root` +- Password: `MySecurePassword123!` + +**Note:** These credentials will be used in: +- The secret file (`SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD`) +- MySQL container environment variables (`DB_PASSWORD`, `DB_ROOT_PASSWORD`) + +The MySQL container will be created automatically when you run `docker-compose`, and the database will be initialized with these credentials. + +### 3.3 Create Secret Configuration File + +The application uses a mounted secret file instead of environment variables for security. Create the secret file: + +**Option 1: Copy from template (if template file exists)** + +```bash +# Create the secrets directory (if it doesn't exist) +sudo mkdir -p /run/secrets + +# Navigate to backend directory +cd /opt/app/backend + +# Check if template file exists +ls -la lottery-config.properties.template + +# If it exists, copy it +sudo cp lottery-config.properties.template /run/secrets/lottery-config.properties +``` + +**Option 2: Create the file directly (if template wasn't copied)** + +If the template file doesn't exist in `/opt/app/backend/`, create the secret file directly: + +```bash +# Create the secrets directory (if it doesn't exist) +sudo mkdir -p /run/secrets + +# Create the secret file +sudo nano /run/secrets/lottery-config.properties +``` + +Then paste the following content (replace placeholder values): + +```properties +# Lottery Application Configuration +# Replace all placeholder values with your actual configuration + +# ============================================ +# Database Configuration +# ============================================ +# SPRING_DATASOURCE_URL format: jdbc:mysql://:/ +# +# How to determine the URL: +# - Hostname: 'db' (this is the MySQL service name in docker-compose.prod.yml) +# * In Docker Compose, services communicate using their service names +# * The MySQL service is named 'db', so use 'db' as the hostname +# * Both containers are on the same Docker network, so 'db' resolves to the MySQL container +# - Port: '3306' (default MySQL port, internal to Docker network) +# - Database name: 'lottery_db' (must match MYSQL_DATABASE in docker-compose.prod.yml) +# +# Example: jdbc:mysql://db:3306/lottery_db +# └─┬─┘ └┬┘ └─┬──┘ └───┬────┘ +# │ │ │ └─ Database name +# │ │ └─ Port (3306 is MySQL default) +# │ └─ Service name in docker-compose (acts as hostname) +# └─ JDBC protocol for MySQL +SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/lottery_db +SPRING_DATASOURCE_USERNAME=root +SPRING_DATASOURCE_PASSWORD=your_secure_database_password_here + +# ============================================ +# Telegram Bot Configuration +# ============================================ +TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here +TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN=your_channel_checker_bot_token_here +TELEGRAM_FOLLOW_TASK_CHANNEL_ID=@your_channel_name + +# ============================================ +# Frontend Configuration +# ============================================ +FRONTEND_URL=https://yourdomain.com + +# ============================================ +# Avatar Storage Configuration +# ============================================ +APP_AVATAR_STORAGE_PATH=/app/data/avatars +APP_AVATAR_PUBLIC_BASE_URL= +APP_AVATAR_MAX_SIZE_BYTES=2097152 +APP_AVATAR_MAX_DIMENSION=512 + +# ============================================ +# Session Configuration (Optional - defaults shown) +# ============================================ +APP_SESSION_MAX_ACTIVE_PER_USER=5 +APP_SESSION_CLEANUP_BATCH_SIZE=5000 +APP_SESSION_CLEANUP_MAX_BATCHES=20 + +# ============================================ +# GeoIP Configuration (Optional) +# ============================================ +GEOIP_DB_PATH= +``` + +**Edit the secret file with your actual values:** + +```bash +sudo nano /run/secrets/lottery-config.properties +``` + +**Important:** Replace all placeholder values with your actual configuration: + +```properties +# Database Configuration +SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/lottery_db +SPRING_DATASOURCE_USERNAME=root +SPRING_DATASOURCE_PASSWORD=your_secure_database_password_here + +# Telegram Bot Configuration +TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here +TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN=your_channel_checker_bot_token_here +TELEGRAM_FOLLOW_TASK_CHANNEL_ID=@your_channel_name + +# Frontend Configuration +FRONTEND_URL=https://yourdomain.com + +# Avatar Storage Configuration +APP_AVATAR_STORAGE_PATH=/app/data/avatars +APP_AVATAR_PUBLIC_BASE_URL= + +# Optional: Session Configuration (defaults shown) +APP_SESSION_MAX_ACTIVE_PER_USER=5 +APP_SESSION_CLEANUP_BATCH_SIZE=5000 +APP_SESSION_CLEANUP_MAX_BATCHES=20 + +# Optional: GeoIP Configuration +GEOIP_DB_PATH= +``` + +**Set secure permissions:** + +```bash +# Make the file readable only by root and the docker group +sudo chmod 640 /run/secrets/lottery-config.properties +sudo chown root:docker /run/secrets/lottery-config.properties +``` + +**Important Notes:** +- The database credentials you set here (`SPRING_DATASOURCE_*`) must match the MySQL container environment variables (see Step 3.4) +- The MySQL container will be created automatically when you run `docker-compose` +- The database `lottery_db` will be created automatically on first startup +- Data will persist in a Docker volume (`mysql_data`) + +### 3.4 Set MySQL Container Environment Variables + +The MySQL container needs the database password as environment variables. These must match the credentials in your secret file. + +**Option 1: Read from secret file automatically (recommended - more secure and consistent)** + +Use the provided script that reads the password from the secret file: + +```bash +cd /opt/app/backend + +# Make sure the script is executable +chmod +x scripts/load-db-password.sh + +# Source the script to load the password (this exports DB_PASSWORD and DB_ROOT_PASSWORD) +source scripts/load-db-password.sh +``` + +**What this does:** +- Reads `SPRING_DATASOURCE_PASSWORD` from `/run/secrets/lottery-config.properties` +- Exports `DB_PASSWORD` and `DB_ROOT_PASSWORD` with the same value +- Ensures MySQL container credentials match the backend credentials automatically + +**Verify it worked:** +```bash +# Check that the variables are set +echo "DB_PASSWORD is set: $([ -n "$DB_PASSWORD" ] && echo "yes" || echo "no")" +``` + +**Option 2: Set environment variables manually (simpler but less secure)** + +If you prefer to set them manually (not recommended): + +```bash +# Export the password (must match SPRING_DATASOURCE_PASSWORD from your secret file) +# Replace with the actual password you set in the secret file +export DB_PASSWORD=your_secure_database_password_here +export DB_ROOT_PASSWORD=your_secure_database_password_here + +# Verify it's set +echo $DB_PASSWORD +``` + +**Important Notes:** +- The `DB_PASSWORD` and `DB_ROOT_PASSWORD` must match `SPRING_DATASOURCE_PASSWORD` from your secret file +- These environment variables are only used by the MySQL container +- The backend application reads credentials from the secret file, not from environment variables +- **Option 1 is recommended** because it ensures consistency and reduces the chance of mismatched passwords + +### 3.5 Build and Start Backend + +**Before starting:** Make sure you have: +- ✅ Secret file created at `/run/secrets/lottery-config.properties` with database credentials +- ✅ Environment variables `DB_PASSWORD` and `DB_ROOT_PASSWORD` set (use `source scripts/load-db-password.sh` from Step 3.4) + +```bash +cd /opt/app/backend + +# Make sure DB_PASSWORD and DB_ROOT_PASSWORD are set (if not already done in Step 3.4) +# If you haven't sourced the script yet, do it now: +source scripts/load-db-password.sh + +# Build and start services +docker compose -f docker-compose.prod.yml up -d --build + +# Check logs (press Ctrl+C to exit) +docker compose -f docker-compose.prod.yml logs -f +``` + +**What happens when you start:** + +1. **MySQL container starts first** (`lottery-mysql`) + - Creates the database `lottery_db` automatically (if it doesn't exist) + - Sets up the root user with your password from `DB_PASSWORD` + - Waits until healthy before backend starts + +2. **Backend container starts** (`lottery-backend`) + - Loads configuration from `/run/secrets/lottery-config.properties` + - Connects to MySQL using credentials from secret file + - Runs Flyway migrations to create all database tables + - Starts the Spring Boot application + +**Wait for the database to be ready and migrations to complete.** You should see: +- `lottery-mysql` container running +- `lottery-backend` container running +- Log message: "📁 Loading configuration from mounted secret file: /run/secrets/lottery-config.properties" +- Database migration messages (Flyway creating tables) +- No errors in logs + +**Verify everything is working:** + +```bash +# Check that both containers are running +docker ps | grep lottery + +# Check backend logs for secret file loading +docker compose -f docker-compose.prod.yml logs backend | grep "Loading configuration" + +# Check backend logs for database connection +docker compose -f docker-compose.prod.yml logs backend | grep -i "database\|mysql\|flyway" + +# Check for any errors +docker compose -f docker-compose.prod.yml logs backend | grep -i error +``` + +You should see: +- `📁 Loading configuration from mounted secret file: /run/secrets/lottery-config.properties` +- `Flyway migration` messages showing tables being created +- No connection errors + +**If you see connection errors:** +- Verify `SPRING_DATASOURCE_PASSWORD` in secret file matches `DB_PASSWORD` environment variable +- Re-run the password loading script: `source scripts/load-db-password.sh` +- Check that MySQL container is healthy: `docker ps | grep mysql` +- Check MySQL logs: `docker compose -f docker-compose.prod.yml logs db` + +## Step 4: Build and Deploy Frontend + +### 4.1 Build Frontend Locally (Recommended) + +On your local machine: + +```bash +cd lottery-fe + +# Build for production (uses relative API URLs by default) +npm install +npm run build + +# The dist/ folder will be created +``` + +### 4.2 Copy Frontend Build to VPS + +```bash +# On your local machine +scp -r lottery-fe/dist/* user@your-vps-ip:/opt/app/frontend/dist/ + +# Or use rsync +rsync -avz lottery-fe/dist/ user@your-vps-ip:/opt/app/frontend/dist/ +``` + +### 4.3 Alternative: Build on VPS + +If you prefer to build on the VPS: + +```bash +# Install Node.js on VPS +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt install -y nodejs + +# Copy frontend source +scp -r lottery-fe/* user@your-vps-ip:/opt/app/frontend-source/ + +# Build +cd /opt/app/frontend-source +npm install +npm run build + +# Copy dist to frontend directory +cp -r dist/* /opt/app/frontend/dist/ +``` + +## Step 5: Configure Nginx + +### 5.1 Copy Nginx Configuration + +```bash +# Copy the template +cp /opt/app/backend/nginx.conf.template /opt/app/nginx/nginx.conf + +# Edit the configuration +nano /opt/app/nginx/nginx.conf +``` + +**Update the following:** +1. Replace `server_name _;` with your domain name (e.g., `server_name yourdomain.com;`) +2. Update SSL certificate paths if using Let's Encrypt (see Step 6) +3. Verify paths match your directory structure + +### 5.2 Link Nginx Configuration + +```bash +# Remove default Nginx config +sudo rm /etc/nginx/sites-enabled/default + +# Create symlink to your config +sudo ln -s /opt/app/nginx/nginx.conf /etc/nginx/sites-available/lottery +sudo ln -s /etc/nginx/sites-available/lottery /etc/nginx/sites-enabled/ + +# Test Nginx configuration +sudo nginx -t + +# If test passes, reload Nginx +sudo systemctl reload nginx +``` + +## Step 6: Setup SSL Certificate (HTTPS) + +### 6.1 Obtain SSL Certificate + +```bash +# Stop Nginx temporarily +sudo systemctl stop nginx + +# Obtain certificate (replace with your domain and email) +sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com --email your-email@example.com --agree-tos + +# Start Nginx +sudo systemctl start nginx +``` + +### 6.2 Update Nginx Config with Certificate Paths + +Edit `/opt/app/nginx/nginx.conf` and update: + +```nginx +ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; +``` + +### 6.3 Setup Auto-Renewal + +```bash +# Test renewal +sudo certbot renew --dry-run + +# Certbot will automatically renew certificates +``` + +## Step 7: Configure Telegram Webhook + +Update your Telegram bot webhook to point to your VPS: + +```bash +# Replace with your bot token, domain, and the same webhook token you set in APP_TELEGRAM_WEBHOOK_TOKEN +curl -X POST "https://api.telegram.org/bot/setWebhook" \ + -d "url=https://yourdomain.com/api/telegram/webhook/" +``` + +Verify webhook: + +```bash +curl "https://api.telegram.org/bot/getWebhookInfo" +``` + +## Step 8: Final Verification + +### 8.1 Check Services + +```bash +# Check Docker containers +docker ps + +# Should show: +# - lottery-mysql +# - lottery-backend + +# Check Nginx +sudo systemctl status nginx +``` + +### 8.2 Test Endpoints + +```bash +# Test backend health +curl http://localhost:8080/actuator/health + +# Test frontend (should return HTML) +curl https://yourdomain.com/ + +# Test API (should return JSON or error with auth) +curl https://yourdomain.com/api/health +``` + +### 8.3 Check Logs + +```bash +# Backend logs +cd /opt/app/backend +docker-compose -f docker-compose.prod.yml logs -f + +# Nginx logs +sudo tail -f /var/log/nginx/access.log +sudo tail -f /var/log/nginx/error.log +``` + +### 8.4 Browser Testing + +1. Open `https://yourdomain.com` in a browser +2. Test the Telegram Mini App +3. Verify API calls work (check browser console) +4. Test WebSocket connection (game updates) + +## Step 9: Maintenance Commands + +### 9.1 Restart Services + +```bash +# Restart backend +cd /opt/app/backend +docker compose -f docker-compose.prod.yml restart + +# Restart Nginx +sudo systemctl restart nginx +``` + +### 9.2 Update Application + +```bash +# Backend update +cd /opt/app/backend +git pull # or copy new files +docker compose -f docker-compose.prod.yml up -d --build + +# Frontend update +# Rebuild and copy dist/ folder +``` + +### 9.3 Backup Database + +```bash +# Create backup +docker exec lottery-mysql mysqldump -u root -p${DB_PASSWORD} lottery_db > backup_$(date +%Y%m%d).sql + +# Restore backup +docker exec -i lottery-mysql mysql -u root -p${DB_PASSWORD} lottery_db < backup_20240101.sql +``` + +### 9.4 View Logs + +```bash +# Backend logs +cd /opt/app/backend +docker-compose -f docker-compose.prod.yml logs -f backend + +# Database logs +docker-compose -f docker-compose.prod.yml logs -f db + +# Nginx logs +sudo tail -f /var/log/nginx/error.log +``` + +## Troubleshooting + +### Backend Not Starting + +```bash +# Check logs +docker compose -f docker-compose.prod.yml logs backend + +# Common issues: +# - Database not ready: wait for health check +# - Missing configuration: check secret file at /run/secrets/lottery-config.properties +# - Secret file not found: ensure file exists and is mounted correctly +# - Port conflict: ensure port 8080 is not exposed to host +``` + +### Frontend Not Loading + +```bash +# Check Nginx error log +sudo tail -f /var/log/nginx/error.log + +# Verify files exist +ls -la /opt/app/frontend/dist/ + +# Check Nginx config +sudo nginx -t +``` + +### Database Connection Issues + +```bash +# Check database container +docker ps | grep mysql + +# Check database logs +docker compose -f docker-compose.prod.yml logs db + +# Test connection +docker exec -it lottery-mysql mysql -u root -p +``` + +### SSL Certificate Issues + +```bash +# Check certificate +sudo certbot certificates + +# Renew certificate +sudo certbot renew + +# Check Nginx SSL config +sudo nginx -t +``` + +### WebSocket Not Working + +```bash +# Check backend logs for WebSocket errors +docker compose -f docker-compose.prod.yml logs backend | grep -i websocket + +# Verify Nginx WebSocket configuration +grep -A 10 "/ws" /opt/app/nginx/nginx.conf +``` + +## Security Checklist + +- [ ] Strong database passwords set +- [ ] Secret file has restricted permissions (`chmod 640`, owned by `root:docker`) +- [ ] Secret file contains all required configuration values +- [ ] SSL certificate installed and auto-renewal configured +- [ ] Firewall configured (UFW recommended) +- [ ] Backend port 8080 not exposed to host +- [ ] MySQL port 3306 not exposed to host +- [ ] Regular backups scheduled +- [ ] Logs monitored for suspicious activity + +## Firewall Setup (Optional but Recommended) + +```bash +# Install UFW +sudo apt install ufw -y + +# Allow SSH (IMPORTANT - do this first!) +sudo ufw allow 22/tcp + +# Allow HTTP and HTTPS +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp + +# Enable firewall +sudo ufw enable + +# Check status +sudo ufw status +``` + +## Directory Structure Summary + +``` +/opt/app/ +├── backend/ +│ ├── Dockerfile +│ ├── docker-compose.prod.yml +│ ├── lottery-config.properties.template +│ ├── pom.xml +│ └── src/ +├── frontend/ +│ └── dist/ (Vite production build) +├── nginx/ +│ └── nginx.conf +├── data/ +│ └── avatars/ (persistent uploads) +└── mysql/ + └── data/ (persistent DB storage) + +/run/secrets/ +└── lottery-config.properties (mounted secret configuration file) +``` + +## Support + +If you encounter issues: +1. Check logs first (backend, Nginx, Docker) +2. Verify secret file exists at `/run/secrets/lottery-config.properties` and has correct values +3. Verify secret file permissions (`chmod 640`, owned by `root:docker`) +4. Check backend logs for "Loading configuration from mounted secret file" message +5. Ensure all directories exist and have proper permissions +6. Verify network connectivity between containers +7. Check SSL certificate validity + +--- + +**Last Updated:** 2026-01-24 +**Version:** 1.0 + diff --git a/DOCKER_LOGGING_SETUP.md b/DOCKER_LOGGING_SETUP.md new file mode 100644 index 0000000..0dd5630 --- /dev/null +++ b/DOCKER_LOGGING_SETUP.md @@ -0,0 +1,202 @@ +# Docker Logging Setup - Automatic Configuration + +## Overview + +The Docker setup is **automatically configured** to use external `logback-spring.xml` for runtime log level changes. No manual configuration needed! + +## How It Works + +### 1. Dockerfile Configuration + +Both `Dockerfile` and `Dockerfile.inferno` automatically: +- Copy `logback-spring.xml` to `/app/config/logback-spring.xml` in the container +- Create `/app/logs` directory for log files +- Set default environment variables: + - `LOGGING_CONFIG=/app/config/logback-spring.xml` + - `LOG_DIR=/app/logs` +- Configure Java to use external config via `-Dlogging.config` and `-DLOG_DIR` + +### 2. Docker Compose Configuration + +Both `docker-compose.inferno.yml` and `docker-compose.prod.yml` automatically: +- **Mount external config**: `/opt/app/backend/config/logback-spring.xml` → `/app/config/logback-spring.xml` (read-write, editable on VPS) +- **Mount logs directory**: `/opt/app/logs` → `/app/logs` (persistent storage) +- **Set environment variables**: `LOGGING_CONFIG` and `LOG_DIR` + +## Initial Setup (One-Time) + +### Option 1: Use Setup Script (Recommended) + +```bash +cd /opt/app/backend +# Make script executable (if not already) +chmod +x scripts/setup-logging.sh +# Run the script +./scripts/setup-logging.sh +``` + +This script will: +1. Create `/opt/app/backend/config` directory +2. Create `/opt/app/logs` directory +3. Extract `logback-spring.xml` from JAR (if available) +4. Set proper permissions + +### Option 2: Manual Setup + +```bash +# Create directories +mkdir -p /opt/app/backend/config +mkdir -p /opt/app/logs + +# Extract logback-spring.xml from JAR +cd /opt/app/backend +unzip -p target/lottery-be-*.jar BOOT-INF/classes/logback-spring.xml > /opt/app/backend/config/logback-spring.xml + +# Or copy from source (if building from source on VPS) +cp src/main/resources/logback-spring.xml /opt/app/backend/config/logback-spring.xml + +# Set permissions +chmod 644 /opt/app/backend/config/logback-spring.xml +chmod 755 /opt/app/logs +``` + +## Usage + +### Start Application + +Just start Docker Compose as usual: + +```bash +cd /opt/app/backend +docker compose -f docker-compose.inferno.yml up -d +``` + +The external logging configuration is **automatically active** - no additional steps needed! + +### Change Log Level at Runtime + +1. **Edit the mounted config file**: + ```bash + nano /opt/app/backend/config/logback-spring.xml + ``` + +2. **Change log level** (example: enable DEBUG): + ```xml + + ``` + +3. **Save the file**. Logback will automatically reload within 30 seconds. + +4. **Verify**: + ```bash + # View logs from VPS + tail -f /opt/app/logs/lottery-be.log + + # Or from inside container + docker exec lottery-backend tail -f /app/logs/lottery-be.log + ``` + +### View Logs + +```bash +# Real-time monitoring +tail -f /opt/app/logs/lottery-be.log + +# Search for errors +grep -i "error" /opt/app/logs/lottery-be.log + +# View last 100 lines +tail -n 100 /opt/app/logs/lottery-be.log + +# From inside container +docker exec lottery-backend tail -f /app/logs/lottery-be.log +``` + +## File Locations + +### On VPS (Host) +- **Config file**: `/opt/app/backend/config/logback-spring.xml` (editable) +- **Log files**: `/opt/app/logs/lottery-be.log` and rolled files + +### Inside Container +- **Config file**: `/app/config/logback-spring.xml` (mounted from host) +- **Log files**: `/app/logs/lottery-be.log` (mounted to host) + +## Verification + +### Check Configuration is Active + +```bash +# Check container logs for logback initialization +docker logs lottery-backend | grep -i "logback\|logging" + +# Check mounted file exists +ls -la /opt/app/backend/config/logback-spring.xml + +# Check log directory +ls -la /opt/app/logs/ + +# Check environment variables in container +docker exec lottery-backend env | grep LOG +``` + +### Expected Output + +You should see: +- `LOGGING_CONFIG=/app/config/logback-spring.xml` +- `LOG_DIR=/app/logs` +- Log files appearing in `/opt/app/logs/` + +## Benefits + +✅ **No manual configuration needed** - Works automatically with Docker +✅ **Runtime log level changes** - Edit file, changes take effect in 30 seconds +✅ **No container restart required** - Changes apply without restarting +✅ **Persistent logs** - Logs survive container restarts +✅ **Editable config** - Edit logback-spring.xml directly on VPS + +## Troubleshooting + +### Config file not found + +```bash +# Check if file exists +ls -la /opt/app/backend/config/logback-spring.xml + +# If missing, extract from JAR or copy from source +./scripts/setup-logging.sh +``` + +### Logs not appearing + +```bash +# Check log directory permissions +ls -ld /opt/app/logs + +# Check container can write +docker exec lottery-backend ls -la /app/logs + +# Check disk space +df -h /opt/app/logs +``` + +### Log level changes not working + +1. Verify `scan="true" scanPeriod="30 seconds"` in logback-spring.xml +2. Check for XML syntax errors +3. Wait 30 seconds after saving +4. Check container logs for Logback errors: + ```bash + docker logs lottery-backend | grep -i "logback\|error" + ``` + +## Summary + +**You don't need to do anything manually!** The Docker setup automatically: +- Uses external logback-spring.xml +- Mounts it as a volume (editable on VPS) +- Sets all required environment variables +- Configures log directory + +Just run `docker compose up` and you're ready to go! 🚀 + diff --git a/Dockerfile b/Dockerfile index a2bc257..61741f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,15 +18,28 @@ WORKDIR /app # Copy fat jar from build stage COPY --from=build /app/target/*.jar app.jar -# Copy startup script +# Copy startup script (optional - only used if secret file doesn't exist) COPY scripts/create-secret-file.sh /app/create-secret-file.sh RUN chmod +x /app/create-secret-file.sh +# Copy logback-spring.xml to config directory (can be overridden by volume mount) +COPY src/main/resources/logback-spring.xml /app/config/logback-spring.xml + +# Create log directory +RUN mkdir -p /app/logs && chmod 755 /app/logs + # Expose port (for local/docker-compose/documentation) EXPOSE 8080 +# Default environment variables (can be overridden in docker-compose) ENV JAVA_OPTS="" +ENV LOGGING_CONFIG="/app/config/logback-spring.xml" +ENV LOG_DIR="/app/logs" -# Create secret file from env vars (for testing ConfigLoader) then start app -ENTRYPOINT ["sh", "-c", "/app/create-secret-file.sh && java $JAVA_OPTS -jar app.jar"] +# Start app +# If /run/secrets/lottery-config.properties exists (mounted), ConfigLoader will use it +# Otherwise, create-secret-file.sh will create it from env vars (for development/testing) +# Uses external logback-spring.xml for runtime log level changes +# Ensure logback-spring.xml exists and is a file (not a directory) +ENTRYPOINT ["sh", "-c", "if [ ! -f /run/secrets/lottery-config.properties ]; then /app/create-secret-file.sh; fi && if [ ! -f \"${LOGGING_CONFIG}\" ] || [ -d \"${LOGGING_CONFIG}\" ]; then echo 'Warning: ${LOGGING_CONFIG} not found or is a directory, using default from JAR'; LOGGING_CONFIG=''; fi && java $JAVA_OPTS ${LOGGING_CONFIG:+-Dlogging.config=${LOGGING_CONFIG}} -DLOG_DIR=${LOG_DIR} -jar app.jar"] diff --git a/Dockerfile.inferno b/Dockerfile.inferno index 92d9e19..7bfc0a7 100644 --- a/Dockerfile.inferno +++ b/Dockerfile.inferno @@ -18,10 +18,22 @@ WORKDIR /app # Copy fat jar from build stage COPY --from=build /app/target/*.jar app.jar +# Copy logback-spring.xml to config directory (can be overridden by volume mount) +COPY --from=build /app/src/main/resources/logback-spring.xml /app/config/logback-spring.xml + +# Create log directory +RUN mkdir -p /app/logs && chmod 755 /app/logs + # Expose port (for internal communication with nginx) EXPOSE 8080 +# Default environment variables (can be overridden in docker-compose) ENV JAVA_OPTS="" +ENV LOGGING_CONFIG="/app/config/logback-spring.xml" +ENV LOG_DIR="/app/logs" + +# Start app with external logback config +# Ensure logback-spring.xml exists and is a file (not a directory) +ENTRYPOINT ["sh", "-c", "if [ ! -f \"${LOGGING_CONFIG}\" ] || [ -d \"${LOGGING_CONFIG}\" ]; then echo 'Warning: ${LOGGING_CONFIG} not found or is a directory, using default from JAR'; LOGGING_CONFIG=''; fi && java $JAVA_OPTS ${LOGGING_CONFIG:+-Dlogging.config=${LOGGING_CONFIG}} -DLOG_DIR=${LOG_DIR} -jar app.jar"] -ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] diff --git a/EXTERNAL_API.md b/EXTERNAL_API.md new file mode 100644 index 0000000..b788fa1 --- /dev/null +++ b/EXTERNAL_API.md @@ -0,0 +1,88 @@ +# Внешние API (токен в пути, без сессионной авторизации) + +Описание трёх эндпоинтов для внешних систем. Токены задаются через переменные окружения на VPS. + +--- + +## 1. GET /api/remotebet/{token} + +Регистрация пользователя в текущий раунд комнаты с указанной ставкой (удалённая ставка). + +**Параметры пути** + +| Параметр | Тип | Описание | +|----------|--------|----------| +| token | string | Секретный токен (должен совпадать с `APP_REMOTE_BET_TOKEN`) | + +**Query-параметры** + +| Параметр | Тип | Обязательный | Описание | +|----------|--------|--------------|----------| +| user_id | integer| да | Внутренний ID пользователя (db_users_a.id) | +| room | integer| да | Номер комнаты: 1, 2 или 3 | +| amount | integer| да | Ставка в билетах (например, 5 = 5 билетов) | + +**Ответ 200** + +| Поле | Тип | Описание | +|--------------|--------|----------| +| success | boolean| Успешность операции | +| roundId | integer| ID раунда (или null) | +| room | integer| Номер комнаты | +| betTickets | integer| Размер ставки в билетах | +| error | string | Сообщение об ошибке (при success = false) | + +**Коды ответа:** 200, 400, 403, 503 + +--- + +## 2. GET /api/check_user/{token}/{telegramId} + +Получение информации о пользователе по Telegram ID. + +**Параметры пути** + +| Параметр | Тип | Описание | +|------------|--------|----------| +| token | string | Секретный токен (должен совпадать с `APP_CHECK_USER_TOKEN`) | +| telegramId | long | Telegram ID пользователя | + +**Тело запроса:** отсутствует + +**Ответ 200** + +При успешном вызове всегда возвращается 200. По полю `found` можно определить, найден ли пользователь. + +| Поле | Тип | Описание | +|-------------|--------|----------| +| found | boolean| true — пользователь найден, остальные поля заполнены; false — пользователь не найден, остальные поля null | +| dateReg | integer| Дата регистрации (при found=true) | +| tickets | number | Баланс в билетах (balance_a / 1_000_000) (при found=true) | +| depositTotal| integer| Сумма stars_amount по завершённым платежам (Stars) (при found=true) | +| refererId | integer| referer_id_1 из db_users_d (0 если нет) (при found=true) | +| roundsPlayed| integer| Количество сыгранных раундов (при found=true) | + +**Коды ответа:** 200, 403, 500 + +--- + +## 3. POST /api/deposit_webhook/{token} + +Уведомление об успешном пополнении пользователя (криптоплатёж). Создаётся платёж в статусе COMPLETED, начисляются билеты, обновляются баланс и статистика депозитов, создаётся транзакция типа DEPOSIT. + +**Параметры пути** + +| Параметр | Тип | Описание | +|----------|--------|----------| +| token | string | Секретный токен (должен совпадать с `APP_DEPOSIT_WEBHOOK_TOKEN`) | + +**Тело запроса (application/json)** + +| Поле | Тип | Обязательный | Описание | +|-----------|--------|--------------|----------| +| user_id | integer| да | Внутренний ID пользователя (db_users_a.id) | +| usd_amount| number | да | Сумма в USD в виде числа (например, 1.45 или 50) | + +**Тело ответа:** пустое при успехе + +**Коды ответа:** 200, 400, 403, 500 diff --git a/LOGGING_GUIDE.md b/LOGGING_GUIDE.md new file mode 100644 index 0000000..18311e2 --- /dev/null +++ b/LOGGING_GUIDE.md @@ -0,0 +1,341 @@ +# Logging Configuration Guide + +## Overview + +The application uses Logback for logging with the following features: +- **Runtime log level changes** (scan every 30 seconds) +- **Asynchronous file logging** (non-blocking I/O for high concurrency) +- **Automatic log rotation** (50MB per file, 14 days retention, 10GB total cap) +- **External configuration file** (editable on VPS without rebuilding) + +## Log File Location + +### Default Location +Logs are stored in: `./logs/` directory (relative to where the JAR is executed) + +### Custom Location +Set the `LOG_DIR` environment variable or system property: +```bash +export LOG_DIR=/var/log/lottery-be +java -jar lottery-be.jar +``` + +Or: +```bash +java -DLOG_DIR=/var/log/lottery-be -jar lottery-be.jar +``` + +## Log File Naming + +- **Current log**: `logs/lottery-be.log` +- **Rolled logs**: `logs/lottery-be-2024-01-15.0.log`, `logs/lottery-be-2024-01-15.1.log`, etc. +- **Max file size**: 50MB per file +- **Retention**: 14 days +- **Total size cap**: 10GB + +## Using External logback-spring.xml on VPS + +By default, `logback-spring.xml` is packaged inside the JAR. To use an external file on your VPS: + +### Step 1: Copy logback-spring.xml to VPS + +```bash +# Copy from JAR (if needed) or from your source code +# Place it in a location like: /opt/lottery-be/config/logback-spring.xml +# Or next to your JAR: /opt/lottery-be/logback-spring.xml +``` + +### Step 2: Start application with external config + +```bash +# Option 1: System property +java -Dlogging.config=/opt/lottery-be/logback-spring.xml -jar lottery-be.jar + +# Option 2: Environment variable +export LOGGING_CONFIG=/opt/lottery-be/logback-spring.xml +java -jar lottery-be.jar + +# Option 3: In systemd service file +[Service] +Environment="LOGGING_CONFIG=/opt/lottery-be/logback-spring.xml" +ExecStart=/usr/bin/java -jar /opt/lottery-be/lottery-be.jar +``` + +### Step 3: Edit logback-spring.xml on VPS + +```bash +# Edit the file +nano /opt/lottery-be/logback-spring.xml + +# Change log level (example: change com.lottery from INFO to DEBUG) +# Find: +# Change to: + +# Save and exit +# Logback will automatically reload within 30 seconds (scanPeriod="30 seconds") +``` + +## Linux Commands for Log Management + +### Find Log Files + +```bash +# Find all log files +find /opt/lottery-be -name "*.log" -type f + +# Find logs in default location +ls -lh ./logs/ + +# Find logs with custom LOG_DIR +ls -lh /var/log/lottery-be/ +``` + +### View Log Files + +```bash +# View current log file (real-time) +tail -f logs/lottery-be.log + +# View last 100 lines +tail -n 100 logs/lottery-be.log + +# View with line numbers +cat -n logs/lottery-be.log | less + +# View specific date's log +cat logs/lottery-be-2024-01-15.0.log +``` + +### Search Logs + +```bash +# Search for errors +grep -i "error" logs/lottery-be.log + +# Search for specific user ID +grep "userId=123" logs/lottery-be.log + +# Search across all log files +grep -r "ERROR" logs/ + +# Search with context (5 lines before/after) +grep -C 5 "ERROR" logs/lottery-be.log + +# Search and highlight +grep --color=always "ERROR\|WARN" logs/lottery-be.log | less -R +``` + +### Monitor Logs in Real-Time + +```bash +# Follow current log +tail -f logs/lottery-be.log + +# Follow and filter for errors only +tail -f logs/lottery-be.log | grep -i error + +# Follow multiple log files +tail -f logs/lottery-be*.log + +# Follow with timestamps +tail -f logs/lottery-be.log | while read line; do echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line"; done +``` + +### Check Log File Sizes + +```bash +# Check size of all log files +du -sh logs/* + +# Check total size of logs directory +du -sh logs/ + +# List files sorted by size +ls -lhS logs/ + +# Check disk space +df -h +``` + +### Clean Old Logs + +```bash +# Logback automatically deletes logs older than 14 days +# But you can manually clean if needed: + +# Remove logs older than 7 days +find logs/ -name "*.log" -mtime +7 -delete + +# Remove logs older than 14 days (matching logback retention) +find logs/ -name "*.log" -mtime +14 -delete +``` + +## Changing Log Level at Runtime + +### Method 1: Edit logback-spring.xml (Recommended) + +1. **Edit the external logback-spring.xml file**: + ```bash + nano /opt/lottery-be/logback-spring.xml + ``` + +2. **Change the logger level** (example: enable DEBUG for entire app): + ```xml + + + + + + ``` + +3. **Save the file**. Logback will automatically reload within 30 seconds. + +4. **Verify the change**: + ```bash + tail -f logs/lottery-be.log + # You should see DEBUG logs appearing after ~30 seconds + ``` + +### Method 2: Change Specific Logger + +To change only a specific service (e.g., GameRoomService): + +```xml + + + + + +``` + +### Method 3: Change Root Level + +To change the root level for all loggers: + +```xml + + + + + +``` + +**Note**: This will generate A LOT of logs. Use with caution in production. + +## Log Levels Explained + +- **ERROR**: Critical errors that need immediate attention +- **WARN**: Warnings that might indicate problems +- **INFO**: Important application events (round completion, payments, etc.) +- **DEBUG**: Detailed debugging information (very verbose, use only for troubleshooting) + +## Default Configuration + +- **Root level**: INFO +- **Application (com.lottery)**: INFO +- **High-traffic services**: WARN (GameRoomService, GameWebSocketController) +- **Infrastructure packages**: WARN (Spring, Hibernate, WebSocket, etc.) + +## Performance Considerations + +- **Asynchronous logging**: Logs are written asynchronously to prevent blocking main threads +- **Queue size**: 256 log entries (good for 1000+ concurrent users) +- **Never block**: If queue is full, lower-level logs (DEBUG/INFO) may be dropped, but WARN/ERROR are always kept +- **File I/O**: All file writes are non-blocking + +## Troubleshooting + +### Logs not appearing + +1. Check log file location: + ```bash + ls -la logs/ + ``` + +2. Check file permissions: + ```bash + ls -l logs/lottery-be.log + # Ensure the application user has write permissions + ``` + +3. Check disk space: + ```bash + df -h + ``` + +### Log level changes not taking effect + +1. Verify scan is enabled in logback-spring.xml: + ```xml + + ``` + +2. Check for syntax errors in logback-spring.xml: + ```bash + # Logback will log errors to console if config is invalid + ``` + +3. Restart application if needed (shouldn't be necessary with scan enabled) + +### Too many logs / Out of memory + +1. Increase log level to WARN: + ```xml + + ``` + +2. Check log file sizes: + ```bash + du -sh logs/* + ``` + +3. Clean old logs manually if needed + +## Example: Enabling DEBUG for Troubleshooting + +1. **Edit logback-spring.xml**: + ```bash + nano /opt/lottery-be/logback-spring.xml + ``` + +2. **Change specific logger to DEBUG**: + ```xml + + ``` + +3. **Save and wait 30 seconds** + +4. **Monitor logs**: + ```bash + tail -f logs/lottery-be.log | grep "GameRoomService" + ``` + +5. **After troubleshooting, change back to WARN**: + ```xml + + ``` + +## Systemd Service Example + +If using systemd, here's an example service file: + +```ini +[Unit] +Description=Lottery Backend Application +After=network.target + +[Service] +Type=simple +User=lottery +WorkingDirectory=/opt/lottery-be +Environment="LOGGING_CONFIG=/opt/lottery-be/logback-spring.xml" +Environment="LOG_DIR=/var/log/lottery-be" +ExecStart=/usr/bin/java -jar /opt/lottery-be/lottery-be.jar +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + + diff --git a/PHPMYADMIN_QUICK_START.md b/PHPMYADMIN_QUICK_START.md new file mode 100644 index 0000000..1081e74 --- /dev/null +++ b/PHPMYADMIN_QUICK_START.md @@ -0,0 +1,94 @@ +# phpMyAdmin Quick Start Guide + +## Quick Setup (Copy & Paste) + +```bash +# 1. Navigate to project directory +cd /opt/app/backend/lottery-be + +# 2. Load database password +source scripts/load-db-password.sh + +# 3. Start phpMyAdmin +docker-compose -f docker-compose.prod.yml up -d phpmyadmin + +# 4. Verify it's running +docker ps | grep phpmyadmin + +# 5. Open firewall port +sudo ufw allow 8081/tcp +sudo ufw reload + +# 6. Get your VPS IP (if you don't know it) +hostname -I | awk '{print $1}' +``` + +## Access phpMyAdmin + +**URL**: `http://YOUR_VPS_IP:8081` + +**Login Credentials**: +- **Server**: `db` (or leave default) +- **Username**: `root` +- **Password**: Get it with: `grep SPRING_DATASOURCE_PASSWORD /run/secrets/lottery-config.properties` + +## Security: Restrict Access to Your IP Only + +```bash +# Get your current IP +curl ifconfig.me + +# Remove open access +sudo ufw delete allow 8081/tcp + +# Allow only your IP (replace YOUR_IP with your actual IP) +sudo ufw allow from YOUR_IP to any port 8081 + +# Reload firewall +sudo ufw reload +``` + +## Verify Everything Works + +```bash +# Check container is running +docker ps | grep phpmyadmin + +# Check logs +docker logs lottery-phpmyadmin + +# Test connection from browser +# Open: http://YOUR_VPS_IP:8081 +``` + +## Common Issues + +**Container won't start?** +```bash +# Make sure password is loaded +source scripts/load-db-password.sh +echo $DB_ROOT_PASSWORD + +# Restart +docker-compose -f docker-compose.prod.yml restart phpmyadmin +``` + +**Can't access from browser?** +```bash +# Check firewall +sudo ufw status | grep 8081 + +# Check if port is listening +sudo netstat -tlnp | grep 8081 +``` + +**Wrong password?** +```bash +# Get the correct password +grep SPRING_DATASOURCE_PASSWORD /run/secrets/lottery-config.properties +``` + +## Full Documentation + +See `PHPMYADMIN_SETUP.md` for detailed instructions and troubleshooting. + diff --git a/PHPMYADMIN_SETUP.md b/PHPMYADMIN_SETUP.md new file mode 100644 index 0000000..85cee6c --- /dev/null +++ b/PHPMYADMIN_SETUP.md @@ -0,0 +1,355 @@ +# phpMyAdmin Setup Guide + +This guide explains how to set up phpMyAdmin for managing your MySQL database on your VPS. + +## Overview + +- **phpMyAdmin Port**: 8081 (mapped to container port 80) +- **MySQL Service Name**: `db` (internal Docker network) +- **Database Name**: `lottery_db` +- **Network**: `lottery-network` (shared with MySQL and backend) + +## Security Features + +✅ **MySQL port 3306 is NOT exposed** - Only accessible within Docker network +✅ **phpMyAdmin accessible on port 8081** - Can be restricted via firewall +✅ **Upload limit set to 64M** - Prevents large file uploads +✅ **Uses same root password** - From your existing secret file + +## Prerequisites + +- Docker and Docker Compose installed on VPS +- Existing MySQL database running in Docker +- `DB_ROOT_PASSWORD` environment variable set (from secret file) + +## Step-by-Step Deployment + +### Step 1: Verify Current Setup + +First, check that your MySQL container is running and the database password is accessible: + +```bash +cd /opt/app/backend/lottery-be + +# Check if MySQL container is running +docker ps | grep lottery-mysql + +# Load database password (if not already set) +source scripts/load-db-password.sh + +# Verify password is set +echo $DB_ROOT_PASSWORD +``` + +### Step 2: Update Docker Compose + +The `docker-compose.prod.yml` file has already been updated with the phpMyAdmin service. Verify the changes: + +```bash +# View the phpMyAdmin service configuration +grep -A 20 "phpmyadmin:" docker-compose.prod.yml +``` + +You should see: +- Service name: `phpmyadmin` +- Port mapping: `8081:80` +- PMA_HOST: `db` +- UPLOAD_LIMIT: `64M` + +### Step 3: Start phpMyAdmin Service + +```bash +cd /opt/app/backend/lottery-be + +# Make sure DB_ROOT_PASSWORD is set +source scripts/load-db-password.sh + +# Start only the phpMyAdmin service (MySQL should already be running) +docker-compose -f docker-compose.prod.yml up -d phpmyadmin +``` + +Or if you want to restart all services: + +```bash +# Stop all services +docker-compose -f docker-compose.prod.yml down + +# Start all services (including phpMyAdmin) +source scripts/load-db-password.sh +docker-compose -f docker-compose.prod.yml up -d +``` + +### Step 4: Verify phpMyAdmin is Running + +```bash +# Check container status +docker ps | grep phpmyadmin + +# Check logs for any errors +docker logs lottery-phpmyadmin + +# Test if port 8081 is listening +netstat -tlnp | grep 8081 +# or +ss -tlnp | grep 8081 +``` + +### Step 5: Configure Firewall (UFW) + +On Inferno Solutions VPS (Ubuntu), you need to allow port 8081: + +```bash +# Check current UFW status +sudo ufw status + +# Allow port 8081 (replace with your VPS IP if you want to restrict access) +sudo ufw allow 8081/tcp + +# If you want to restrict to specific IP only (recommended for production): +# sudo ufw allow from YOUR_IP_ADDRESS to any port 8081 + +# Reload UFW +sudo ufw reload + +# Verify the rule was added +sudo ufw status numbered +``` + +**Security Recommendation**: If you have a static IP, restrict access to that IP only: + +```bash +# Replace YOUR_IP_ADDRESS with your actual IP +sudo ufw allow from YOUR_IP_ADDRESS to any port 8081 +``` + +### Step 6: Access phpMyAdmin + +Open your web browser and navigate to: + +``` +http://YOUR_VPS_IP:8081 +``` + +**Example**: If your VPS IP is `37.1.206.220`, use: +``` +http://37.1.206.220:8081 +``` + +### Step 7: Login to phpMyAdmin + +Use these credentials: + +- **Server**: `db` (or leave as default - phpMyAdmin will auto-detect) +- **Username**: `root` +- **Password**: The value from `SPRING_DATASOURCE_PASSWORD` in your secret file + +To get the password: + +```bash +# On your VPS +grep SPRING_DATASOURCE_PASSWORD /run/secrets/lottery-config.properties +``` + +## Verification Checklist + +After setup, verify: + +- [ ] phpMyAdmin container is running: `docker ps | grep phpmyadmin` +- [ ] Port 8081 is accessible: `curl http://localhost:8081` (should return HTML) +- [ ] Firewall allows port 8081: `sudo ufw status | grep 8081` +- [ ] Can login to phpMyAdmin with root credentials +- [ ] Can see `lottery_db` database in phpMyAdmin +- [ ] MySQL port 3306 is NOT exposed: `netstat -tlnp | grep 3306` (should show nothing or only 127.0.0.1) + +## Security Best Practices + +### 1. Restrict Access by IP (Recommended) + +Only allow your IP address to access phpMyAdmin: + +```bash +# Find your current IP +curl ifconfig.me + +# Allow only your IP +sudo ufw delete allow 8081/tcp +sudo ufw allow from YOUR_IP_ADDRESS to any port 8081 +``` + +### 2. Use HTTPS (Optional but Recommended) + +If you have a domain and SSL certificate, you can set up Nginx as a reverse proxy: + +```nginx +# /etc/nginx/sites-available/phpmyadmin +server { + listen 443 ssl; + server_name phpmyadmin.yourdomain.com; + + ssl_certificate /path/to/cert.pem; + ssl_certificate_key /path/to/key.pem; + + location / { + proxy_pass http://127.0.0.1:8081; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +### 3. Change Default phpMyAdmin Behavior + +You can add additional security settings to the phpMyAdmin service in `docker-compose.prod.yml`: + +```yaml +environment: + # ... existing settings ... + # Disable certain features for security + PMA_CONTROLUSER: '' + PMA_CONTROLPASS: '' + # Enable HTTPS only (if using reverse proxy) + # PMA_ABSOLUTE_URI: https://phpmyadmin.yourdomain.com +``` + +### 4. Regular Updates + +Keep phpMyAdmin updated: + +```bash +# Pull latest image +docker-compose -f docker-compose.prod.yml pull phpmyadmin + +# Restart service +docker-compose -f docker-compose.prod.yml up -d phpmyadmin +``` + +## Troubleshooting + +### phpMyAdmin Container Won't Start + +```bash +# Check logs +docker logs lottery-phpmyadmin + +# Common issues: +# 1. DB_ROOT_PASSWORD not set +source scripts/load-db-password.sh +docker-compose -f docker-compose.prod.yml up -d phpmyadmin + +# 2. MySQL container not running +docker-compose -f docker-compose.prod.yml up -d db +``` + +### Cannot Connect to Database + +```bash +# Verify MySQL is accessible from phpMyAdmin container +docker exec lottery-phpmyadmin ping -c 3 db + +# Check if MySQL is healthy +docker ps | grep lottery-mysql +docker logs lottery-mysql | tail -20 +``` + +### Port 8081 Not Accessible + +```bash +# Check if port is listening +sudo netstat -tlnp | grep 8081 + +# Check firewall +sudo ufw status + +# Check if container is running +docker ps | grep phpmyadmin + +# Restart phpMyAdmin +docker-compose -f docker-compose.prod.yml restart phpmyadmin +``` + +### "Access Denied" When Logging In + +1. Verify password is correct: + ```bash + grep SPRING_DATASOURCE_PASSWORD /run/secrets/lottery-config.properties + ``` + +2. Verify `DB_ROOT_PASSWORD` matches: + ```bash + source scripts/load-db-password.sh + echo $DB_ROOT_PASSWORD + ``` + +3. Test MySQL connection directly: + ```bash + docker exec -it lottery-mysql mysql -u root -p + # Enter the password when prompted + ``` + +## Spring Boot Configuration Verification + +Your Spring Boot application should be using the Docker service name for the database connection. Verify: + +1. **Secret file** (`/run/secrets/lottery-config.properties`) should contain: + ``` + SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/lottery_db + ``` + +2. **NOT using localhost**: + - ❌ Wrong: `jdbc:mysql://localhost:3306/lottery_db` + - ✅ Correct: `jdbc:mysql://db:3306/lottery_db` + +To verify: + +```bash +grep SPRING_DATASOURCE_URL /run/secrets/lottery-config.properties +``` + +## Maintenance Commands + +```bash +# View phpMyAdmin logs +docker logs lottery-phpmyadmin + +# Restart phpMyAdmin +docker-compose -f docker-compose.prod.yml restart phpmyadmin + +# Stop phpMyAdmin +docker-compose -f docker-compose.prod.yml stop phpmyadmin + +# Start phpMyAdmin +docker-compose -f docker-compose.prod.yml start phpmyadmin + +# Remove phpMyAdmin (keeps data) +docker-compose -f docker-compose.prod.yml rm -f phpmyadmin + +# Update phpMyAdmin to latest version +docker-compose -f docker-compose.prod.yml pull phpmyadmin +docker-compose -f docker-compose.prod.yml up -d phpmyadmin +``` + +## Quick Reference + +| Item | Value | +|------|-------| +| **URL** | `http://YOUR_VPS_IP:8081` | +| **Username** | `root` | +| **Password** | From `SPRING_DATASOURCE_PASSWORD` in secret file | +| **Server** | `db` (auto-detected) | +| **Database** | `lottery_db` | +| **Container** | `lottery-phpmyadmin` | +| **Port** | `8081` (host) → `80` (container) | +| **Network** | `lottery-network` | + +## Next Steps + +After phpMyAdmin is set up: + +1. ✅ Test login and database access +2. ✅ Verify you can see all tables in `lottery_db` +3. ✅ Set up IP restrictions for better security +4. ✅ Consider setting up HTTPS via Nginx reverse proxy +5. ✅ Document your access credentials securely + diff --git a/QUICK_REFERENCE.md b/QUICK_REFERENCE.md new file mode 100644 index 0000000..757bc15 --- /dev/null +++ b/QUICK_REFERENCE.md @@ -0,0 +1,217 @@ +# Quick Reference - VPS Deployment + +## Common Commands + +### Docker Compose (Backend) + +```bash +cd /opt/app/backend + +# Start services +docker compose -f docker-compose.prod.yml up -d + +# Stop services +docker compose -f docker-compose.prod.yml down + +# Restart services +docker compose -f docker-compose.prod.yml restart + +# View logs +docker compose -f docker-compose.prod.yml logs -f + +# Rebuild and restart +docker compose -f docker-compose.prod.yml up -d --build + +# Check status +docker compose -f docker-compose.prod.yml ps +``` + +### Nginx + +```bash +# Test configuration +sudo nginx -t + +# Reload configuration +sudo systemctl reload nginx + +# Restart Nginx +sudo systemctl restart nginx + +# Check status +sudo systemctl status nginx + +# View logs +sudo tail -f /var/log/nginx/error.log +sudo tail -f /var/log/nginx/access.log +``` + +### SSL Certificate + +```bash +# Renew certificate +sudo certbot renew + +# Test renewal +sudo certbot renew --dry-run + +# Check certificates +sudo certbot certificates +``` + +### Database + +```bash +# Load database password from secret file (if not already loaded) +cd /opt/app/backend +source scripts/load-db-password.sh + +# Backup +docker exec lottery-mysql mysqldump -u root -p${DB_PASSWORD} lottery_db > backup_$(date +%Y%m%d).sql + +# Restore +docker exec -i lottery-mysql mysql -u root -p${DB_PASSWORD} lottery_db < backup.sql + +# Access MySQL shell +docker exec -it lottery-mysql mysql -u root -p +``` + +### Health Checks + +```bash +# Backend health +curl http://localhost:8080/actuator/health + +# Frontend +curl https://yourdomain.com/ + +# API endpoint +curl https://yourdomain.com/api/health +``` + +### Logs + +```bash +# Backend logs +cd /opt/app/backend +docker compose -f docker-compose.prod.yml logs -f backend + +# Database logs +docker compose -f docker-compose.prod.yml logs -f db + +# All logs +docker compose -f docker-compose.prod.yml logs -f + +# Nginx error log +sudo tail -f /var/log/nginx/error.log +``` + +### File Permissions + +```bash +# Fix avatar directory permissions +sudo chown -R $USER:$USER /opt/app/data/avatars +sudo chmod -R 755 /opt/app/data/avatars + +# Secure secret file +sudo chmod 640 /run/secrets/lottery-config.properties +sudo chown root:docker /run/secrets/lottery-config.properties +``` + +### Update Application + +```bash +# Backend update +cd /opt/app/backend +git pull # or copy new files +docker compose -f docker-compose.prod.yml up -d --build + +# Frontend update +# 1. Build locally: npm run build +# 2. Copy dist/ to /opt/app/frontend/dist/ +scp -r dist/* user@vps:/opt/app/frontend/dist/ +``` + +## Troubleshooting + +### Backend won't start +```bash +# Check logs +docker compose -f docker-compose.prod.yml logs backend + +# Check secret file exists and is readable +sudo ls -la /run/secrets/lottery-config.properties + +# Verify secret file is loaded (check logs for "Loading configuration from mounted secret file") +docker compose -f docker-compose.prod.yml logs backend | grep "Loading configuration" + +# Verify database is ready +docker compose -f docker-compose.prod.yml ps db +``` + +### Frontend not loading +```bash +# Check Nginx config +sudo nginx -t + +# Verify files exist +ls -la /opt/app/frontend/dist/ + +# Check Nginx error log +sudo tail -f /var/log/nginx/error.log +``` + +### WebSocket issues +```bash +# Check backend logs +docker compose -f docker-compose.prod.yml logs backend | grep -i websocket + +# Verify Nginx WebSocket config +grep -A 10 "/ws" /opt/app/nginx/nginx.conf +``` + +### Database connection failed +```bash +# Check database container +docker ps | grep mysql + +# Check database logs +docker compose -f docker-compose.prod.yml logs db + +# Test connection +docker exec -it lottery-mysql mysql -u root -p +``` + +## File Locations + +``` +Backend source: /opt/app/backend/ +Frontend build: /opt/app/frontend/dist/ +Nginx config: /opt/app/nginx/nginx.conf +Avatar storage: /opt/app/data/avatars/ +Database data: /opt/app/mysql/data/ (via Docker volume) +Secret file: /run/secrets/lottery-config.properties +``` + +## Configuration Variables + +Required in `/run/secrets/lottery-config.properties`: + +- `SPRING_DATASOURCE_URL` +- `SPRING_DATASOURCE_USERNAME` +- `SPRING_DATASOURCE_PASSWORD` +- `TELEGRAM_BOT_TOKEN` +- `TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN` +- `TELEGRAM_FOLLOW_TASK_CHANNEL_ID` +- `FRONTEND_URL` + +Optional: +- `APP_AVATAR_STORAGE_PATH` +- `APP_AVATAR_PUBLIC_BASE_URL` +- `APP_SESSION_MAX_ACTIVE_PER_USER` +- `APP_SESSION_CLEANUP_BATCH_SIZE` +- `APP_SESSION_CLEANUP_MAX_BATCHES` +- `GEOIP_DB_PATH` + +**Note:** The MySQL container also needs `DB_PASSWORD` and `DB_ROOT_PASSWORD` as environment variables (should match `SPRING_DATASOURCE_PASSWORD`). + diff --git a/README.md b/README.md index 53b3f2d..b351885 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Honey Backend +# Lottery Backend -Spring Boot backend application for Honey project. +Spring Boot backend application for Lottery project. ## Technology Stack @@ -30,7 +30,7 @@ Spring Boot backend application for Honey project. 3. **Create `.env` file** (for local development): ```env - DB_NAME=honey_db + DB_NAME=lottery_db DB_USERNAME=root DB_PASSWORD=password DB_ROOT_PASSWORD=password @@ -75,7 +75,7 @@ Railway is the primary deployment platform for staging. It provides built-in log 1. In your Railway project, click **"+ New"** → **"GitHub Repo"** (or **"Empty Service"**) 2. If using GitHub: - Connect your GitHub account - - Select the `honey-be` repository + - Select the `lottery-be` repository - Railway will automatically detect it's a Java/Maven project 3. If using Empty Service: - Click **"Empty Service"** @@ -122,12 +122,12 @@ PORT=8080 1. In your backend service, go to **"Settings"** → **"Networking"** 2. Click **"Generate Domain"** to get a public URL 3. Or use the default Railway domain -4. Copy the URL (e.g., `https://honey-be-production.up.railway.app`) +4. Copy the URL (e.g., `https://lottery-be-production.up.railway.app`) #### Step 9: Create Frontend Service (Optional - if deploying frontend to Railway) 1. In your Railway project, click **"+ New"** → **"GitHub Repo"** -2. Select your `honey-fe` repository +2. Select your `lottery-fe` repository 3. Railway will detect it's a Node.js project 4. Add environment variable: ```env @@ -140,7 +140,7 @@ PORT=8080 If you need persistent storage: 1. In your Railway project, click **"+ New"** → **"Volume"** -2. Name it (e.g., `honey-data`) +2. Name it (e.g., `lottery-data`) 3. Mount it to your service if needed ### Inferno Deployment (Production Environment) @@ -184,16 +184,16 @@ Inferno Solution provides the production environment. It requires manual server 5. **Create project directory**: ```bash - mkdir -p /opt/honey - cd /opt/honey + mkdir -p /opt/lottery + cd /opt/lottery ``` #### Step 2: Clone Repository ```bash -cd /opt/honey -git clone https://github.com/your-username/honey-be.git -cd honey-be +cd /opt/lottery +git clone https://github.com/your-username/lottery-be.git +cd lottery-be ``` #### Step 3: Create Secret Configuration File @@ -206,14 +206,14 @@ sudo mkdir -p /run/secrets sudo chmod 700 /run/secrets # Create secret file -sudo nano /run/secrets/honey-config.properties +sudo nano /run/secrets/lottery-config.properties ``` Add the following content (replace with your actual values): ```properties -SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/honey_db -SPRING_DATASOURCE_USERNAME=honey_user +SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/lottery_db +SPRING_DATASOURCE_USERNAME=lottery_user SPRING_DATASOURCE_PASSWORD=your_secure_mysql_password TELEGRAM_BOT_TOKEN=your_telegram_bot_token FRONTEND_URL=https://your-frontend-domain.com @@ -233,7 +233,7 @@ The `docker-compose.inferno.yml` file is already configured. Make sure it's pres #### Step 5: Build and Start Services ```bash -cd /opt/honey/honey-be +cd /opt/lottery/lottery-be # Build and start all services docker-compose -f docker-compose.inferno.yml up -d --build @@ -249,7 +249,7 @@ This will: 1. **Edit nginx configuration**: ```bash - nano nginx/conf.d/honey.conf + nano nginx/conf.d/lottery.conf ``` 2. **Update server_name** (if using HTTPS): @@ -278,7 +278,7 @@ This will: sudo certbot --nginx -d your-domain.com ``` -3. **Update nginx config** to use HTTPS (uncomment HTTPS server block in `nginx/conf.d/honey.conf`) +3. **Update nginx config** to use HTTPS (uncomment HTTPS server block in `nginx/conf.d/lottery.conf`) 4. **Reload nginx**: ```bash @@ -304,21 +304,21 @@ sudo ufw enable Create a systemd service to ensure services start on boot: ```bash -sudo nano /etc/systemd/system/honey.service +sudo nano /etc/systemd/system/lottery.service ``` Add: ```ini [Unit] -Description=Honey Application +Description=Lottery Application Requires=docker.service After=docker.service [Service] Type=oneshot RemainAfterExit=yes -WorkingDirectory=/opt/honey/honey-be +WorkingDirectory=/opt/lottery/lottery-be ExecStart=/usr/local/bin/docker-compose -f docker-compose.inferno.yml up -d ExecStop=/usr/local/bin/docker-compose -f docker-compose.inferno.yml down TimeoutStartSec=0 @@ -331,8 +331,8 @@ Enable the service: ```bash sudo systemctl daemon-reload -sudo systemctl enable honey.service -sudo systemctl start honey.service +sudo systemctl enable lottery.service +sudo systemctl start lottery.service ``` #### Step 10: Set Up Grafana Integration (Production Logging) @@ -357,13 +357,13 @@ sudo systemctl start honey.service - url: http://loki:3100/loki/api/v1/push scrape_configs: - - job_name: honey-backend + - job_name: lottery-backend docker_sd_configs: - host: unix:///var/run/docker.sock refresh_interval: 5s relabel_configs: - source_labels: [__meta_docker_container_name] - regex: honey-backend + regex: lottery-backend action: keep ``` @@ -398,14 +398,14 @@ docker-compose -f docker-compose.inferno.yml logs -f app **Update application**: ```bash -cd /opt/honey/honey-be +cd /opt/lottery/lottery-be git pull docker-compose -f docker-compose.inferno.yml up -d --build ``` **Backup database**: ```bash -docker-compose -f docker-compose.inferno.yml exec db mysqldump -u honey_user -p honey_db > backup_$(date +%Y%m%d).sql +docker-compose -f docker-compose.inferno.yml exec db mysqldump -u lottery_user -p lottery_db > backup_$(date +%Y%m%d).sql ``` ## Configuration @@ -415,7 +415,7 @@ docker-compose -f docker-compose.inferno.yml exec db mysqldump -u honey_user -p The application supports two configuration strategies: 1. **Environment Variables** (Railway): Set variables in Railway dashboard -2. **Secret File** (Inferno): Mount file at `/run/secrets/honey-config.properties` +2. **Secret File** (Inferno): Mount file at `/run/secrets/lottery-config.properties` Priority: Secret file → Environment variables @@ -511,10 +511,10 @@ docker-compose up --build ## Project Structure ``` -honey-be/ +lottery-be/ ├── src/ │ ├── main/ -│ │ ├── java/com/honey/honey/ +│ │ ├── java/com/lottery/lottery/ │ │ │ ├── config/ # Configuration classes │ │ │ ├── controller/ # REST controllers │ │ │ ├── dto/ # Data transfer objects @@ -540,3 +540,4 @@ honey-be/ [Your License Here] + diff --git a/ROLLING_UPDATE_GUIDE.md b/ROLLING_UPDATE_GUIDE.md new file mode 100644 index 0000000..0bf80aa --- /dev/null +++ b/ROLLING_UPDATE_GUIDE.md @@ -0,0 +1,356 @@ +# Rolling Update Deployment Guide + +This guide explains how to perform zero-downtime deployments using the rolling update strategy. + +## Overview + +The rolling update approach allows you to deploy new backend code without any downtime for users. Here's how it works: + +1. **Build** new backend image while old container is still running +2. **Start** new container on port 8082 (old one stays on 8080) +3. **Health check** new container to ensure it's ready +4. **Switch** Nginx to point to new container (zero downtime) +5. **Stop** old container after grace period + +## Architecture + +``` +┌─────────────┐ +│ Nginx │ (Port 80/443) +│ (Host) │ +└──────┬──────┘ + │ + ├───> Backend (Port 8080) - Primary + └───> Backend-New (Port 8082) - Standby (during deployment) +``` + +## Prerequisites + +1. **Nginx running on host** (not in Docker) +2. **Backend containers** managed by Docker Compose +3. **Health check endpoint** available at `/actuator/health/readiness` +4. **Sufficient memory** for two backend containers during deployment (~24GB) + +## Quick Start + +### 1. Make Script Executable + +```bash +cd /opt/app/backend/lottery-be +chmod +x scripts/rolling-update.sh +``` + +### 2. Run Deployment + +```bash +# Load database password (if not already set) +source scripts/load-db-password.sh + +# Run rolling update +sudo ./scripts/rolling-update.sh +``` + +That's it! The script handles everything automatically. + +## What the Script Does + +1. **Checks prerequisites**: + - Verifies Docker and Nginx are available + - Ensures primary backend is running + - Loads database password + +2. **Builds new image**: + - Builds backend-new service + - Uses Docker Compose build cache for speed + +3. **Starts new container**: + - Starts `lottery-backend-new` on port 8082 + - Waits for container initialization + +4. **Health checks**: + - Checks `/actuator/health/readiness` endpoint + - Retries up to 30 times (60 seconds total) + - Fails deployment if health check doesn't pass + +5. **Updates Nginx**: + - Backs up current Nginx config + - Updates upstream to point to port 8082 + - Sets old backend (8080) as backup + - Tests Nginx configuration + +6. **Reloads Nginx**: + - Uses `systemctl reload nginx` (zero downtime) + - Traffic immediately switches to new backend + +7. **Stops old container**: + - Waits 10 seconds grace period + - Stops old backend container + - Old container can be removed or kept for rollback + +## Manual Steps (If Needed) + +If you prefer to do it manually or need to troubleshoot: + +### Step 1: Build New Image + +```bash +cd /opt/app/backend/lottery-be +source scripts/load-db-password.sh +docker-compose -f docker-compose.prod.yml --profile rolling-update build backend-new +``` + +### Step 2: Start New Container + +```bash +docker-compose -f docker-compose.prod.yml --profile rolling-update up -d backend-new +``` + +### Step 3: Health Check + +```bash +# Wait for container to be ready +sleep 10 + +# Check health +curl http://127.0.0.1:8082/actuator/health/readiness + +# Check logs +docker logs lottery-backend-new +``` + +### Step 4: Update Nginx + +```bash +# Backup config +sudo cp /etc/nginx/conf.d/lottery.conf /etc/nginx/conf.d/lottery.conf.backup + +# Edit config +sudo nano /etc/nginx/conf.d/lottery.conf +``` + +Change upstream from: +```nginx +upstream lottery_backend { + server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; +} +``` + +To: +```nginx +upstream lottery_backend { + server 127.0.0.1:8082 max_fails=3 fail_timeout=30s; + server 127.0.0.1:8080 backup; +} +``` + +### Step 5: Reload Nginx + +```bash +# Test config +sudo nginx -t + +# Reload (zero downtime) +sudo systemctl reload nginx +``` + +### Step 6: Stop Old Container + +```bash +# Wait for active connections to finish +sleep 10 + +# Stop old container +docker-compose -f docker-compose.prod.yml stop backend +``` + +## Rollback Procedure + +If something goes wrong, you can quickly rollback: + +### Automatic Rollback + +The script automatically rolls back if: +- Health check fails +- Nginx config test fails +- Nginx reload fails + +### Manual Rollback + +```bash +# 1. Restore Nginx config +sudo cp /etc/nginx/conf.d/lottery.conf.backup /etc/nginx/conf.d/lottery.conf +sudo systemctl reload nginx + +# 2. Start old backend (if stopped) +cd /opt/app/backend/lottery-be +docker-compose -f docker-compose.prod.yml start backend + +# 3. Stop new backend +docker-compose -f docker-compose.prod.yml --profile rolling-update stop backend-new +docker-compose -f docker-compose.prod.yml --profile rolling-update rm -f backend-new +``` + +## Configuration + +### Health Check Settings + +Edit `scripts/rolling-update.sh` to adjust: + +```bash +HEALTH_CHECK_RETRIES=30 # Number of retries +HEALTH_CHECK_INTERVAL=2 # Seconds between retries +GRACE_PERIOD=10 # Seconds to wait before stopping old container +``` + +### Nginx Upstream Settings + +Edit `/etc/nginx/conf.d/lottery.conf`: + +```nginx +upstream lottery_backend { + server 127.0.0.1:8082 max_fails=3 fail_timeout=30s; + server 127.0.0.1:8080 backup; # Old backend as backup + keepalive 32; +} +``` + +## Monitoring + +### During Deployment + +```bash +# Watch container status +watch -n 1 'docker ps | grep lottery-backend' + +# Monitor new backend logs +docker logs -f lottery-backend-new + +# Check Nginx access logs +sudo tail -f /var/log/nginx/access.log + +# Monitor memory usage +free -h +docker stats --no-stream +``` + +### After Deployment + +```bash +# Verify new backend is serving traffic +curl http://localhost/api/health + +# Check container status +docker ps | grep lottery-backend + +# Verify Nginx upstream +curl http://localhost/actuator/health +``` + +## Troubleshooting + +### Health Check Fails + +```bash +# Check new container logs +docker logs lottery-backend-new + +# Check if container is running +docker ps | grep lottery-backend-new + +# Test health endpoint directly +curl -v http://127.0.0.1:8082/actuator/health/readiness + +# Check database connection +docker exec lottery-backend-new wget -q -O- http://localhost:8080/actuator/health +``` + +### Nginx Reload Fails + +```bash +# Test Nginx config +sudo nginx -t + +# Check Nginx error logs +sudo tail -f /var/log/nginx/error.log + +# Verify upstream syntax +sudo nginx -T | grep -A 5 upstream +``` + +### Memory Issues + +If you run out of memory during deployment: + +```bash +# Check memory usage +free -h +docker stats --no-stream + +# Option 1: Reduce heap size temporarily +# Edit docker-compose.prod.yml, change JAVA_OPTS to use 8GB heap + +# Option 2: Stop other services temporarily +docker stop lottery-phpmyadmin # If not needed +``` + +### Old Container Won't Stop + +```bash +# Force stop +docker stop lottery-backend + +# If still running, kill it +docker kill lottery-backend + +# Remove container +docker rm lottery-backend +``` + +## Best Practices + +1. **Test in staging first** - Always test the deployment process in a staging environment + +2. **Monitor during deployment** - Watch logs and metrics during the first few deployments + +3. **Keep backups** - The script automatically backs up Nginx config, but keep your own backups too + +4. **Database migrations** - Ensure migrations are backward compatible or run them separately + +5. **Gradual rollout** - For major changes, consider deploying during low-traffic periods + +6. **Health checks** - Ensure your health check endpoint properly validates all dependencies + +7. **Graceful shutdown** - Spring Boot graceful shutdown (30s) allows active requests to finish + +## Performance Considerations + +- **Build time**: First build takes longer, subsequent builds use cache +- **Memory**: Two containers use ~24GB during deployment (brief period) +- **Network**: No network interruption, Nginx handles the switch seamlessly +- **Database**: No impact, both containers share the same database + +## Security Notes + +- New container uses same secrets and configuration as old one +- No exposure of new port to internet (only localhost) +- Nginx handles all external traffic +- Health checks are internal only + +## Next Steps + +After successful deployment: + +1. ✅ Monitor new backend for errors +2. ✅ Verify all endpoints are working +3. ✅ Check application logs +4. ✅ Remove old container image (optional): `docker image prune` + +## Support + +If you encounter issues: + +1. Check logs: `docker logs lottery-backend-new` +2. Check Nginx: `sudo nginx -t && sudo tail -f /var/log/nginx/error.log` +3. Rollback if needed (see Rollback Procedure above) +4. Review this guide's Troubleshooting section + diff --git a/VPS_DEPLOYMENT_NOTES.md b/VPS_DEPLOYMENT_NOTES.md new file mode 100644 index 0000000..9060f65 --- /dev/null +++ b/VPS_DEPLOYMENT_NOTES.md @@ -0,0 +1,208 @@ +# VPS Deployment Notes - Logging Configuration + +## Automatic Setup (Docker - Recommended) + +The Docker setup is **automatically configured** to use external logback-spring.xml. No manual setup needed! + +### How It Works + +1. **Dockerfile** automatically: + - Copies logback-spring.xml to `/app/config/logback-spring.xml` in the container + - Sets `LOGGING_CONFIG` and `LOG_DIR` environment variables + - Configures Java to use external config + +2. **docker-compose.inferno.yml** automatically: + - Mounts `/opt/app/backend/config/logback-spring.xml` → `/app/config/logback-spring.xml` (editable on VPS) + - Mounts `/opt/app/logs` → `/app/logs` (persistent log storage) + - Sets environment variables + +### Initial Setup (One-Time) + +Run the setup script to extract logback-spring.xml: + +```bash +cd /opt/app/backend +# Make script executable (if not already) +chmod +x scripts/setup-logging.sh +# Run the script +./scripts/setup-logging.sh +``` + +Or run directly with bash: +```bash +bash scripts/setup-logging.sh +``` + +Or manually: + +```bash +# Create directories +mkdir -p /opt/app/backend/config +mkdir -p /opt/app/logs + +# Extract logback-spring.xml from JAR (if building on VPS) +unzip -p target/lottery-be-*.jar BOOT-INF/classes/logback-spring.xml > /opt/app/backend/config/logback-spring.xml + +# Or copy from source +cp src/main/resources/logback-spring.xml /opt/app/backend/config/logback-spring.xml + +# Set permissions +chmod 644 /opt/app/backend/config/logback-spring.xml +``` + +### Verify Configuration + +After starting the container, check that external config is being used: + +```bash +# Check container logs +docker logs lottery-backend | grep -i "logback\|logging" + +# Check mounted file exists +ls -la /opt/app/backend/config/logback-spring.xml + +# Check log directory +ls -la /opt/app/logs/ +``` + +## Manual Setup (Non-Docker) + +If you're not using Docker, follow these steps: + +### 1. Extract logback-spring.xml from JAR + +```bash +# Option 1: Extract from JAR +unzip -p lottery-be.jar BOOT-INF/classes/logback-spring.xml > /opt/lottery-be/logback-spring.xml + +# Option 2: Copy from source code +scp logback-spring.xml user@vps:/opt/lottery-be/ +``` + +### 2. Set Up Log Directory + +```bash +# Create log directory +mkdir -p /var/log/lottery-be +chown lottery:lottery /var/log/lottery-be +chmod 755 /var/log/lottery-be +``` + +### 3. Update Your Startup Script/Service + +Add these environment variables or system properties: + +```bash +# In your startup script or systemd service: +export LOGGING_CONFIG=/opt/lottery-be/logback-spring.xml +export LOG_DIR=/var/log/lottery-be + +java -jar lottery-be.jar +``` + +Or with system properties: + +```bash +java -Dlogging.config=/opt/lottery-be/logback-spring.xml \ + -DLOG_DIR=/var/log/lottery-be \ + -jar lottery-be.jar +``` + +### 4. Verify External Config is Being Used + +Check application startup logs for: +``` +Loading configuration from: /opt/lottery-be/logback-spring.xml +``` + +If you see this, the external config is active. + +## Changing Log Level at Runtime + +### Quick Method (30 seconds to take effect) + +**For Docker deployment:** +1. Edit the mounted logback-spring.xml: + ```bash + nano /opt/app/backend/config/logback-spring.xml + ``` + +2. Change the level (example: enable DEBUG): + ```xml + + ``` + +3. Save the file. Logback will reload within 30 seconds automatically. + +4. Verify: + ```bash + tail -f /opt/app/logs/lottery-be.log + # Or from inside container: + docker exec lottery-backend tail -f /app/logs/lottery-be.log + ``` + +**For non-Docker deployment:** +1. Edit the external logback-spring.xml: + ```bash + nano /opt/lottery-be/logback-spring.xml + ``` + +2. Change the level (example: enable DEBUG): + ```xml + + ``` + +3. Save the file. Logback will reload within 30 seconds automatically. + +4. Verify: + ```bash + tail -f /var/log/lottery-be/lottery-be.log + ``` + +### Common Log Level Changes + +**Enable DEBUG for entire app:** +```xml + +``` + +**Enable DEBUG for specific service:** +```xml + +``` + +**Enable DEBUG for WebSocket:** +```xml + +``` + +**Change root level (affects everything):** +```xml + +``` + +## Important Notes + +- **Default log level**: INFO (good for production) +- **High-traffic services**: WARN (GameRoomService, WebSocketController) +- **Auto-reload**: Changes take effect within 30 seconds +- **No restart needed**: Runtime log level changes work without restarting the app +- **Log location (Docker)**: `/opt/app/logs/` on VPS (mounted to `/app/logs` in container) +- **Log location (Non-Docker)**: `/var/log/lottery-be/` (or `./logs/` if LOG_DIR not set) +- **Config location (Docker)**: `/opt/app/backend/config/logback-spring.xml` on VPS +- **Config location (Non-Docker)**: `/opt/lottery-be/logback-spring.xml` (or your custom path) + +## Troubleshooting + +**If external config is not being used:** +1. Check the path is correct +2. Verify file permissions (readable by application user) +3. Check startup logs for errors +4. Ensure `-Dlogging.config=` or `LOGGING_CONFIG` is set correctly + +**If log level changes don't work:** +1. Verify `scan="true" scanPeriod="30 seconds"` is in logback-spring.xml +2. Check for XML syntax errors +3. Wait 30 seconds after saving +4. Check application logs for Logback errors + diff --git a/VPS_DEPLOYMENT_SUMMARY.md b/VPS_DEPLOYMENT_SUMMARY.md new file mode 100644 index 0000000..0c9cd30 --- /dev/null +++ b/VPS_DEPLOYMENT_SUMMARY.md @@ -0,0 +1,188 @@ +# VPS Deployment Summary + +## ✅ Compatibility Check + +### Backend (lottery-be) + +✅ **Dockerfile**: Production-ready +- Multi-stage build (Maven → JRE) +- Exposes port 8080 (internal only) +- HTTP only (no HTTPS configuration) +- Binds to 0.0.0.0 by default (Spring Boot default) +- Graceful shutdown supported + +✅ **Configuration**: Externalized +- Database connection via environment variables +- Avatar storage path configurable (`APP_AVATAR_STORAGE_PATH`) +- All sensitive data via `.env` file +- CORS configured via `FRONTEND_URL` env var + +✅ **File Uploads**: Persistent storage ready +- Avatar path configurable and mountable as Docker volume +- Uses filesystem (not ephemeral storage) +- Path: `/app/data/avatars` (configurable) + +✅ **Networking**: Internal Docker network +- No ports exposed to host in production compose +- Accessible only via Nginx reverse proxy +- Uses Docker bridge network + +✅ **Production Readiness**: +- Logging to stdout/stderr (Docker logs) +- Health checks configured +- Graceful shutdown +- No dev-only features enabled + +### Frontend (lottery-fe) + +✅ **Build Mode**: Production-ready +- `npm run build` creates static files in `dist/` +- Vite production build configured + +✅ **API Base URL**: Configurable +- Uses relative URLs in production (empty string) +- Falls back to `localhost:8080` in development +- Can be overridden via `VITE_API_BASE_URL` env var + +✅ **Docker Usage**: Optional +- Dockerfile exists but not required for VPS +- Static files can be served directly by Nginx + +✅ **Telegram Mini App**: Ready +- Works under HTTPS +- No localhost assumptions +- Uses relative API URLs + +## 📋 Required Changes Made + +### Frontend Changes + +1. **API Base URL Configuration** (`src/api.js`, `src/auth/authService.js`, `src/services/gameWebSocket.js`, `src/utils/remoteLogger.js`) + - Changed to use relative URLs in production + - Falls back to `localhost:8080` only in development + - Pattern: `import.meta.env.VITE_API_BASE_URL || (import.meta.env.PROD ? "" : "http://localhost:8080")` + +### Backend Changes + +✅ **No changes required** - Already production-ready! + +## 📁 New Files Created + +1. **`docker-compose.prod.yml`** - Production Docker Compose configuration + - No port exposure to host + - Persistent volumes for database and avatars + - Health checks configured + - Internal Docker network + +2. **`nginx.conf.template`** - Nginx reverse proxy configuration + - HTTPS termination + - Frontend static file serving + - Backend API proxying (`/api/*`) + - WebSocket support (`/ws`) + - Avatar file serving (`/avatars/*`) + - Security headers + - Gzip compression + +3. **`DEPLOYMENT_GUIDE.md`** - Comprehensive deployment guide + - Step-by-step instructions + - Troubleshooting section + - Maintenance commands + - Security checklist + +## 🚀 Deployment Steps Overview + +1. **VPS Setup**: Install Docker, Docker Compose, Nginx, Certbot +2. **Directory Structure**: Create `/opt/app` with subdirectories +3. **Backend Deployment**: Copy files, create secret file at `/run/secrets/lottery-config.properties`, build and start +4. **Frontend Deployment**: Build locally, copy `dist/` to VPS +5. **Nginx Configuration**: Copy template, update domain, link config +6. **SSL Setup**: Obtain Let's Encrypt certificate +7. **Telegram Webhook**: Update webhook URL +8. **Verification**: Test all endpoints and functionality + +## 🔧 Configuration Required + +### Backend Secret File (`/run/secrets/lottery-config.properties`) + +All configuration is stored in a mounted secret file. See `lottery-config.properties.template` for the complete template. + +**Required variables:** +- `SPRING_DATASOURCE_URL` +- `SPRING_DATASOURCE_USERNAME` +- `SPRING_DATASOURCE_PASSWORD` +- `TELEGRAM_BOT_TOKEN` +- `TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN` +- `TELEGRAM_FOLLOW_TASK_CHANNEL_ID` +- `FRONTEND_URL` + +**Optional variables:** +- `APP_AVATAR_STORAGE_PATH` +- `APP_AVATAR_PUBLIC_BASE_URL` +- `APP_SESSION_MAX_ACTIVE_PER_USER` +- `APP_SESSION_CLEANUP_BATCH_SIZE` +- `APP_SESSION_CLEANUP_MAX_BATCHES` +- `GEOIP_DB_PATH` + +**Note:** The MySQL container also needs `DB_PASSWORD` and `DB_ROOT_PASSWORD` as environment variables (should match `SPRING_DATASOURCE_PASSWORD`). + +## 📂 Final Directory Structure on VPS + +``` +/opt/app/ +├── backend/ +│ ├── Dockerfile +│ ├── docker-compose.prod.yml +│ ├── lottery-config.properties.template +│ └── [source files] +├── frontend/ +│ └── dist/ (Vite production build) +├── nginx/ +│ └── nginx.conf +├── data/ +│ └── avatars/ (persistent uploads) +└── mysql/ + └── data/ (persistent DB storage) + +/run/secrets/ +└── lottery-config.properties (mounted secret file) +``` + +## ✅ Verification Checklist + +Before going live: + +- [ ] All environment variables set in `.env` +- [ ] Backend containers running (`docker ps`) +- [ ] Frontend `dist/` folder populated +- [ ] Nginx configuration tested (`nginx -t`) +- [ ] SSL certificate installed and valid +- [ ] Telegram webhook updated +- [ ] Health checks passing (`/actuator/health`) +- [ ] Frontend loads in browser +- [ ] API calls work (check browser console) +- [ ] WebSocket connects (game updates work) +- [ ] Avatar uploads work +- [ ] Database persists data (restart test) + +## 🔒 Security Notes + +- Backend port 8080 not exposed to host +- MySQL port 3306 not exposed to host +- HTTPS enforced (HTTP → HTTPS redirect) +- Strong passwords required +- `.env` file permissions restricted +- Firewall recommended (UFW) + +## 📝 Next Steps + +1. Review `DEPLOYMENT_GUIDE.md` for detailed instructions +2. Prepare your VPS (Ubuntu recommended) +3. Follow the step-by-step guide +4. Test thoroughly before going live +5. Set up monitoring and backups + +--- + +**Status**: ✅ Ready for VPS Deployment +**Last Updated**: 2026-01-24 + diff --git a/docker-compose.inferno.yml b/docker-compose.inferno.yml index 8df3090..b9ea1df 100644 --- a/docker-compose.inferno.yml +++ b/docker-compose.inferno.yml @@ -3,48 +3,56 @@ version: "3.9" services: db: image: mysql:8.0 - container_name: honey-mysql + container_name: lottery-mysql restart: always environment: - MYSQL_DATABASE: honey_db - MYSQL_USER: honey_user + MYSQL_DATABASE: lottery_db + MYSQL_USER: lottery_user MYSQL_PASSWORD: ${MYSQL_PASSWORD} MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} volumes: - - honey_mysql_data:/var/lib/mysql + - lottery_mysql_data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] interval: 10s timeout: 5s retries: 5 networks: - - honey-network + - lottery-network app: build: context: . dockerfile: Dockerfile.inferno - container_name: honey-backend + container_name: lottery-backend restart: always depends_on: db: condition: service_healthy environment: - - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/honey_db - - SPRING_DATASOURCE_USERNAME=honey_user + - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/lottery_db + - SPRING_DATASOURCE_USERNAME=lottery_user - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD} - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN} - FRONTEND_URL=${FRONTEND_URL} + # Logging configuration (external logback-spring.xml) + - LOGGING_CONFIG=/app/config/logback-spring.xml + - LOG_DIR=/app/logs volumes: # Mount secret file from tmpfs - /run/secrets:/run/secrets:ro + # Mount logback config directory (editable on VPS without rebuilding) + # Note: File must exist on host before mounting. Run setup-logging.sh first. + - /opt/app/backend/config:/app/config:rw + # Mount logs directory (persistent storage) + - /opt/app/logs:/app/logs networks: - - honey-network + - lottery-network # Don't expose port directly - nginx will handle it nginx: image: nginx:alpine - container_name: honey-nginx + container_name: lottery-nginx restart: always ports: - "80:80" @@ -57,12 +65,13 @@ services: depends_on: - app networks: - - honey-network + - lottery-network volumes: - honey_mysql_data: + lottery_mysql_data: networks: - honey-network: + lottery-network: driver: bridge + diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..d5c9f35 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,193 @@ +version: "3.9" + +services: + db: + image: mysql:8.0 + container_name: lottery-mysql + restart: always + # Database credentials are read from the secret file via backend container + # The backend will construct the connection URL from SPRING_DATASOURCE_* properties + # For MySQL container, we need to set these via environment or use a separate secret + # Option 1: Use environment variables (for MySQL container only) + # Note: MYSQL_USER cannot be "root" - root user is configured via MYSQL_ROOT_PASSWORD only + environment: + MYSQL_DATABASE: lottery_db + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + # Option 2: Mount secret file and read values (more secure) + # This requires parsing the secret file or using a script + # For simplicity, we'll use environment variables for MySQL container + # The secret file is primarily for the backend application + # Do NOT expose MySQL port to host - only accessible within Docker network + # ports: + # - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + # Mount MySQL performance configuration (created on VPS at /opt/app/mysql/conf/my.cnf) + - /opt/app/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf:ro + # Resource limits for MySQL (16GB buffer pool + 2GB overhead) + deploy: + resources: + limits: + cpus: '2.0' + memory: 18G + healthcheck: + # Use shell to access environment variable (Docker Compose doesn't interpolate in healthcheck arrays) + test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD || exit 1"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - lottery-network + + backend: + build: + context: . + dockerfile: Dockerfile + container_name: lottery-backend + depends_on: + db: + condition: service_healthy + # Expose backend port to localhost only (for Nginx on host to access) + # This is safe - only accessible from the host, not from internet + # Port 8080 is the primary/active backend + ports: + - "127.0.0.1:8080:8080" + # Labels for rolling update management + labels: + - "deployment.role=primary" + - "deployment.version=current" + volumes: + # Mount persistent avatar storage (absolute path for consistency) + - /opt/app/data/avatars:/app/data/avatars + # Mount secret configuration file (read-only) + - /run/secrets/lottery-config.properties:/run/secrets/lottery-config.properties:ro + # Mount logback config directory (editable on VPS without rebuilding) + # Note: File must exist on host before mounting. Run setup-logging.sh first. + - /opt/app/backend/config:/app/config:rw + # Mount logs directory (persistent storage) + - /opt/app/logs:/app/logs + environment: + # Java memory settings: 10GB heap (Xms/Xmx) + G1GC for low latency + # -Xms: Start with 10GB (prevents resizing overhead) + # -Xmx: Max limit 10GB + # -XX:+UseG1GC: Use G1 garbage collector (best for large heaps) + # -XX:MaxGCPauseMillis=200: Target max GC pause time + JAVA_OPTS: -Xms10g -Xmx10g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 + # Logging configuration (external logback-spring.xml) + LOGGING_CONFIG: /app/config/logback-spring.xml + LOG_DIR: /app/logs + # Resource limits for backend (10GB heap + 2GB overhead for stack/metaspace) + deploy: + resources: + limits: + cpus: '4.0' + memory: 12G + networks: + - lottery-network + restart: always + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/actuator/health/liveness"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + backend-new: + # This service is used during rolling updates + # It will be started manually via deployment script + build: + context: . + dockerfile: Dockerfile + container_name: lottery-backend-new + depends_on: + db: + condition: service_healthy + # Port 8082 is the new/standby backend during deployment (8081 is used by phpMyAdmin) + ports: + - "127.0.0.1:8082:8080" + profiles: + - rolling-update + # Labels for rolling update management + labels: + - "deployment.role=standby" + - "deployment.version=new" + volumes: + # Mount persistent avatar storage (absolute path for consistency) + - /opt/app/data/avatars:/app/data/avatars + # Mount secret configuration file (read-only) + - /run/secrets/lottery-config.properties:/run/secrets/lottery-config.properties:ro + # Mount logback config directory (editable on VPS without rebuilding) + # Note: File must exist on host before mounting. Run setup-logging.sh first. + - /opt/app/backend/config:/app/config:rw + # Mount logs directory (persistent storage) + - /opt/app/logs:/app/logs + environment: + # Java memory settings: 10GB heap (Xms/Xmx) + G1GC for low latency + # -Xms: Start with 10GB (prevents resizing overhead) + # -Xmx: Max limit 10GB + # -XX:+UseG1GC: Use G1 garbage collector (best for large heaps) + # -XX:MaxGCPauseMillis=200: Target max GC pause time + JAVA_OPTS: -Xms10g -Xmx10g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 + # Logging configuration (external logback-spring.xml) + LOGGING_CONFIG: /app/config/logback-spring.xml + LOG_DIR: /app/logs + # Resource limits for backend (10GB heap + 2GB overhead for stack/metaspace) + deploy: + resources: + limits: + cpus: '4.0' + memory: 12G + networks: + - lottery-network + restart: always + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/actuator/health/liveness"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + + phpmyadmin: + image: phpmyadmin:latest + container_name: lottery-phpmyadmin + restart: always + depends_on: + db: + condition: service_healthy + # Expose phpMyAdmin to localhost only (Nginx will proxy it with path protection) + ports: + - "127.0.0.1:8081:80" + environment: + # Connect to MySQL service using Docker service name + PMA_HOST: db + PMA_PORT: 3306 + # Use the same root password as MySQL container + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + # Security: Set upload limit + UPLOAD_LIMIT: 64M + # Configure absolute URI so phpMyAdmin generates correct URLs for assets + # This variable must be set from a secret file on the VPS (not in git) + # Example: export PMA_ABSOLUTE_URI="https://win-spin.live/your-secret-path" + PMA_ABSOLUTE_URI: ${PMA_ABSOLUTE_URI:-} + # Tell phpMyAdmin it's behind a proxy using HTTPS + PMA_SSL: "true" + # Trust proxy headers (X-Forwarded-Proto, etc.) + PMA_TRUSTED_PROXIES: "127.0.0.1" + networks: + - lottery-network + # Resource limits for phpMyAdmin + deploy: + resources: + limits: + cpus: '1.0' + memory: 512M + +volumes: + mysql_data: + driver: local + +networks: + lottery-network: + driver: bridge + diff --git a/docker-compose.yml b/docker-compose.yml index cf46bb4..dbd2a1b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,17 +3,17 @@ version: "3.9" services: db: image: mysql:8.0 - container_name: honey-mysql + container_name: lottery-mysql restart: always environment: - MYSQL_DATABASE: ${DB_NAME:honey_db} + MYSQL_DATABASE: ${DB_NAME:lottery_db} MYSQL_USER: ${DB_USERNAME:root} MYSQL_PASSWORD: ${DB_PASSWORD:password} MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:password} ports: - "3306:3306" volumes: - - honey_mysql_data:/var/lib/mysql + - lottery_mysql_data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:password}"] interval: 10s @@ -24,17 +24,18 @@ services: env_file: - .env build: . - container_name: honey-backend + container_name: lottery-backend depends_on: db: condition: service_healthy ports: - "8080:8080" environment: - - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/${DB_NAME:honey_db} + - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/${DB_NAME:lottery_db} - SPRING_DATASOURCE_USERNAME=${DB_USERNAME:root} - SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD:password} volumes: - honey_mysql_data: + lottery_mysql_data: + diff --git a/lottery-config.properties.template b/lottery-config.properties.template new file mode 100644 index 0000000..3451b89 --- /dev/null +++ b/lottery-config.properties.template @@ -0,0 +1,62 @@ +# Lottery Application Configuration +# Copy this file to /run/secrets/lottery-config.properties on your VPS +# Replace all placeholder values with your actual configuration + +# ============================================ +# Database Configuration +# ============================================ +# SPRING_DATASOURCE_URL format: jdbc:mysql://:/ +# +# How to determine the URL: +# - Hostname: 'db' (this is the MySQL service name in docker-compose.prod.yml) +# * In Docker Compose, services communicate using their service names +# * The MySQL service is named 'db', so use 'db' as the hostname +# * Both containers are on the same Docker network, so 'db' resolves to the MySQL container +# - Port: '3306' (default MySQL port, internal to Docker network) +# - Database name: 'lottery_db' (must match MYSQL_DATABASE in docker-compose.prod.yml) +# +# Example: jdbc:mysql://db:3306/lottery_db +# └─┬─┘ └┬┘ └─┬──┘ └───┬────┘ +# │ │ │ └─ Database name +# │ │ └─ Port (3306 is MySQL default) +# │ └─ Service name in docker-compose (acts as hostname) +# └─ JDBC protocol for MySQL +# +# IMPORTANT: Use 'db' as hostname, NOT 'localhost' or '127.0.0.1' +# This is an internal Docker network connection +SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/lottery_db +SPRING_DATASOURCE_USERNAME=root +SPRING_DATASOURCE_PASSWORD=your_secure_database_password_here + +# ============================================ +# Telegram Bot Configuration +# ============================================ +TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here +TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN=your_channel_checker_bot_token_here +TELEGRAM_FOLLOW_TASK_CHANNEL_ID=@your_channel_name + +# ============================================ +# Frontend Configuration +# ============================================ +FRONTEND_URL=https://yourdomain.com + +# ============================================ +# Avatar Storage Configuration +# ============================================ +APP_AVATAR_STORAGE_PATH=/app/data/avatars +APP_AVATAR_PUBLIC_BASE_URL= +APP_AVATAR_MAX_SIZE_BYTES=2097152 +APP_AVATAR_MAX_DIMENSION=512 + +# ============================================ +# Session Configuration (Optional - defaults shown) +# ============================================ +APP_SESSION_MAX_ACTIVE_PER_USER=5 +APP_SESSION_CLEANUP_BATCH_SIZE=5000 +APP_SESSION_CLEANUP_MAX_BATCHES=20 + +# ============================================ +# GeoIP Configuration (Optional) +# ============================================ +GEOIP_DB_PATH= + diff --git a/nginx.conf.template b/nginx.conf.template new file mode 100644 index 0000000..2176aac --- /dev/null +++ b/nginx.conf.template @@ -0,0 +1,128 @@ +# Nginx configuration for Lottery Application +# Place this file at: /opt/app/nginx/nginx.conf +# +# This configuration assumes: +# - Frontend static files are at: /opt/app/frontend/dist +# - Avatar files are at: /opt/app/data/avatars +# - Backend is accessible at: http://127.0.0.1:8080 (exposed to localhost only) +# - HTTPS is handled by Nginx (SSL certificates should be configured separately) + +# Upstream backend (using localhost since Nginx runs on host, not in Docker) +upstream backend { + server 127.0.0.1:8080; +} + +# Redirect HTTP to HTTPS +server { + listen 80; + server_name _; # Replace with your domain name + + # For Let's Encrypt ACME challenge + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # Redirect all other HTTP traffic to HTTPS + location / { + return 301 https://$host$request_uri; + } +} + +# HTTPS server +server { + listen 443 ssl http2; + server_name _; # Replace with your domain name + + # SSL certificate configuration + # Update these paths to your actual certificate files + ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; + + # SSL configuration (recommended settings) + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Root directory for frontend static files + root /opt/app/frontend/dist; + index index.html; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript; + + # Serve avatar files with aggressive caching + # Avatars are served by backend, but we can also serve them directly from filesystem + location /avatars/ { + alias /opt/app/data/avatars/; + expires 1h; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Backend API endpoints + location /api/ { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + + # Timeouts for long-running requests + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # WebSocket endpoint (for game updates) + location /ws { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket timeouts + proxy_connect_timeout 7d; + proxy_send_timeout 7d; + proxy_read_timeout 7d; + } + + # Frontend static files (SPA routing) + location / { + try_files $uri $uri/ /index.html; + expires 1h; + add_header Cache-Control "public"; + } + + # Cache static assets (JS, CSS, images) + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Health check endpoint (optional, for monitoring) + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +} + diff --git a/nginx/conf.d/honey.conf b/nginx/conf.d/lottery.conf similarity index 79% rename from nginx/conf.d/honey.conf rename to nginx/conf.d/lottery.conf index 8a7d92e..db343a7 100644 --- a/nginx/conf.d/honey.conf +++ b/nginx/conf.d/lottery.conf @@ -1,5 +1,12 @@ -upstream honey_backend { - server app:8080; +upstream lottery_backend { + # Primary backend (port 8080) + server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; + # Standby backend (port 8082) - used during rolling updates + # Uncomment the line below to switch traffic to new backend + # server 127.0.0.1:8082 max_fails=3 fail_timeout=30s backup; + + # Health check configuration + keepalive 32; } server { @@ -16,7 +23,7 @@ server { # API endpoints location /api/ { - proxy_pass http://honey_backend; + proxy_pass http://lottery_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; @@ -34,7 +41,7 @@ server { # Actuator endpoints (for health checks) location /actuator/ { - proxy_pass http://honey_backend; + proxy_pass http://lottery_backend; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -44,7 +51,7 @@ server { # Ping endpoint location /ping { - proxy_pass http://honey_backend; + proxy_pass http://lottery_backend; proxy_http_version 1.1; proxy_set_header Host $host; } @@ -74,3 +81,4 @@ server { # # ... # } + diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 63d9fda..b380f0d 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -32,3 +32,4 @@ http { include /etc/nginx/conf.d/*.conf; } + diff --git a/pom.xml b/pom.xml index e5093ae..88a43ce 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,8 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.honey - honey-be + com.lottery + lottery-be 1.0.0 jar @@ -79,6 +79,51 @@ 4.2.0 + + + org.springframework.boot + spring-boot-starter-websocket + + + + + org.telegram + telegrambots + 6.9.0 + + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + + + + + org.springframework.boot + spring-boot-starter-security + + + + + io.jsonwebtoken + jjwt-api + 0.12.3 + + + io.jsonwebtoken + jjwt-impl + 0.12.3 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.12.3 + runtime + + diff --git a/scripts/backup-database.sh b/scripts/backup-database.sh new file mode 100644 index 0000000..d0b2bb5 --- /dev/null +++ b/scripts/backup-database.sh @@ -0,0 +1,216 @@ +#!/bin/bash +# Database Backup Script for Lottery Application +# This script creates a MySQL dump and transfers it to the backup VPS +# +# Usage: +# ./scripts/backup-database.sh [--keep-local] [--compress] +# +# Options: +# --keep-local Keep a local copy of the backup (default: delete after transfer) +# --compress Compress the backup before transfer (default: gzip) +# +# Prerequisites: +# 1. SSH key-based authentication to backup VPS (5.45.77.77) +# 2. Database password accessible via /run/secrets/lottery-config.properties +# 3. Docker container 'lottery-mysql' running +# +# Backup location on backup VPS: /raid/backup/acc_260182/ + +set -euo pipefail + +# Configuration +BACKUP_VPS_HOST="5.45.77.77" +BACKUP_VPS_USER="acc_260182" # User account on backup VPS +BACKUP_VPS_PATH="/raid/backup/acc_260182" +MYSQL_CONTAINER="lottery-mysql" +MYSQL_DATABASE="lottery_db" +SECRET_FILE="/run/secrets/lottery-config.properties" +BACKUP_DIR="/opt/app/backups" +KEEP_LOCAL=false +COMPRESS=true + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --keep-local) + KEEP_LOCAL=true + shift + ;; + --no-compress) + COMPRESS=false + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 [--keep-local] [--no-compress]" + exit 1 + ;; + esac +done + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Logging function +log() { + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" >&2 +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +# Check if running as root or with sudo +if [ "$EUID" -ne 0 ]; then + error "This script must be run as root (or with sudo)" + exit 1 +fi + +# Load database password +if [ ! -f "$SECRET_FILE" ]; then + error "Secret file not found at $SECRET_FILE" + exit 1 +fi + +DB_PASSWORD=$(grep "^SPRING_DATASOURCE_PASSWORD=" "$SECRET_FILE" | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + +if [ -z "$DB_PASSWORD" ]; then + error "SPRING_DATASOURCE_PASSWORD not found in secret file" + exit 1 +fi + +# Check if MySQL container is running +if ! docker ps --format '{{.Names}}' | grep -q "^${MYSQL_CONTAINER}$"; then + error "MySQL container '${MYSQL_CONTAINER}' is not running" + exit 1 +fi + +# Create backup directory if it doesn't exist +mkdir -p "$BACKUP_DIR" + +# Generate backup filename with timestamp +TIMESTAMP=$(date +'%Y%m%d_%H%M%S') +BACKUP_FILENAME="lottery_db_backup_${TIMESTAMP}.sql" +BACKUP_PATH="${BACKUP_DIR}/${BACKUP_FILENAME}" + +# If compression is enabled, add .gz extension +if [ "$COMPRESS" = true ]; then + BACKUP_FILENAME="${BACKUP_FILENAME}.gz" + BACKUP_PATH="${BACKUP_DIR}/${BACKUP_FILENAME}" +fi + +log "Starting database backup..." +log "Database: ${MYSQL_DATABASE}" +log "Container: ${MYSQL_CONTAINER}" +log "Backup file: ${BACKUP_FILENAME}" + +# Create MySQL dump +log "Creating MySQL dump..." + +if [ "$COMPRESS" = true ]; then + # Dump and compress in one step (saves disk space) + if docker exec "${MYSQL_CONTAINER}" mysqldump \ + -u root \ + -p"${DB_PASSWORD}" \ + --single-transaction \ + --routines \ + --triggers \ + --events \ + --quick \ + --lock-tables=false \ + "${MYSQL_DATABASE}" | gzip > "${BACKUP_PATH}"; then + log "✅ Database dump created and compressed: ${BACKUP_PATH}" + else + error "Failed to create database dump" + exit 1 + fi +else + # Dump without compression + if docker exec "${MYSQL_CONTAINER}" mysqldump \ + -u root \ + -p"${DB_PASSWORD}" \ + --single-transaction \ + --routines \ + --triggers \ + --events \ + --quick \ + --lock-tables=false \ + "${MYSQL_DATABASE}" > "${BACKUP_PATH}"; then + log "✅ Database dump created: ${BACKUP_PATH}" + else + error "Failed to create database dump" + exit 1 + fi +fi + +# Get backup file size +BACKUP_SIZE=$(du -h "${BACKUP_PATH}" | cut -f1) +log "Backup size: ${BACKUP_SIZE}" + +# Transfer to backup VPS +log "Transferring backup to backup VPS (${BACKUP_VPS_HOST})..." + +# Test SSH connection first +if ! ssh -o ConnectTimeout=10 -o BatchMode=yes "${BACKUP_VPS_USER}@${BACKUP_VPS_HOST}" "echo 'SSH connection successful'" > /dev/null 2>&1; then + error "Cannot connect to backup VPS via SSH" + error "Please ensure:" + error " 1. SSH key-based authentication is set up" + error " 2. Backup VPS is accessible from this server" + error " 3. User '${BACKUP_VPS_USER}' has access to ${BACKUP_VPS_PATH}" + + if [ "$KEEP_LOCAL" = true ]; then + warn "Keeping local backup despite transfer failure: ${BACKUP_PATH}" + else + rm -f "${BACKUP_PATH}" + fi + exit 1 +fi + +# Create backup directory on remote VPS if it doesn't exist +ssh "${BACKUP_VPS_USER}@${BACKUP_VPS_HOST}" "mkdir -p ${BACKUP_VPS_PATH}" + +# Transfer the backup file +if scp "${BACKUP_PATH}" "${BACKUP_VPS_USER}@${BACKUP_VPS_HOST}:${BACKUP_VPS_PATH}/"; then + log "✅ Backup transferred successfully to ${BACKUP_VPS_HOST}:${BACKUP_VPS_PATH}/${BACKUP_FILENAME}" + + # Verify remote file exists + REMOTE_SIZE=$(ssh "${BACKUP_VPS_USER}@${BACKUP_VPS_HOST}" "du -h ${BACKUP_VPS_PATH}/${BACKUP_FILENAME} 2>/dev/null | cut -f1" || echo "0") + if [ "$REMOTE_SIZE" != "0" ]; then + log "✅ Remote backup verified (size: ${REMOTE_SIZE})" + else + warn "Could not verify remote backup file" + fi +else + error "Failed to transfer backup to backup VPS" + if [ "$KEEP_LOCAL" = true ]; then + warn "Keeping local backup despite transfer failure: ${BACKUP_PATH}" + else + rm -f "${BACKUP_PATH}" + fi + exit 1 +fi + +# Clean up local backup if not keeping it +if [ "$KEEP_LOCAL" = false ]; then + rm -f "${BACKUP_PATH}" + log "Local backup file removed (transferred successfully)" +fi + +# Clean up old backups on remote VPS (keep last 10 days) +log "Cleaning up old backups on remote VPS (keeping last 10 days)..." +ssh "${BACKUP_VPS_USER}@${BACKUP_VPS_HOST}" "find ${BACKUP_VPS_PATH} -name 'lottery_db_backup_*.sql*' -type f -mtime +10 -delete" || warn "Failed to clean up old backups" + +# Count remaining backups +BACKUP_COUNT=$(ssh "${BACKUP_VPS_USER}@${BACKUP_VPS_HOST}" "ls -1 ${BACKUP_VPS_PATH}/lottery_db_backup_*.sql* 2>/dev/null | wc -l" || echo "0") +log "Total backups on remote VPS: ${BACKUP_COUNT}" + +log "✅ Backup completed successfully!" +log " Remote location: ${BACKUP_VPS_HOST}:${BACKUP_VPS_PATH}/${BACKUP_FILENAME}" + diff --git a/scripts/create-secret-file-from-template.sh b/scripts/create-secret-file-from-template.sh new file mode 100644 index 0000000..e1dd53a --- /dev/null +++ b/scripts/create-secret-file-from-template.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Script to create secret file from template +# Usage: ./create-secret-file-from-template.sh /path/to/template /path/to/output + +TEMPLATE_FILE="${1:-lottery-config.properties.template}" +OUTPUT_FILE="${2:-/run/secrets/lottery-config.properties}" +OUTPUT_DIR=$(dirname "$OUTPUT_FILE") + +# Check if template exists +if [ ! -f "$TEMPLATE_FILE" ]; then + echo "❌ Template file not found: $TEMPLATE_FILE" + exit 1 +fi + +# Create output directory if it doesn't exist +mkdir -p "$OUTPUT_DIR" + +# Copy template to output +cp "$TEMPLATE_FILE" "$OUTPUT_FILE" + +# Set secure permissions (read-only for owner, no access for others) +chmod 600 "$OUTPUT_FILE" + +echo "✅ Secret file created at $OUTPUT_FILE" +echo "⚠️ IMPORTANT: Edit this file and replace all placeholder values with your actual configuration!" +echo "⚠️ After editing, ensure permissions are secure: chmod 600 $OUTPUT_FILE" + + + diff --git a/scripts/create-secret-file.sh b/scripts/create-secret-file.sh index 2247b89..3d2f23f 100644 --- a/scripts/create-secret-file.sh +++ b/scripts/create-secret-file.sh @@ -3,7 +3,7 @@ # Create secret file from environment variables for testing ConfigLoader # This simulates the mounted secret file approach used in Inferno -SECRET_FILE="/run/secrets/honey-config.properties" +SECRET_FILE="/run/secrets/lottery-config.properties" SECRET_DIR="/run/secrets" # Create directory if it doesn't exist @@ -25,3 +25,4 @@ chmod 644 "$SECRET_FILE" echo "✅ Secret file created at $SECRET_FILE from environment variables" + diff --git a/scripts/diagnose-backup-permissions.sh b/scripts/diagnose-backup-permissions.sh new file mode 100644 index 0000000..5fddf7b --- /dev/null +++ b/scripts/diagnose-backup-permissions.sh @@ -0,0 +1,227 @@ +#!/bin/bash +# Diagnostic script for backup-database.sh permission issues +# Run this on your VPS to identify the root cause + +SCRIPT="/opt/app/backend/lottery-be/scripts/backup-database.sh" +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo "==========================================" +echo "Backup Script Permission Diagnostic" +echo "==========================================" +echo "" + +# 1. File exists +echo "1. Checking if file exists..." +if [ -f "$SCRIPT" ]; then + echo -e " ${GREEN}✅ File exists${NC}" +else + echo -e " ${RED}❌ File NOT found at: $SCRIPT${NC}" + echo " Please verify the path." + exit 1 +fi +echo "" + +# 2. File permissions +echo "2. File permissions:" +ls -la "$SCRIPT" +echo "" + +# 3. Is executable +echo "3. Is file executable?" +if [ -x "$SCRIPT" ]; then + echo -e " ${GREEN}✅ File is executable${NC}" +else + echo -e " ${RED}❌ File is NOT executable${NC}" + echo " Fix: chmod +x $SCRIPT" +fi +echo "" + +# 4. Shebang line +echo "4. Shebang line (first line):" +SHEBANG=$(head -1 "$SCRIPT") +echo " $SHEBANG" +if [[ "$SHEBANG" == "#!/bin/bash" ]] || [[ "$SHEBANG" == "#!/usr/bin/bash" ]]; then + echo -e " ${GREEN}✅ Shebang looks correct${NC}" +else + echo -e " ${YELLOW}⚠️ Unexpected shebang${NC}" +fi +echo "" + +# 5. Bash exists +echo "5. Checking if bash interpreter exists:" +if [ -f /bin/bash ]; then + echo -e " ${GREEN}✅ /bin/bash exists${NC}" + /bin/bash --version | head -1 +elif [ -f /usr/bin/bash ]; then + echo -e " ${GREEN}✅ /usr/bin/bash exists${NC}" + /usr/bin/bash --version | head -1 +else + echo -e " ${RED}❌ bash not found in /bin/bash or /usr/bin/bash${NC}" + echo " Found at: $(which bash 2>/dev/null || echo 'NOT FOUND')" +fi +echo "" + +# 6. Line endings +echo "6. Checking line endings:" +FILE_TYPE=$(file "$SCRIPT") +echo " $FILE_TYPE" +if echo "$FILE_TYPE" | grep -q "CRLF"; then + echo -e " ${RED}❌ File has Windows line endings (CRLF)${NC}" + echo " Fix: dos2unix $SCRIPT" + echo " Or: sed -i 's/\r$//' $SCRIPT" +elif echo "$FILE_TYPE" | grep -q "ASCII text"; then + echo -e " ${GREEN}✅ Line endings look correct (LF)${NC}" +else + echo -e " ${YELLOW}⚠️ Could not determine line endings${NC}" +fi +echo "" + +# 7. Mount options +echo "7. Checking filesystem mount options:" +MOUNT_INFO=$(mount | grep -E "(/opt|/app)" || echo "Not a separate mount") +echo " $MOUNT_INFO" +if echo "$MOUNT_INFO" | grep -q "noexec"; then + echo -e " ${RED}❌ Filesystem mounted with 'noexec' flag${NC}" + echo " This prevents script execution!" + echo " Fix: Remove 'noexec' from /etc/fstab and remount" +else + echo -e " ${GREEN}✅ No 'noexec' flag detected${NC}" +fi +echo "" + +# 8. SELinux +echo "8. Checking SELinux:" +if command -v getenforce &> /dev/null; then + SELINUX_STATUS=$(getenforce 2>/dev/null) + echo " Status: $SELINUX_STATUS" + if [ "$SELINUX_STATUS" = "Enforcing" ]; then + echo -e " ${YELLOW}⚠️ SELinux is enforcing - may block execution${NC}" + echo " Check context: ls -Z $SCRIPT" + else + echo -e " ${GREEN}✅ SELinux not blocking (or disabled)${NC}" + fi +else + echo -e " ${GREEN}✅ SELinux not installed${NC}" +fi +echo "" + +# 9. Directory permissions +echo "9. Parent directory permissions:" +DIR=$(dirname "$SCRIPT") +ls -ld "$DIR" +if [ -x "$DIR" ]; then + echo -e " ${GREEN}✅ Directory is executable${NC}" +else + echo -e " ${RED}❌ Directory is NOT executable${NC}" + echo " Fix: chmod +x $DIR" +fi +echo "" + +# 10. Syntax check +echo "10. Checking script syntax:" +if bash -n "$SCRIPT" 2>&1; then + echo -e " ${GREEN}✅ Syntax is valid${NC}" +else + echo -e " ${RED}❌ Syntax errors found${NC}" + bash -n "$SCRIPT" +fi +echo "" + +# 11. Test execution +echo "11. Testing script execution (dry run):" +echo " Attempting to read first 10 lines..." +if head -10 "$SCRIPT" > /dev/null 2>&1; then + echo -e " ${GREEN}✅ Can read script${NC}" +else + echo -e " ${RED}❌ Cannot read script${NC}" +fi +echo "" + +# 12. Cron job check +echo "12. Checking cron configuration:" +if [ "$EUID" -eq 0 ]; then + echo " Root's crontab:" + crontab -l 2>/dev/null | grep -i backup || echo " (No backup cron job found in root's crontab)" + echo "" + echo " To check cron job, run: sudo crontab -l" +else + echo " (Run as root to check crontab: sudo crontab -l)" +fi +echo "" + +# 13. Environment check +echo "13. Checking required commands:" +REQUIRED_COMMANDS=("docker" "ssh" "gzip" "bash") +for cmd in "${REQUIRED_COMMANDS[@]}"; do + if command -v "$cmd" &> /dev/null; then + CMD_PATH=$(which "$cmd") + echo -e " ${GREEN}✅ $cmd${NC} found at: $CMD_PATH" + else + echo -e " ${RED}❌ $cmd${NC} NOT found in PATH" + fi +done +echo "" + +# 14. Secret file check +echo "14. Checking secret file:" +SECRET_FILE="/run/secrets/lottery-config.properties" +if [ -f "$SECRET_FILE" ]; then + echo -e " ${GREEN}✅ Secret file exists${NC}" + if [ -r "$SECRET_FILE" ]; then + echo -e " ${GREEN}✅ Secret file is readable${NC}" + else + echo -e " ${RED}❌ Secret file is NOT readable${NC}" + fi +else + echo -e " ${YELLOW}⚠️ Secret file not found (script will fail at runtime)${NC}" +fi +echo "" + +# Summary +echo "==========================================" +echo "Summary & Recommendations" +echo "==========================================" + +ISSUES=0 + +if [ ! -x "$SCRIPT" ]; then + echo -e "${RED}❌ Issue: File is not executable${NC}" + echo " Fix: chmod +x $SCRIPT" + ISSUES=$((ISSUES + 1)) +fi + +if file "$SCRIPT" | grep -q "CRLF"; then + echo -e "${RED}❌ Issue: Windows line endings detected${NC}" + echo " Fix: dos2unix $SCRIPT (or: sed -i 's/\r$//' $SCRIPT)" + ISSUES=$((ISSUES + 1)) +fi + +if mount | grep -E "(/opt|/app)" | grep -q "noexec"; then + echo -e "${RED}❌ Issue: Filesystem mounted with noexec${NC}" + echo " Fix: Remove noexec from /etc/fstab and remount" + ISSUES=$((ISSUES + 1)) +fi + +if [ "$ISSUES" -eq 0 ]; then + echo -e "${GREEN}✅ No obvious issues found${NC}" + echo "" + echo "If cron still fails, try:" + echo " 1. Update cron to use bash explicitly:" + echo " 0 2 * * * /bin/bash $SCRIPT >> /opt/app/logs/backup.log 2>&1" + echo "" + echo " 2. Check cron logs:" + echo " sudo journalctl -u cron | tail -50" + echo "" + echo " 3. Test manual execution:" + echo " sudo $SCRIPT --keep-local" +else + echo "" + echo -e "${YELLOW}Found $ISSUES issue(s) that need to be fixed.${NC}" +fi + +echo "" +echo "==========================================" + diff --git a/scripts/load-db-password.sh b/scripts/load-db-password.sh new file mode 100644 index 0000000..7b1f6e4 --- /dev/null +++ b/scripts/load-db-password.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Script to load database password from secret file +# This ensures DB_PASSWORD and DB_ROOT_PASSWORD match SPRING_DATASOURCE_PASSWORD +# Usage: source ./load-db-password.sh + +SECRET_FILE="/run/secrets/lottery-config.properties" + +if [ ! -f "$SECRET_FILE" ]; then + echo "❌ Error: Secret file not found at $SECRET_FILE" + echo " Please create the secret file first (see deployment guide Step 3.3)" + return 1 2>/dev/null || exit 1 +fi + +# Read SPRING_DATASOURCE_PASSWORD from secret file +DB_PASSWORD=$(grep "^SPRING_DATASOURCE_PASSWORD=" "$SECRET_FILE" | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + +if [ -z "$DB_PASSWORD" ]; then + echo "❌ Error: SPRING_DATASOURCE_PASSWORD not found in secret file" + echo " Please ensure the secret file contains: SPRING_DATASOURCE_PASSWORD=your_password" + return 1 2>/dev/null || exit 1 +fi + +# Export both variables (MySQL uses both) +export DB_PASSWORD="$DB_PASSWORD" +export DB_ROOT_PASSWORD="$DB_PASSWORD" + +# Optionally load PMA_ABSOLUTE_URI from secret file (for phpMyAdmin path protection) +PMA_ABSOLUTE_URI=$(grep "^PMA_ABSOLUTE_URI=" "$SECRET_FILE" | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/^"//;s/"$//' | sed "s/^'//;s/'$//") +if [ -n "$PMA_ABSOLUTE_URI" ]; then + export PMA_ABSOLUTE_URI="$PMA_ABSOLUTE_URI" + echo "✅ PMA_ABSOLUTE_URI loaded from secret file" +fi + +echo "✅ Database password loaded from secret file" +echo " DB_PASSWORD and DB_ROOT_PASSWORD are now set (matching SPRING_DATASOURCE_PASSWORD)" + + + diff --git a/scripts/restore-database.sh b/scripts/restore-database.sh new file mode 100644 index 0000000..767ca23 --- /dev/null +++ b/scripts/restore-database.sh @@ -0,0 +1,183 @@ +#!/bin/bash +# Database Restore Script for Lottery Application +# This script restores a MySQL database from a backup file +# +# Usage: +# ./scripts/restore-database.sh +# +# Examples: +# # Restore from local file +# ./scripts/restore-database.sh /opt/app/backups/lottery_db_backup_20240101_120000.sql.gz +# +# # Restore from backup VPS +# ./scripts/restore-database.sh 5.45.77.77:/raid/backup/acc_260182/lottery_db_backup_20240101_120000.sql.gz +# +# Prerequisites: +# 1. Database password accessible via /run/secrets/lottery-config.properties +# 2. Docker container 'lottery-mysql' running +# 3. Database will be DROPPED and RECREATED (all data will be lost!) + +set -euo pipefail + +# Configuration +MYSQL_CONTAINER="lottery-mysql" +MYSQL_DATABASE="lottery_db" +SECRET_FILE="/run/secrets/lottery-config.properties" +BACKUP_VPS_USER="acc_260182" # User account on backup VPS + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Logging function +log() { + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" >&2 +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +# Check arguments +if [ $# -eq 0 ]; then + error "No backup file specified" + echo "Usage: $0 " + echo "" + echo "Examples:" + echo " $0 /opt/app/backups/lottery_db_backup_20240101_120000.sql.gz" + echo " $0 5.45.77.77:/raid/backup/acc_260182/lottery_db_backup_20240101_120000.sql.gz" + exit 1 +fi + +BACKUP_SOURCE="$1" + +# Check if running as root or with sudo +if [ "$EUID" -ne 0 ]; then + error "This script must be run as root (or with sudo)" + exit 1 +fi + +# Load database password +if [ ! -f "$SECRET_FILE" ]; then + error "Secret file not found at $SECRET_FILE" + exit 1 +fi + +DB_PASSWORD=$(grep "^SPRING_DATASOURCE_PASSWORD=" "$SECRET_FILE" | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + +if [ -z "$DB_PASSWORD" ]; then + error "SPRING_DATASOURCE_PASSWORD not found in secret file" + exit 1 +fi + +# Check if MySQL container is running +if ! docker ps --format '{{.Names}}' | grep -q "^${MYSQL_CONTAINER}$"; then + error "MySQL container '${MYSQL_CONTAINER}' is not running" + exit 1 +fi + +# Determine if backup is remote or local +TEMP_BACKUP="/tmp/restore_backup_$$" +BACKUP_IS_COMPRESSED=false + +if [[ "$BACKUP_SOURCE" == *":"* ]]; then + # Remote backup (format: host:/path/to/file) + log "Detected remote backup: ${BACKUP_SOURCE}" + HOST_PATH=(${BACKUP_SOURCE//:/ }) + REMOTE_HOST="${HOST_PATH[0]}" + REMOTE_PATH="${HOST_PATH[1]}" + + log "Downloading backup from ${REMOTE_HOST}..." + if scp "${REMOTE_HOST}:${REMOTE_PATH}" "${TEMP_BACKUP}"; then + log "✅ Backup downloaded successfully" + else + error "Failed to download backup from remote VPS" + exit 1 + fi +else + # Local backup + if [ ! -f "$BACKUP_SOURCE" ]; then + error "Backup file not found: ${BACKUP_SOURCE}" + exit 1 + fi + log "Using local backup: ${BACKUP_SOURCE}" + cp "$BACKUP_SOURCE" "${TEMP_BACKUP}" +fi + +# Check if backup is compressed +if [[ "$TEMP_BACKUP" == *.gz ]] || file "$TEMP_BACKUP" | grep -q "gzip compressed"; then + BACKUP_IS_COMPRESSED=true + log "Backup is compressed (gzip)" +fi + +# Get backup file size +BACKUP_SIZE=$(du -h "${TEMP_BACKUP}" | cut -f1) +log "Backup size: ${BACKUP_SIZE}" + +# WARNING: This will destroy all existing data! +warn "⚠️ WARNING: This will DROP and RECREATE the database '${MYSQL_DATABASE}'" +warn "⚠️ ALL EXISTING DATA WILL BE LOST!" +echo "" +read -p "Are you sure you want to continue? Type 'YES' to confirm: " CONFIRM + +if [ "$CONFIRM" != "YES" ]; then + log "Restore cancelled by user" + rm -f "${TEMP_BACKUP}" + exit 0 +fi + +log "Starting database restore..." + +# Drop and recreate database +log "Dropping existing database (if exists)..." +docker exec "${MYSQL_CONTAINER}" mysql -u root -p"${DB_PASSWORD}" -e "DROP DATABASE IF EXISTS ${MYSQL_DATABASE};" || true + +log "Creating fresh database..." +docker exec "${MYSQL_CONTAINER}" mysql -u root -p"${DB_PASSWORD}" -e "CREATE DATABASE ${MYSQL_DATABASE} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + +# Restore database +log "Restoring database from backup..." + +if [ "$BACKUP_IS_COMPRESSED" = true ]; then + # Restore from compressed backup + if gunzip -c "${TEMP_BACKUP}" | docker exec -i "${MYSQL_CONTAINER}" mysql -u root -p"${DB_PASSWORD}" "${MYSQL_DATABASE}"; then + log "✅ Database restored successfully from compressed backup" + else + error "Failed to restore database" + rm -f "${TEMP_BACKUP}" + exit 1 + fi +else + # Restore from uncompressed backup + if docker exec -i "${MYSQL_CONTAINER}" mysql -u root -p"${DB_PASSWORD}" "${MYSQL_DATABASE}" < "${TEMP_BACKUP}"; then + log "✅ Database restored successfully" + else + error "Failed to restore database" + rm -f "${TEMP_BACKUP}" + exit 1 + fi +fi + +# Clean up temporary file +rm -f "${TEMP_BACKUP}" + +# Verify restore +log "Verifying restore..." +TABLE_COUNT=$(docker exec "${MYSQL_CONTAINER}" mysql -u root -p"${DB_PASSWORD}" -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${MYSQL_DATABASE}';" 2>/dev/null || echo "0") + +if [ "$TABLE_COUNT" -gt 0 ]; then + log "✅ Restore verified: ${TABLE_COUNT} tables found in database" +else + warn "⚠️ Warning: No tables found in database after restore" +fi + +log "✅ Database restore completed!" +warn "⚠️ Remember to restart the backend container if it's running:" +warn " docker restart lottery-backend" + diff --git a/scripts/rolling-update.sh b/scripts/rolling-update.sh new file mode 100644 index 0000000..8ef547b --- /dev/null +++ b/scripts/rolling-update.sh @@ -0,0 +1,628 @@ +#!/bin/bash +# Rolling Update Deployment Script +# This script performs zero-downtime deployment by: +# 1. Building new backend image +# 2. Starting new backend container on port 8082 +# 3. Health checking the new container +# 4. Updating Nginx to point to new container +# 5. Reloading Nginx (zero downtime) +# 6. Stopping old container after grace period + +set -euo pipefail + +# Colors (define early for use in config detection) +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Logging functions (define early) +log() { + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" >&2 +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +# Configuration +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPOSE_FILE="${PROJECT_DIR}/docker-compose.prod.yml" + +# Detect Nginx config file (try common locations) +# Priority: sites-enabled (what Nginx actually loads) > conf.d > custom paths +NGINX_CONF="${NGINX_CONF:-}" +if [ -z "$NGINX_CONF" ]; then + if [ -f "/etc/nginx/sites-enabled/win-spin.live" ]; then + NGINX_CONF="/etc/nginx/sites-enabled/win-spin.live" + log "Using Nginx config: $NGINX_CONF (sites-enabled - active config)" + elif [ -f "/etc/nginx/sites-enabled/win-spin.live.conf" ]; then + NGINX_CONF="/etc/nginx/sites-enabled/win-spin.live.conf" + log "Using Nginx config: $NGINX_CONF (sites-enabled - active config)" + elif [ -f "/etc/nginx/conf.d/lottery.conf" ]; then + NGINX_CONF="/etc/nginx/conf.d/lottery.conf" + log "Using Nginx config: $NGINX_CONF (conf.d)" + elif [ -f "/opt/app/nginx/win-spin.live.conf" ]; then + warn "Found config at /opt/app/nginx/win-spin.live.conf" + warn "Checking if it's symlinked to /etc/nginx/sites-enabled/..." + if [ -L "/etc/nginx/sites-enabled/win-spin.live" ] || [ -L "/etc/nginx/sites-enabled/win-spin.live.conf" ]; then + # Find the actual target + local target=$(readlink -f /etc/nginx/sites-enabled/win-spin.live 2>/dev/null || readlink -f /etc/nginx/sites-enabled/win-spin.live.conf 2>/dev/null) + if [ -n "$target" ]; then + NGINX_CONF="$target" + log "Using Nginx config: $NGINX_CONF (symlink target)" + else + NGINX_CONF="/opt/app/nginx/win-spin.live.conf" + warn "Using custom path - will update this file, but you may need to copy to sites-enabled" + fi + else + NGINX_CONF="/opt/app/nginx/win-spin.live.conf" + warn "Using custom path - will update this file, but you may need to copy to sites-enabled" + fi + else + error "Cannot find Nginx config file." + error "Searched:" + error " - /etc/nginx/sites-enabled/win-spin.live" + error " - /etc/nginx/sites-enabled/win-spin.live.conf" + error " - /etc/nginx/conf.d/lottery.conf" + error " - /opt/app/nginx/win-spin.live.conf" + error "" + error "Please set NGINX_CONF environment variable with the correct path." + exit 1 + fi +else + log "Using Nginx config: $NGINX_CONF (from NGINX_CONF environment variable)" +fi + +# Create backup in /tmp to avoid nginx including it (sites-enabled/* includes all files) +NGINX_CONF_BACKUP="/tmp/nginx-backup-$(basename $NGINX_CONF).$(date +%Y%m%d_%H%M%S)" + +# Ports for backends (will be swapped dynamically) +PRIMARY_PORT=8080 +STANDBY_PORT=8082 + +# Detect which backend is currently active +detect_active_backend() { + # Check which port Nginx is currently using in upstream block + # Look for server line that is NOT marked as backup + local active_port_line=$(grep -A 10 "^upstream backend {" "$NGINX_CONF" | grep "server 127\.0\.0\.1:" | grep -v "backup" | head -1) + + if echo "$active_port_line" | grep -q "127\.0\.0\.1:8082"; then + # Port 8082 is active (not backup) + ACTIVE_PORT=8082 + STANDBY_PORT=8080 + ACTIVE_CONTAINER="lottery-backend-new" + STANDBY_CONTAINER="lottery-backend" + log "Detected: Port 8082 is currently active" + else + # Port 8080 is active (default or only one present) + ACTIVE_PORT=8080 + STANDBY_PORT=8082 + ACTIVE_CONTAINER="lottery-backend" + STANDBY_CONTAINER="lottery-backend-new" + log "Detected: Port 8080 is currently active" + fi + + PRIMARY_PORT=$ACTIVE_PORT + HEALTH_CHECK_URL="http://127.0.0.1:${STANDBY_PORT}/actuator/health/readiness" +} + +HEALTH_CHECK_RETRIES=60 # Increased for Spring Boot startup (60 * 2s = 120s max) +HEALTH_CHECK_INTERVAL=2 +GRACE_PERIOD=10 + +# Check for KEEP_FAILED_CONTAINER environment variable (preserve it for rollback) +# This allows keeping failed containers for debugging even when using sudo +if [ "${KEEP_FAILED_CONTAINER:-}" = "true" ]; then + SCRIPT_KEEP_FAILED_CONTAINER="true" + export SCRIPT_KEEP_FAILED_CONTAINER + log "KEEP_FAILED_CONTAINER=true - failed containers will be kept for debugging" +fi + +# Detect docker compose command (newer Docker uses 'docker compose', older uses 'docker-compose') +DOCKER_COMPOSE_CMD="" +if docker compose version &> /dev/null; then + DOCKER_COMPOSE_CMD="docker compose" +elif command -v docker-compose &> /dev/null; then + DOCKER_COMPOSE_CMD="docker-compose" +else + error "Neither 'docker compose' nor 'docker-compose' is available" + exit 1 +fi + +# Check prerequisites +check_prerequisites() { + log "Checking prerequisites..." + + # Check if running as root + if [ "$EUID" -ne 0 ]; then + error "This script must be run as root (or with sudo)" + exit 1 + fi + + # Check if docker compose is available (already detected above) + log "Using Docker Compose command: $DOCKER_COMPOSE_CMD" + + # Check if Nginx config exists + if [ ! -f "$NGINX_CONF" ]; then + error "Nginx config not found at $NGINX_CONF" + exit 1 + fi + + # Check if DB_ROOT_PASSWORD is set + if [ -z "${DB_ROOT_PASSWORD:-}" ]; then + warn "DB_ROOT_PASSWORD not set, attempting to load from secret file..." + if [ -f "${SCRIPT_DIR}/load-db-password.sh" ]; then + source "${SCRIPT_DIR}/load-db-password.sh" + else + error "Cannot load DB_ROOT_PASSWORD. Please set it or run: source scripts/load-db-password.sh" + exit 1 + fi + fi + + # Detect which backend is currently active + detect_active_backend + + # Check if active backend is running + if ! docker ps --format '{{.Names}}' | grep -q "^${ACTIVE_CONTAINER}$"; then + error "Active backend container (${ACTIVE_CONTAINER}) is not running" + error "Please start it first: docker-compose -f ${COMPOSE_FILE} up -d backend" + exit 1 + fi + + log "✅ Prerequisites check passed" + log "Active backend: ${ACTIVE_CONTAINER} on port ${ACTIVE_PORT}" + log "New backend will use: ${STANDBY_CONTAINER} on port ${STANDBY_PORT}" +} + +# Build new backend image +build_new_image() { + log "Building new backend image..." + + cd "$PROJECT_DIR" + + # Determine which service to build based on which container will be used + # Both services use the same Dockerfile, but we need to build the correct one + # to ensure the image cache is updated for the service that will be started + if [ "$STANDBY_PORT" = "8082" ]; then + SERVICE_TO_BUILD="backend-new" + else + SERVICE_TO_BUILD="backend" + fi + + log "Building service: ${SERVICE_TO_BUILD} (for port ${STANDBY_PORT})..." + + # Build the image for the service that will be used + # This ensures the correct service's image cache is updated with latest migrations + if [ "$SERVICE_TO_BUILD" = "backend-new" ]; then + if $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update build "$SERVICE_TO_BUILD" 2>&1 | tee /tmp/rolling-update-build.log; then + log "✅ New backend image built successfully" + else + error "Failed to build new backend image" + exit 1 + fi + else + if $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" build "$SERVICE_TO_BUILD" 2>&1 | tee /tmp/rolling-update-build.log; then + log "✅ New backend image built successfully" + else + error "Failed to build new backend image" + exit 1 + fi + fi +} + +# Start new backend container +start_new_container() { + log "Starting new backend container on port ${STANDBY_PORT}..." + + cd "$PROJECT_DIR" + + # Determine which service to start based on standby port + if [ "$STANDBY_PORT" = "8082" ]; then + SERVICE_NAME="backend-new" + CONTAINER_NAME="lottery-backend-new" + else + SERVICE_NAME="backend" + CONTAINER_NAME="lottery-backend" + fi + + # Check if standby container exists (running or stopped) + # We need to remove it to ensure a fresh start with migrations + if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + warn "${CONTAINER_NAME} container is already running, stopping it first..." + else + warn "${CONTAINER_NAME} container exists but is stopped, removing it for fresh start..." + fi + if [ "$SERVICE_NAME" = "backend-new" ]; then + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update stop "$SERVICE_NAME" || true + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update rm -f "$SERVICE_NAME" || true + else + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" stop "$SERVICE_NAME" || true + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" rm -f "$SERVICE_NAME" || true + fi + fi + + # Start the new container + if [ "$SERVICE_NAME" = "backend-new" ]; then + if $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update up -d "$SERVICE_NAME"; then + log "✅ New backend container started" + else + error "Failed to start new backend container" + exit 1 + fi + else + if $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" up -d "$SERVICE_NAME"; then + log "✅ New backend container started" + else + error "Failed to start new backend container" + exit 1 + fi + fi + + # Wait for container to initialize (Spring Boot needs time to start) + log "Waiting for container to initialize (Spring Boot startup can take 60+ seconds)..." + sleep 10 + + # Check if container is still running (might have crashed) + if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + error "Container ${CONTAINER_NAME} stopped immediately after start. Check logs:" + error " docker logs ${CONTAINER_NAME}" + exit 1 + fi +} + +# Health check new container +health_check_new_container() { + log "Performing health check on new backend container (port ${STANDBY_PORT})..." + + # First, check if container is still running + if [ "$STANDBY_PORT" = "8082" ]; then + local container_name="lottery-backend-new" + else + local container_name="lottery-backend" + fi + + if ! docker ps --format '{{.Names}}' | grep -q "^${container_name}$"; then + error "Container ${container_name} is not running!" + error "Check logs: docker logs ${container_name}" + return 1 + fi + + # Check container health status + local health_status=$(docker inspect --format='{{.State.Health.Status}}' "${container_name}" 2>/dev/null || echo "none") + if [ "$health_status" != "none" ]; then + info "Container health status: $health_status" + fi + + local retries=0 + while [ $retries -lt $HEALTH_CHECK_RETRIES ]; do + # Check if container is still running + if ! docker ps --format '{{.Names}}' | grep -q "^${container_name}$"; then + error "Container ${container_name} stopped during health check!" + error "Check logs: docker logs ${container_name}" + return 1 + fi + + # Try health check + if curl -sf "$HEALTH_CHECK_URL" > /dev/null 2>&1; then + log "✅ New backend container is healthy" + return 0 + fi + + retries=$((retries + 1)) + if [ $retries -lt $HEALTH_CHECK_RETRIES ]; then + # Show container status every 5 attempts + if [ $((retries % 5)) -eq 0 ]; then + info "Health check failed (attempt $retries/$HEALTH_CHECK_RETRIES)" + info "Container status: $(docker ps --filter name=${container_name} --format '{{.Status}}')" + info "Last 5 log lines:" + docker logs --tail 5 "${container_name}" 2>&1 | sed 's/^/ /' + else + info "Health check failed (attempt $retries/$HEALTH_CHECK_RETRIES), retrying in ${HEALTH_CHECK_INTERVAL}s..." + fi + sleep $HEALTH_CHECK_INTERVAL + fi + done + + error "Health check failed after $HEALTH_CHECK_RETRIES attempts" + error "New backend container is not responding at $HEALTH_CHECK_URL" + error "" + error "Container status:" + docker ps --filter name=${container_name} --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' || true + error "" + error "Last 200 log lines:" + docker logs --tail 200 "${container_name}" 2>&1 | sed 's/^/ /' + error "" + error "To debug, keep container running and check:" + error " docker logs -f ${container_name}" + error " docker logs --tail 500 ${container_name} # For even more logs" + error " curl -v $HEALTH_CHECK_URL" + return 1 +} + +# Update Nginx configuration +update_nginx_config() { + log "Updating Nginx configuration to point to new backend (port ${STANDBY_PORT})..." + + # Backup current config + cp "$NGINX_CONF" "$NGINX_CONF_BACKUP" + log "Backed up Nginx config to: $NGINX_CONF_BACKUP" + + # Use Python for reliable config manipulation + # Pass variables directly to Python (not via sys.argv) + python3 << PYTHON_SCRIPT +import re +import sys + +config_file = "$NGINX_CONF" +standby_port = "$STANDBY_PORT" +active_port = "$ACTIVE_PORT" + +try: + # Read the entire file + with open(config_file, 'r') as f: + lines = f.readlines() + + # Find and update upstream block + new_lines = [] + in_upstream = False + upstream_start_idx = -1 + upstream_end_idx = -1 + keepalive_line = None + keepalive_idx = -1 + + # First pass: find upstream block boundaries + for i, line in enumerate(lines): + if re.match(r'^\s*upstream\s+backend\s*\{', line): + upstream_start_idx = i + in_upstream = True + elif in_upstream and re.match(r'^\s*\}', line): + upstream_end_idx = i + break + elif in_upstream and re.search(r'keepalive', line): + keepalive_line = line + keepalive_idx = i + + if upstream_start_idx == -1 or upstream_end_idx == -1: + raise Exception("Could not find upstream backend block") + + # Build new lines + for i, line in enumerate(lines): + if i < upstream_start_idx: + # Before upstream block - keep as is + new_lines.append(line) + elif i == upstream_start_idx: + # Start of upstream block + new_lines.append(line) + elif i > upstream_start_idx and i < upstream_end_idx: + # Inside upstream block + # Skip old server lines + if re.search(r'server\s+127\.0\.0\.1:808[02]', line): + continue + # Skip keepalive (we'll add it at the end) + if re.search(r'keepalive', line): + continue + # Keep comments and other lines + new_lines.append(line) + elif i == upstream_end_idx: + # Before closing brace - add server lines and keepalive + new_lines.append(f" server 127.0.0.1:{standby_port};\n") + new_lines.append(f" server 127.0.0.1:{active_port} backup;\n") + if keepalive_line: + new_lines.append(keepalive_line) + else: + new_lines.append(" keepalive 200;\n") + new_lines.append(line) + else: + # After upstream block - keep as is + new_lines.append(line) + + # Write updated config + with open(config_file, 'w') as f: + f.writelines(new_lines) + + print("Nginx config updated successfully") + +except Exception as e: + print(f"Error updating Nginx config: {e}", file=sys.stderr) + import traceback + traceback.print_exc() + sys.exit(1) +PYTHON_SCRIPT + + if [ $? -ne 0 ]; then + error "Failed to update Nginx config" + cp "$NGINX_CONF_BACKUP" "$NGINX_CONF" + exit 1 + fi + + # Test Nginx configuration + if nginx -t; then + log "✅ Nginx configuration is valid" + else + error "Nginx configuration test failed, restoring backup..." + error "Error details:" + nginx -t 2>&1 | sed 's/^/ /' + error "" + error "Current config (first 50 lines):" + head -50 "$NGINX_CONF" | sed 's/^/ /' + cp "$NGINX_CONF_BACKUP" "$NGINX_CONF" + exit 1 + fi +} + +# Reload Nginx (zero downtime) +reload_nginx() { + log "Reloading Nginx (zero downtime)..." + + if systemctl reload nginx; then + log "✅ Nginx reloaded successfully" + log "✅ Traffic is now being served by new backend (port 8082)" + else + error "Failed to reload Nginx, restoring backup config..." + cp "$NGINX_CONF_BACKUP" "$NGINX_CONF" + systemctl reload nginx + exit 1 + fi +} + +# Stop old container after grace period +stop_old_container() { + log "Waiting ${GRACE_PERIOD}s grace period for active connections to finish..." + sleep $GRACE_PERIOD + + log "Stopping old backend container (${ACTIVE_CONTAINER})..." + + cd "$PROJECT_DIR" + + if [ "$ACTIVE_CONTAINER" = "lottery-backend-new" ]; then + if $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update stop backend-new; then + log "✅ Old backend container stopped" + else + warn "Failed to stop old backend container gracefully" + fi + else + if $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" stop backend; then + log "✅ Old backend container stopped" + else + warn "Failed to stop old backend container gracefully" + fi + fi + + # Optionally remove the old container (comment out if you want to keep it for rollback) + # if [ "$ACTIVE_CONTAINER" = "lottery-backend-new" ]; then + # docker-compose -f "$COMPOSE_FILE" --profile rolling-update rm -f backend-new + # else + # docker-compose -f "$COMPOSE_FILE" rm -f backend + # fi +} + +# Rollback function +rollback() { + error "Rolling back to previous version..." + + # Check KEEP_FAILED_CONTAINER (check both current env and script-level variable) + local keep_container="${KEEP_FAILED_CONTAINER:-false}" + if [ "$keep_container" != "true" ] && [ "${SCRIPT_KEEP_FAILED_CONTAINER:-false}" = "true" ]; then + keep_container="true" + fi + + # Restore Nginx config + if [ -f "$NGINX_CONF_BACKUP" ]; then + cp "$NGINX_CONF_BACKUP" "$NGINX_CONF" + systemctl reload nginx + log "✅ Nginx config restored" + fi + + # Stop new container (but keep it for debugging if KEEP_FAILED_CONTAINER is set) + cd "$PROJECT_DIR" + if [ "$keep_container" = "true" ]; then + warn "" + warn "═══════════════════════════════════════════════════════════════" + warn "KEEP_FAILED_CONTAINER=true - Container will be KEPT for debugging" + warn "═══════════════════════════════════════════════════════════════" + if [ "$STANDBY_PORT" = "8082" ]; then + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update stop backend-new || true + warn "" + warn "Container 'lottery-backend-new' is STOPPED but NOT REMOVED" + warn "" + warn "To check logs:" + warn " docker logs lottery-backend-new" + warn " docker logs --tail 100 lottery-backend-new" + warn "" + warn "To remove manually:" + warn " $DOCKER_COMPOSE_CMD -f $COMPOSE_FILE --profile rolling-update rm -f backend-new" + else + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" stop backend || true + warn "" + warn "Container 'lottery-backend' is STOPPED but NOT REMOVED" + warn "" + warn "To check logs:" + warn " docker logs lottery-backend" + warn " docker logs --tail 100 lottery-backend" + warn "" + warn "To remove manually:" + warn " $DOCKER_COMPOSE_CMD -f $COMPOSE_FILE rm -f backend" + fi + warn "═══════════════════════════════════════════════════════════════" + else + if [ "$STANDBY_PORT" = "8082" ]; then + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update stop backend-new || true + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update rm -f backend-new || true + else + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" stop backend || true + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" rm -f backend || true + fi + fi + + # Start old container if it was stopped + if ! docker ps --format '{{.Names}}' | grep -q "^${ACTIVE_CONTAINER}$"; then + if [ "$ACTIVE_CONTAINER" = "lottery-backend-new" ]; then + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update start backend-new || \ + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" --profile rolling-update up -d backend-new + else + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" start backend || \ + $DOCKER_COMPOSE_CMD -f "$COMPOSE_FILE" up -d backend + fi + fi + + error "Rollback completed" + exit 1 +} + +# Main deployment flow +main() { + log "Starting rolling update deployment..." + + # Trap errors for rollback + trap rollback ERR + + check_prerequisites + build_new_image + start_new_container + + if ! health_check_new_container; then + rollback + fi + + update_nginx_config + reload_nginx + + # Clear error trap after successful switch + trap - ERR + + stop_old_container + + log "✅ Rolling update completed successfully!" + log "" + log "Summary:" + log " - New backend is running on port ${STANDBY_PORT} (${STANDBY_CONTAINER})" + log " - Nginx is serving traffic from new backend" + log " - Old backend (${ACTIVE_CONTAINER}) has been stopped" + log "" + log "To rollback (if needed):" + log " 1. Restore Nginx config: cp $NGINX_CONF_BACKUP $NGINX_CONF" + log " 2. Reload Nginx: systemctl reload nginx" + if [ "$ACTIVE_CONTAINER" = "lottery-backend-new" ]; then + log " 3. Start old backend: docker-compose -f $COMPOSE_FILE --profile rolling-update start backend-new" + log " 4. Stop new backend: docker-compose -f $COMPOSE_FILE stop backend" + else + log " 3. Start old backend: docker-compose -f $COMPOSE_FILE start backend" + log " 4. Stop new backend: docker-compose -f $COMPOSE_FILE --profile rolling-update stop backend-new" + fi +} + +# Run main function +main "$@" + diff --git a/scripts/setup-logging.sh b/scripts/setup-logging.sh new file mode 100644 index 0000000..0e5b021 --- /dev/null +++ b/scripts/setup-logging.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# Setup script for external logback-spring.xml on VPS +# This script extracts logback-spring.xml from the JAR and places it in the config directory +# MUST be run before starting Docker containers to create the required files + +set -e + +# Determine config directory based on current location +if [ -d "/opt/app/backend" ]; then + CONFIG_DIR="/opt/app/backend/config" + LOG_DIR="/opt/app/logs" +elif [ -d "/opt/app/backend/lottery-be" ]; then + CONFIG_DIR="/opt/app/backend/lottery-be/config" + LOG_DIR="/opt/app/logs" +else + # Try to find from current directory + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + BACKEND_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + CONFIG_DIR="$BACKEND_DIR/config" + LOG_DIR="/opt/app/logs" +fi + +echo "Setting up external logging configuration..." +echo "Config directory: $CONFIG_DIR" +echo "Log directory: $LOG_DIR" + +# Create config directory if it doesn't exist +mkdir -p "$CONFIG_DIR" +chmod 755 "$CONFIG_DIR" + +# Create log directory if it doesn't exist +mkdir -p "$LOG_DIR" +chmod 755 "$LOG_DIR" + +# Extract logback-spring.xml from JAR if it doesn't exist +if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then + echo "Extracting logback-spring.xml from JAR..." + + # Try multiple locations for JAR file + JAR_PATH="" + for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")" "$(dirname "$(dirname "$CONFIG_DIR")")"; do + if [ -d "$search_path" ]; then + found_jar=$(find "$search_path" -name "lottery-be-*.jar" -type f 2>/dev/null | head -n 1) + if [ -n "$found_jar" ]; then + JAR_PATH="$found_jar" + break + fi + fi + done + + # Try to find in target directory + if [ -z "$JAR_PATH" ]; then + for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")"; do + if [ -d "$search_path/target" ]; then + found_jar=$(find "$search_path/target" -name "*.jar" -type f | head -n 1) + if [ -n "$found_jar" ]; then + JAR_PATH="$found_jar" + break + fi + fi + done + fi + + if [ -z "$JAR_PATH" ]; then + echo "Warning: JAR file not found. Trying to copy from source..." + # If JAR not found, copy from source (if available) + for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")"; do + if [ -f "$search_path/src/main/resources/logback-spring.xml" ]; then + cp "$search_path/src/main/resources/logback-spring.xml" "$CONFIG_DIR/logback-spring.xml" + echo "Copied from source: $search_path/src/main/resources/logback-spring.xml" + break + fi + done + + if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then + echo "Error: Cannot find logback-spring.xml in JAR or source." + echo "Please ensure the file exists or copy it manually to: $CONFIG_DIR/logback-spring.xml" + exit 1 + fi + else + echo "Found JAR: $JAR_PATH" + # Extract from JAR + unzip -p "$JAR_PATH" BOOT-INF/classes/logback-spring.xml > "$CONFIG_DIR/logback-spring.xml" 2>/dev/null || \ + unzip -p "$JAR_PATH" logback-spring.xml > "$CONFIG_DIR/logback-spring.xml" 2>/dev/null || { + echo "Warning: Could not extract from JAR. Trying to copy from source..." + # Try copying from source + for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")"; do + if [ -f "$search_path/src/main/resources/logback-spring.xml" ]; then + cp "$search_path/src/main/resources/logback-spring.xml" "$CONFIG_DIR/logback-spring.xml" + break + fi + done + + if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then + echo "Error: Cannot extract or find logback-spring.xml." + echo "Please copy it manually to: $CONFIG_DIR/logback-spring.xml" + exit 1 + fi + } + echo "Extracted from JAR: $JAR_PATH" + fi + + echo "logback-spring.xml created at $CONFIG_DIR/logback-spring.xml" +else + echo "logback-spring.xml already exists at $CONFIG_DIR/logback-spring.xml" +fi + +# Set proper permissions +chmod 644 "$CONFIG_DIR/logback-spring.xml" +chown $USER:$USER "$CONFIG_DIR/logback-spring.xml" 2>/dev/null || true + +echo "Logging configuration setup complete!" +echo "" +echo "Configuration file: $CONFIG_DIR/logback-spring.xml" +echo "Log directory: $LOG_DIR" +echo "" +echo "You can now edit $CONFIG_DIR/logback-spring.xml to change log levels at runtime." +echo "Changes will take effect within 30 seconds (no restart needed)." + diff --git a/src/main/java/com/honey/honey/config/TelegramProperties.java b/src/main/java/com/honey/honey/config/TelegramProperties.java deleted file mode 100644 index b206aab..0000000 --- a/src/main/java/com/honey/honey/config/TelegramProperties.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.honey.honey.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConfigurationProperties(prefix = "telegram") -@Data -public class TelegramProperties { - private String botToken; -} - diff --git a/src/main/java/com/honey/honey/config/WebConfig.java b/src/main/java/com/honey/honey/config/WebConfig.java deleted file mode 100644 index 4cb9441..0000000 --- a/src/main/java/com/honey/honey/config/WebConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.honey.honey.config; - -import com.honey.honey.security.AuthInterceptor; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -@RequiredArgsConstructor -public class WebConfig implements WebMvcConfigurer { - - private final AuthInterceptor authInterceptor; - - @Override - public void addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry registry) { - registry.addInterceptor(authInterceptor) - .excludePathPatterns( - "/ping", - "/actuator/**", - "/api/auth/tma/session" // Session creation endpoint doesn't require auth - ); - } -} - diff --git a/src/main/java/com/honey/honey/controller/UserController.java b/src/main/java/com/honey/honey/controller/UserController.java deleted file mode 100644 index 5ff1150..0000000 --- a/src/main/java/com/honey/honey/controller/UserController.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.honey.honey.controller; - -import com.honey.honey.dto.UserDto; -import com.honey.honey.model.UserA; -import com.honey.honey.security.UserContext; -import com.honey.honey.service.UserService; -import com.honey.honey.util.IpUtils; -import lombok.Data; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; - -@Slf4j -@RestController -@RequestMapping("/api/users") -@RequiredArgsConstructor -public class UserController { - - private final UserService userService; - - @GetMapping("/current") - public UserDto getCurrentUser() { - UserA user = UserContext.get(); - - // Convert IP from byte[] to string for display - String ipAddress = IpUtils.bytesToIp(user.getIp()); - - return UserDto.builder() - .telegram_id(user.getTelegramId()) - .username(user.getTelegramName()) - .ip(ipAddress) - .build(); - } - - /** - * Updates user's language code. - * Called when user changes language in app header. - */ - @PutMapping("/language") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void updateLanguage(@RequestBody UpdateLanguageRequest request) { - UserA user = UserContext.get(); - userService.updateLanguageCode(user.getId(), request.getLanguageCode()); - } - - @Data - public static class UpdateLanguageRequest { - private String languageCode; - } -} diff --git a/src/main/java/com/honey/honey/exception/GlobalExceptionHandler.java b/src/main/java/com/honey/honey/exception/GlobalExceptionHandler.java deleted file mode 100644 index 4ca1f24..0000000 --- a/src/main/java/com/honey/honey/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.honey.honey.exception; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -@Slf4j -@RestControllerAdvice -public class GlobalExceptionHandler { - - @ExceptionHandler(UnauthorizedException.class) - public ResponseEntity handleUnauthorized(UnauthorizedException ex) { - log.warn("Unauthorized: {}", ex.getMessage()); - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(new ErrorResponse("UNAUTHORIZED", ex.getMessage())); - } - - @ExceptionHandler(Exception.class) - public ResponseEntity handleGeneric(Exception ex) { - log.error("Unexpected error", ex); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred")); - } -} - diff --git a/src/main/java/com/honey/honey/health/DatabaseHealthIndicator.java b/src/main/java/com/honey/honey/health/DatabaseHealthIndicator.java deleted file mode 100644 index f4def52..0000000 --- a/src/main/java/com/honey/honey/health/DatabaseHealthIndicator.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.honey.honey.health; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.stereotype.Component; - -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.SQLException; - -@Slf4j -@Component -@RequiredArgsConstructor -public class DatabaseHealthIndicator implements HealthIndicator { - - private final DataSource dataSource; - - @Override - public Health health() { - try (Connection connection = dataSource.getConnection()) { - if (connection.isValid(1)) { - return Health.up() - .withDetail("database", "MySQL") - .withDetail("status", "Connected") - .build(); - } - } catch (SQLException e) { - log.error("Database health check failed", e); - return Health.down() - .withDetail("database", "MySQL") - .withDetail("error", e.getMessage()) - .build(); - } - return Health.down().withDetail("database", "MySQL").build(); - } -} - diff --git a/src/main/java/com/honey/honey/logging/GrafanaLoggingConfig.java b/src/main/java/com/honey/honey/logging/GrafanaLoggingConfig.java deleted file mode 100644 index 85b21c0..0000000 --- a/src/main/java/com/honey/honey/logging/GrafanaLoggingConfig.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.honey.honey.logging; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Configuration; - -import jakarta.annotation.PostConstruct; - -/** - * Configuration for Grafana integration. - * This class prepares the logging infrastructure for Grafana. - * - * In production (Inferno), logs will be sent to Grafana via: - * - Loki (log aggregation) - * - Prometheus (metrics) - * - * For now, this is a placeholder that ensures structured logging - * is ready for Grafana integration. - */ -@Slf4j -@Configuration -public class GrafanaLoggingConfig { - - @PostConstruct - public void init() { - log.info("📊 Grafana logging configuration initialized"); - log.info("📊 Logs are structured and ready for Grafana/Loki integration"); - log.info("📊 Metrics will be available for Prometheus when configured"); - } - - /** - * Log structured data for Grafana. - * This method can be used to send custom logs to Grafana/Loki. - * - * @param level Log level (INFO, WARN, ERROR, etc.) - * @param message Log message - * @param metadata Additional metadata as key-value pairs - */ - public static void logToGrafana(String level, String message, java.util.Map metadata) { - // For now, just use standard logging - // In production, this will send logs to Grafana/Loki - switch (level.toUpperCase()) { - case "ERROR": - log.error("{} | Metadata: {}", message, metadata); - break; - case "WARN": - log.warn("{} | Metadata: {}", message, metadata); - break; - case "INFO": - default: - log.info("{} | Metadata: {}", message, metadata); - break; - } - } -} - diff --git a/src/main/java/com/honey/honey/repository/UserARepository.java b/src/main/java/com/honey/honey/repository/UserARepository.java deleted file mode 100644 index 0e72c55..0000000 --- a/src/main/java/com/honey/honey/repository/UserARepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.honey.honey.repository; - -import com.honey.honey.model.UserA; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface UserARepository extends JpaRepository { - Optional findByTelegramId(Long telegramId); -} - diff --git a/src/main/java/com/honey/honey/repository/UserBRepository.java b/src/main/java/com/honey/honey/repository/UserBRepository.java deleted file mode 100644 index 85482be..0000000 --- a/src/main/java/com/honey/honey/repository/UserBRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.honey.honey.repository; - -import com.honey.honey.model.UserB; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface UserBRepository extends JpaRepository { -} - diff --git a/src/main/java/com/honey/honey/repository/UserDRepository.java b/src/main/java/com/honey/honey/repository/UserDRepository.java deleted file mode 100644 index d726f60..0000000 --- a/src/main/java/com/honey/honey/repository/UserDRepository.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.honey.honey.repository; - -import com.honey.honey.model.UserD; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -@Repository -public interface UserDRepository extends JpaRepository { - - /** - * Increments referals_1 for a user. - */ - @Modifying - @Query("UPDATE UserD u SET u.referals1 = u.referals1 + 1 WHERE u.id = :userId") - void incrementReferals1(@Param("userId") Integer userId); - - /** - * Increments referals_2 for a user. - */ - @Modifying - @Query("UPDATE UserD u SET u.referals2 = u.referals2 + 1 WHERE u.id = :userId") - void incrementReferals2(@Param("userId") Integer userId); - - /** - * Increments referals_3 for a user. - */ - @Modifying - @Query("UPDATE UserD u SET u.referals3 = u.referals3 + 1 WHERE u.id = :userId") - void incrementReferals3(@Param("userId") Integer userId); - - /** - * Increments referals_4 for a user. - */ - @Modifying - @Query("UPDATE UserD u SET u.referals4 = u.referals4 + 1 WHERE u.id = :userId") - void incrementReferals4(@Param("userId") Integer userId); - - /** - * Increments referals_5 for a user. - */ - @Modifying - @Query("UPDATE UserD u SET u.referals5 = u.referals5 + 1 WHERE u.id = :userId") - void incrementReferals5(@Param("userId") Integer userId); -} - diff --git a/src/main/java/com/honey/honey/service/UserService.java b/src/main/java/com/honey/honey/service/UserService.java deleted file mode 100644 index ee5969c..0000000 --- a/src/main/java/com/honey/honey/service/UserService.java +++ /dev/null @@ -1,317 +0,0 @@ -package com.honey.honey.service; - -import com.honey.honey.model.UserA; -import com.honey.honey.model.UserB; -import com.honey.honey.model.UserD; -import com.honey.honey.repository.UserARepository; -import com.honey.honey.repository.UserBRepository; -import com.honey.honey.repository.UserDRepository; -import com.honey.honey.util.IpUtils; -import com.honey.honey.util.TimeProvider; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Map; -import java.util.Optional; - -/** - * Service for user management with sharded tables. - * Handles registration, login, and referral system. - */ -@Slf4j -@Service -@RequiredArgsConstructor -public class UserService { - - private final UserARepository userARepository; - private final UserBRepository userBRepository; - private final UserDRepository userDRepository; - private final CountryCodeService countryCodeService; - - /** - * Gets or creates a user based on Telegram initData. - * Updates user data on each login. - * Handles referral system if start parameter is present. - * - * @param tgUserData Parsed Telegram data from initData (contains "user" map and "start" string) - * @param request HTTP request for IP extraction - * @return UserA entity - */ - @Transactional - public UserA getOrCreateUser(Map tgUserData, HttpServletRequest request) { - // Extract user data and start parameter (from URL: /honey?start=774876) - @SuppressWarnings("unchecked") - Map tgUser = (Map) tgUserData.get("user"); - String start = (String) tgUserData.get("start"); - Long telegramId = ((Number) tgUser.get("id")).longValue(); - String firstName = (String) tgUser.get("first_name"); - String lastName = (String) tgUser.get("last_name"); - String username = (String) tgUser.get("username"); - Boolean isPremium = (Boolean) tgUser.get("is_premium"); - String languageCode = (String) tgUser.get("language_code"); - - // Build screen_name from first_name and last_name - String screenName = buildScreenName(firstName, lastName); - - // device_code should be language_code from initData (uppercase) - String deviceCode = languageCode != null ? languageCode.toUpperCase() : "XX"; - - // Get client IP and convert to bytes - String clientIp = IpUtils.getClientIp(request); - byte[] ipBytes = IpUtils.ipToBytes(clientIp); - - // Get country code from IP - String countryCode = countryCodeService.getCountryCode(clientIp); - - // Get current timestamp - long nowSeconds = TimeProvider.nowSeconds(); - - // Check if user exists - Optional existingUserOpt = userARepository.findByTelegramId(telegramId); - - if (existingUserOpt.isPresent()) { - // User exists - update login data - UserA userA = existingUserOpt.get(); - updateUserOnLogin(userA, screenName, username, isPremium, languageCode, countryCode, deviceCode, ipBytes, nowSeconds); - return userA; - } else { - // New user - create in all 3 tables - return createNewUser(telegramId, screenName, username, isPremium, languageCode, countryCode, deviceCode, ipBytes, nowSeconds, start); - } - } - - /** - * Updates user data on login (when session is created). - * Note: language_code is NOT updated here - it should be updated via separate endpoint when user changes language in app. - */ - private void updateUserOnLogin(UserA userA, String screenName, String username, Boolean isPremium, - String languageCode, String countryCode, String deviceCode, - byte[] ipBytes, long nowSeconds) { - userA.setScreenName(screenName); - userA.setTelegramName(username != null ? username : "-"); - userA.setIsPremium(isPremium != null && isPremium ? 1 : 0); - userA.setCountryCode(countryCode); - userA.setDeviceCode(deviceCode != null ? deviceCode.toUpperCase() : "XX"); - userA.setIp(ipBytes); - userA.setDateLogin((int) nowSeconds); - // language_code is NOT updated here - it's updated via separate endpoint when user changes language - - userARepository.save(userA); - log.debug("Updated user data on login: userId={}", userA.getId()); - } - - /** - * Updates user's language code (called when user changes language in app header). - */ - @Transactional - public void updateLanguageCode(Integer userId, String languageCode) { - Optional userOpt = userARepository.findById(userId); - if (userOpt.isPresent()) { - UserA user = userOpt.get(); - user.setLanguageCode(languageCode != null && languageCode.length() == 2 ? languageCode.toUpperCase() : "XX"); - userARepository.save(user); - log.debug("Updated language_code for userId={}: {}", userId, user.getLanguageCode()); - } - } - - /** - * Creates a new user in all 3 tables with referral handling. - */ - private UserA createNewUser(Long telegramId, String screenName, String username, Boolean isPremium, - String languageCode, String countryCode, String deviceCode, - byte[] ipBytes, long nowSeconds, String start) { - - // Create UserA - UserA userA = UserA.builder() - .screenName(screenName) - .telegramId(telegramId) - .telegramName(username != null ? username : "-") - .isPremium(isPremium != null && isPremium ? 1 : 0) - .languageCode(languageCode != null ? languageCode.toUpperCase() : "XX") - .countryCode(countryCode) - .deviceCode(deviceCode != null ? deviceCode.toUpperCase() : "XX") - .ip(ipBytes) - .dateReg((int) nowSeconds) - .dateLogin((int) nowSeconds) - .banned(0) - .build(); - - userA = userARepository.save(userA); - Integer userId = userA.getId(); - - log.info("Created new user: userId={}, telegramId={}", userId, telegramId); - - // Create UserB with same ID - UserB userB = UserB.builder() - .id(userId) - .balanceA(0L) - .balanceB(0L) - .depositTotal(0L) - .depositCount(0) - .withdrawTotal(0L) - .withdrawCount(0) - .build(); - userBRepository.save(userB); - - // Create UserD with referral handling - UserD userD = createUserDWithReferral(userId, start); - userDRepository.save(userD); - - return userA; - } - - /** - * Creates UserD entity with referral chain setup. - * @param userId New user's ID - * @param start Referral parameter from URL (e.g., "774876" from /honey?start=774876) - */ - private UserD createUserDWithReferral(Integer userId, String start) { - UserD.UserDBuilder builder = UserD.builder() - .id(userId) - .refererId1(0) - .refererId2(0) - .refererId3(0) - .refererId4(0) - .refererId5(0) - .masterId(1) // Default master_id = 1 - .referals1(0) - .referals2(0) - .referals3(0) - .referals4(0) - .referals5(0) - .fromReferals1(0L) - .fromReferals2(0L) - .fromReferals3(0L) - .fromReferals4(0L) - .fromReferals5(0L) - .toReferer1(0L) - .toReferer2(0L) - .toReferer3(0L) - .toReferer4(0L) - .toReferer5(0L); - - if (start != null && !start.isEmpty()) { - try { - Integer refererId = Integer.parseInt(start); - Optional refererUserDOpt = userDRepository.findById(refererId); - - if (refererUserDOpt.isPresent()) { - UserD refererUserD = refererUserDOpt.get(); - - // Set referral chain: shift referer's chain down by 1 level - builder.refererId1(refererId) - .masterId(refererUserD.getMasterId()) - .refererId2(refererUserD.getRefererId1()) - .refererId3(refererUserD.getRefererId2()) - .refererId4(refererUserD.getRefererId3()) - .refererId5(refererUserD.getRefererId4()); - - // Increment referal counts for all 5 levels up the chain - setupReferralChain(userId, refererId); - } else { - // Referer doesn't exist, just set referer_id_1 - log.warn("Referer with id {} not found, setting only referer_id_1", refererId); - builder.refererId1(refererId); - } - } catch (NumberFormatException e) { - log.warn("Invalid start parameter format: {}", start); - } - } - - return builder.build(); - } - - /** - * Sets up referral chain and increments referal counts for all 5 levels. - * Example: If user F registers with referer E, increments: - * - referals_1 for E - * - referals_2 for D (E's referer_id_1) - * - referals_3 for C (D's referer_id_1) - * - referals_4 for B (C's referer_id_1) - * - referals_5 for A (B's referer_id_1) - */ - private void setupReferralChain(Integer newUserId, Integer refererId) { - // Level 1: Direct referer - userDRepository.incrementReferals1(refererId); - - Optional level1Opt = userDRepository.findById(refererId); - if (level1Opt.isEmpty()) { - return; - } - - UserD level1 = level1Opt.get(); - - // Level 2 - if (level1.getRefererId1() > 0) { - userDRepository.incrementReferals2(level1.getRefererId1()); - - Optional level2Opt = userDRepository.findById(level1.getRefererId1()); - if (level2Opt.isPresent()) { - UserD level2 = level2Opt.get(); - - // Level 3 - if (level2.getRefererId1() > 0) { - userDRepository.incrementReferals3(level2.getRefererId1()); - - Optional level3Opt = userDRepository.findById(level2.getRefererId1()); - if (level3Opt.isPresent()) { - UserD level3 = level3Opt.get(); - - // Level 4 - if (level3.getRefererId1() > 0) { - userDRepository.incrementReferals4(level3.getRefererId1()); - - Optional level4Opt = userDRepository.findById(level3.getRefererId1()); - if (level4Opt.isPresent()) { - UserD level4 = level4Opt.get(); - - // Level 5 - if (level4.getRefererId1() > 0) { - userDRepository.incrementReferals5(level4.getRefererId1()); - } - } - } - } - } - } - } - - log.info("Referral chain setup completed: newUserId={}, refererId={}", newUserId, refererId); - } - - /** - * Builds screen_name from first_name and last_name. - */ - private String buildScreenName(String firstName, String lastName) { - StringBuilder sb = new StringBuilder(); - if (firstName != null && !firstName.isEmpty()) { - sb.append(firstName); - } - if (lastName != null && !lastName.isEmpty()) { - if (sb.length() > 0) { - sb.append(" "); - } - sb.append(lastName); - } - String result = sb.toString().trim(); - return result.isEmpty() ? "-" : (result.length() > 75 ? result.substring(0, 75) : result); - } - - /** - * Gets user by ID. - */ - public Optional getUserById(Integer userId) { - return userARepository.findById(userId); - } - - /** - * Gets user by Telegram ID. - */ - public Optional getUserByTelegramId(Long telegramId) { - return userARepository.findByTelegramId(telegramId); - } -} - diff --git a/src/main/java/com/honey/honey/HoneyBackendApplication.java b/src/main/java/com/lottery/lottery/LotteryBackendApplication.java similarity index 59% rename from src/main/java/com/honey/honey/HoneyBackendApplication.java rename to src/main/java/com/lottery/lottery/LotteryBackendApplication.java index 5cb5365..e8c2e2d 100644 --- a/src/main/java/com/honey/honey/HoneyBackendApplication.java +++ b/src/main/java/com/lottery/lottery/LotteryBackendApplication.java @@ -1,18 +1,20 @@ -package com.honey.honey; +package com.lottery.lottery; -import com.honey.honey.config.ConfigLoader; -import com.honey.honey.config.TelegramProperties; +import com.lottery.lottery.config.ConfigLoader; +import com.lottery.lottery.config.TelegramProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling +@EnableAsync @EnableConfigurationProperties({TelegramProperties.class}) -public class HoneyBackendApplication { +public class LotteryBackendApplication { public static void main(String[] args) { - SpringApplication app = new SpringApplication(HoneyBackendApplication.class); + SpringApplication app = new SpringApplication(LotteryBackendApplication.class); app.addListeners(new ConfigLoader()); app.run(args); } diff --git a/src/main/java/com/lottery/lottery/config/AdminSecurityConfig.java b/src/main/java/com/lottery/lottery/config/AdminSecurityConfig.java new file mode 100644 index 0000000..13d7799 --- /dev/null +++ b/src/main/java/com/lottery/lottery/config/AdminSecurityConfig.java @@ -0,0 +1,95 @@ +package com.lottery.lottery.config; + +import com.lottery.lottery.security.admin.AdminDetailsService; +import com.lottery.lottery.security.admin.JwtAuthenticationFilter; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; +import java.util.List; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +@RequiredArgsConstructor +public class AdminSecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final AdminDetailsService adminDetailsService; + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public DaoAuthenticationProvider adminAuthenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(adminDetailsService); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Bean + public AuthenticationManager adminAuthenticationManager() { + return new ProviderManager(adminAuthenticationProvider()); + } + + @Bean + public SecurityFilterChain adminSecurityFilterChain(HttpSecurity http) throws Exception { + http + .securityMatcher("/api/admin/**") + .csrf(csrf -> csrf.disable()) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/admin/login").permitAll() + .requestMatchers("/api/admin/users/**").hasAnyRole("ADMIN", "GAME_ADMIN") + .requestMatchers("/api/admin/payments/**").hasAnyRole("ADMIN", "GAME_ADMIN") + .requestMatchers("/api/admin/payouts/**").hasAnyRole("ADMIN", "PAYOUT_SUPPORT", "GAME_ADMIN") + .requestMatchers("/api/admin/rooms/**").hasAnyRole("ADMIN", "GAME_ADMIN") + .requestMatchers("/api/admin/configurations/**").hasAnyRole("ADMIN", "GAME_ADMIN") + .requestMatchers("/api/admin/tickets/**").hasAnyRole("ADMIN", "TICKETS_SUPPORT", "GAME_ADMIN") + .requestMatchers("/api/admin/quick-answers/**").hasAnyRole("ADMIN", "TICKETS_SUPPORT", "GAME_ADMIN") + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .anyRequest().denyAll() + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList( + "http://localhost:5173", + "http://localhost:3000", + "https://win-spin.live" // Main domain (admin panel is on same domain with secret path) + )); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(List.of("*")); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/api/admin/**", configuration); + return source; + } +} + diff --git a/src/main/java/com/honey/honey/config/ConfigLoader.java b/src/main/java/com/lottery/lottery/config/ConfigLoader.java similarity index 96% rename from src/main/java/com/honey/honey/config/ConfigLoader.java rename to src/main/java/com/lottery/lottery/config/ConfigLoader.java index 76b6bbb..e523f21 100644 --- a/src/main/java/com/honey/honey/config/ConfigLoader.java +++ b/src/main/java/com/lottery/lottery/config/ConfigLoader.java @@ -1,4 +1,4 @@ -package com.honey.honey.config; +package com.lottery.lottery.config; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; @@ -19,13 +19,13 @@ import java.util.Properties; * This allows switching between Railway (env vars) and Inferno (mounted file) deployments. * * Priority: - * 1. Mounted file at /run/secrets/honey-config.properties (Inferno) + * 1. Mounted file at /run/secrets/lottery-config.properties (Inferno) * 2. Environment variables (Railway) */ @Slf4j public class ConfigLoader implements ApplicationListener { - private static final String SECRET_FILE_PATH = "/run/secrets/honey-config.properties"; + private static final String SECRET_FILE_PATH = "/run/secrets/lottery-config.properties"; @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { diff --git a/src/main/java/com/honey/honey/config/CorsConfig.java b/src/main/java/com/lottery/lottery/config/CorsConfig.java similarity index 97% rename from src/main/java/com/honey/honey/config/CorsConfig.java rename to src/main/java/com/lottery/lottery/config/CorsConfig.java index 1d0990e..b44e588 100644 --- a/src/main/java/com/honey/honey/config/CorsConfig.java +++ b/src/main/java/com/lottery/lottery/config/CorsConfig.java @@ -1,4 +1,4 @@ -package com.honey.honey.config; +package com.lottery.lottery.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/com/lottery/lottery/config/LocaleConfig.java b/src/main/java/com/lottery/lottery/config/LocaleConfig.java new file mode 100644 index 0000000..747dcdf --- /dev/null +++ b/src/main/java/com/lottery/lottery/config/LocaleConfig.java @@ -0,0 +1,61 @@ +package com.lottery.lottery.config; + +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +@Configuration +public class LocaleConfig { + + // Supported languages + public static final List SUPPORTED_LANGUAGES = Arrays.asList( + "EN", "RU", "DE", "IT", "NL", "PL", "FR", "ES", "ID", "TR" + ); + + @Bean + public MessageSource messageSource() { + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + messageSource.setBasename("messages"); + messageSource.setDefaultEncoding("UTF-8"); + messageSource.setFallbackToSystemLocale(false); + return messageSource; + } + + @Bean + public LocaleResolver localeResolver() { + AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver(); + resolver.setDefaultLocale(Locale.ENGLISH); + return resolver; + } + + /** + * Converts language code (EN, RU, etc.) to Locale. + */ + public static Locale languageCodeToLocale(String languageCode) { + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + return Locale.ENGLISH; + } + + String upperCode = languageCode.toUpperCase(); + // Handle special cases + switch (upperCode) { + case "ID": + return new Locale("id"); // Indonesian + default: + try { + return new Locale(upperCode.toLowerCase()); + } catch (Exception e) { + return Locale.ENGLISH; // Default fallback + } + } + } +} + + diff --git a/src/main/java/com/lottery/lottery/config/TelegramProperties.java b/src/main/java/com/lottery/lottery/config/TelegramProperties.java new file mode 100644 index 0000000..0609f3f --- /dev/null +++ b/src/main/java/com/lottery/lottery/config/TelegramProperties.java @@ -0,0 +1,35 @@ +package com.lottery.lottery.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "telegram") +@Data +public class TelegramProperties { + private String botToken; + + /** + * Bot token for checking channel membership. + * Can be set via environment variable TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN + * or in mounted file at /run/secrets/lottery-config.properties as telegram.channel-checker-bot-token + */ + private String channelCheckerBotToken; + + /** + * Channel ID for follow tasks (e.g., "@win_spin_news" or numeric ID). + * Can be set via environment variable TELEGRAM_FOLLOW_TASK_CHANNEL_ID + * or in mounted file at /run/secrets/lottery-config.properties as telegram.follow-task-channel-id + */ + private String followTaskChannelId; + + /** + * Channel ID for follow withdrawals channel task (e.g., "@win_spin_withdrawals" or numeric ID). + * Can be set via environment variable TELEGRAM_FOLLOW_TASK_CHANNEL_ID_2 + * or in mounted file at /run/secrets/lottery-config.properties as telegram.follow-task-channel-id-2 + */ + private String followTaskChannelId2; +} + + diff --git a/src/main/java/com/lottery/lottery/config/WebConfig.java b/src/main/java/com/lottery/lottery/config/WebConfig.java new file mode 100644 index 0000000..3305311 --- /dev/null +++ b/src/main/java/com/lottery/lottery/config/WebConfig.java @@ -0,0 +1,44 @@ +package com.lottery.lottery.config; + +import com.lottery.lottery.security.AuthInterceptor; +import com.lottery.lottery.security.RateLimitInterceptor; +import com.lottery.lottery.security.UserRateLimitInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@RequiredArgsConstructor +public class WebConfig implements WebMvcConfigurer { + + private final AuthInterceptor authInterceptor; + private final RateLimitInterceptor rateLimitInterceptor; + private final UserRateLimitInterceptor userRateLimitInterceptor; + + @Override + public void addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry registry) { + // NOTE: Rate limiting is NOT applied to Telegram webhook endpoint + // Telegram sends webhooks from multiple IPs and we need to process all updates, especially payments + // Rate limiting interceptor is only for bot registration endpoint (if needed elsewhere) + + // User session interceptor for all other authenticated endpoints + registry.addInterceptor(authInterceptor) + .excludePathPatterns( + "/ping", + "/actuator/**", + "/api/auth/tma/session", // Session creation endpoint doesn't require auth + "/api/telegram/webhook/**", // Telegram webhook (token in path, validated in controller) + "/avatars/**", // Avatar static files don't require auth (served by Nginx in production) + "/api/check_user/**", // User check endpoint for external applications (open endpoint) + "/api/deposit_webhook/**", // 3rd party deposit completion webhook (token in path, no auth) + "/api/notify_broadcast/**", // Notify broadcast start/stop (token in path, no auth) + "/api/remotebet/**", // Remote bet: token + feature switch protected, no user auth + "/api/admin/**" // Admin endpoints are handled by Spring Security + ); + + // User-based rate limiting for payment creation and payout creation (applied after auth interceptor) + registry.addInterceptor(userRateLimitInterceptor) + .addPathPatterns("/api/payments/create", "/api/payouts"); + } +} + diff --git a/src/main/java/com/lottery/lottery/config/WebSocketAuthInterceptor.java b/src/main/java/com/lottery/lottery/config/WebSocketAuthInterceptor.java new file mode 100644 index 0000000..bb5a3c8 --- /dev/null +++ b/src/main/java/com/lottery/lottery/config/WebSocketAuthInterceptor.java @@ -0,0 +1,106 @@ +package com.lottery.lottery.config; + +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.service.SessionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.messaging.support.MessageHeaderAccessor; +import org.springframework.stereotype.Component; + +import java.security.Principal; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WebSocketAuthInterceptor implements ChannelInterceptor { + + private final SessionService sessionService; + + @Override + public Message preSend(Message message, MessageChannel channel) { + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + + if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) { + // Extract Bearer token from headers + List authHeaders = accessor.getNativeHeader("Authorization"); + String token = null; + + if (authHeaders != null && !authHeaders.isEmpty()) { + String authHeader = authHeaders.get(0); + if (authHeader != null && authHeader.startsWith("Bearer ")) { + token = authHeader.substring(7); + } + } + + // Also check query parameter (for SockJS fallback) + if (token == null) { + String query = accessor.getFirstNativeHeader("query"); + if (query != null && query.contains("token=")) { + int tokenStart = query.indexOf("token=") + 6; + int tokenEnd = query.indexOf("&", tokenStart); + if (tokenEnd == -1) { + tokenEnd = query.length(); + } + token = query.substring(tokenStart, tokenEnd); + } + } + + if (token == null || token.isBlank()) { + log.warn("WebSocket connection rejected: No token provided"); + throw new SecurityException("Authentication required"); + } + + // Validate token and get user + var userOpt = sessionService.getUserBySession(token); + if (userOpt.isEmpty()) { + log.warn("WebSocket connection rejected: Invalid token"); + throw new SecurityException("Invalid authentication token"); + } + + UserA user = userOpt.get(); + accessor.setUser(new StompPrincipal(user.getId(), user)); + UserContext.set(user); + + log.debug("WebSocket connection authenticated for user {}", user.getId()); + } + + return message; + } + + @Override + public void postSend(Message message, MessageChannel channel, boolean sent) { + UserContext.clear(); + } + + // Simple principal class to store user info + public static class StompPrincipal implements Principal { + private final Integer userId; + private final UserA user; + + public StompPrincipal(Integer userId, UserA user) { + this.userId = userId; + this.user = user; + } + + public Integer getUserId() { + return userId; + } + + public UserA getUser() { + return user; + } + + @Override + public String getName() { + return String.valueOf(userId); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/config/WebSocketConfig.java b/src/main/java/com/lottery/lottery/config/WebSocketConfig.java new file mode 100644 index 0000000..e53ad4f --- /dev/null +++ b/src/main/java/com/lottery/lottery/config/WebSocketConfig.java @@ -0,0 +1,62 @@ +package com.lottery.lottery.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.ChannelRegistration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +import java.util.Arrays; +import java.util.List; + +@Slf4j +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + private final WebSocketAuthInterceptor authInterceptor; + + @Value("${app.websocket.allowed-origins:https://win-spin.live,https://lottery-fe-production.up.railway.app,https://web.telegram.org,https://webk.telegram.org,https://webz.telegram.org}") + private String allowedOrigins; + + public WebSocketConfig(WebSocketAuthInterceptor authInterceptor) { + this.authInterceptor = authInterceptor; + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + // Enable simple broker for sending messages to clients + config.enableSimpleBroker("/topic", "/queue"); + // Prefix for messages from client to server + config.setApplicationDestinationPrefixes("/app"); + } + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + // Parse allowed origins from configuration + // Spring's setAllowedOriginPatterns uses Ant-style patterns, not regex + // For exact matches, use the URL as-is + // For subdomain matching, use https://*.example.com + List origins = Arrays.asList(allowedOrigins.split(",")); + String[] originPatterns = origins.stream() + .map(String::trim) + .filter(origin -> !origin.isEmpty()) + .toArray(String[]::new); + + log.info("[WEBSOCKET] Configuring WebSocket endpoint /ws with allowed origins: {}", Arrays.toString(originPatterns)); + + // WebSocket endpoint - clients connect here + registry.addEndpoint("/ws") + .setAllowedOriginPatterns(originPatterns) // Restricted to configured domains + .withSockJS(); + } + + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(authInterceptor); + } +} + diff --git a/src/main/java/com/lottery/lottery/config/WebSocketSubscriptionListener.java b/src/main/java/com/lottery/lottery/config/WebSocketSubscriptionListener.java new file mode 100644 index 0000000..1a4ddef --- /dev/null +++ b/src/main/java/com/lottery/lottery/config/WebSocketSubscriptionListener.java @@ -0,0 +1,178 @@ +package com.lottery.lottery.config; + +import com.lottery.lottery.dto.GameRoomStateDto; +import com.lottery.lottery.service.GameRoomService; +import com.lottery.lottery.service.RoomConnectionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.messaging.SessionDisconnectEvent; +import org.springframework.web.socket.messaging.SessionSubscribeEvent; +import org.springframework.web.socket.messaging.SessionUnsubscribeEvent; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WebSocketSubscriptionListener { + + private final GameRoomService gameRoomService; + private final SimpMessagingTemplate messagingTemplate; + private final RoomConnectionService roomConnectionService; + + // Pattern to match room subscription: /topic/room/{roomNumber} + private static final Pattern ROOM_SUBSCRIPTION_PATTERN = Pattern.compile("/topic/room/(\\d+)"); + + /** + * Listens for WebSocket subscription events. + * When a client subscribes to a room topic, sends the current room state immediately. + */ + @EventListener + public void handleSubscription(SessionSubscribeEvent event) { + StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); + String destination = accessor.getDestination(); + + if (destination == null) { + return; + } + + // Check if this is a room subscription + Matcher matcher = ROOM_SUBSCRIPTION_PATTERN.matcher(destination); + if (matcher.matches()) { + try { + Integer roomNumber = Integer.parseInt(matcher.group(1)); + + // Get the user ID from the principal + Object principal = accessor.getUser(); + Integer userId = null; + if (principal instanceof WebSocketAuthInterceptor.StompPrincipal) { + userId = ((WebSocketAuthInterceptor.StompPrincipal) principal).getUserId(); + } + + // Get session ID + String sessionId = accessor.getSessionId(); + + log.info("Client subscribed to room {} (userId: {}, sessionId: {})", roomNumber, userId, sessionId); + + // Register session for disconnect tracking + if (sessionId != null && userId != null) { + roomConnectionService.registerSession(sessionId, userId); + } + + // Track room-level connection (not just round participation) + if (userId != null && sessionId != null) { + roomConnectionService.addUserToRoom(userId, roomNumber, sessionId); + } else { + log.warn("Cannot track room connection: userId={}, sessionId={}", userId, sessionId); + } + + // Get current room state and send it to the subscribing client + // This ensures client gets authoritative state immediately on subscribe + GameRoomStateDto state = gameRoomService.getRoomState(roomNumber); + + // Send state directly to the destination (room topic) + // This will be received by the subscribing client + messagingTemplate.convertAndSend(destination, state); + + log.debug("Sent initial room state to subscriber: room={}, phase={}, participants={}, connectedUsers={}", + roomNumber, state.getPhase(), + state.getParticipants() != null ? state.getParticipants().size() : 0, + state.getConnectedUsers()); + + } catch (NumberFormatException e) { + log.warn("Invalid room number in subscription destination: {}", destination); + } catch (Exception e) { + log.error("Error sending initial state for room subscription: {}", destination, e); + } + } + } + + /** + * Listens for WebSocket unsubscribe events. + * When a client unsubscribes from a room topic, removes them from room connections. + */ + @EventListener + public void handleUnsubscribe(SessionUnsubscribeEvent event) { + StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); + String destination = accessor.getDestination(); + + // Skip if destination is null (Spring WebSocket sometimes sends unsubscribe events without destination during cleanup) + if (destination == null) { + return; + } + + log.debug("Unsubscribe event received for destination: {}", destination); + + // Check if this is a room unsubscription + Matcher matcher = ROOM_SUBSCRIPTION_PATTERN.matcher(destination); + if (matcher.matches()) { + try { + Integer roomNumber = Integer.parseInt(matcher.group(1)); + + // Get the user ID from the principal + Object principal = accessor.getUser(); + Integer userId = null; + if (principal instanceof WebSocketAuthInterceptor.StompPrincipal) { + userId = ((WebSocketAuthInterceptor.StompPrincipal) principal).getUserId(); + } else { + log.warn("Unsubscribe event: principal is not StompPrincipal, type: {}", + principal != null ? principal.getClass().getName() : "null"); + } + + // Get session ID + String sessionId = accessor.getSessionId(); + + if (userId != null && sessionId != null) { + log.info("Client unsubscribed from room {} (userId: {}, sessionId: {})", roomNumber, userId, sessionId); + roomConnectionService.removeUserFromRoom(userId, roomNumber, sessionId); + } else { + log.warn("Unsubscribe event: userId or sessionId is null for destination: {} (userId: {}, sessionId: {})", + destination, userId, sessionId); + } + } catch (NumberFormatException e) { + log.warn("Invalid room number in unsubscription destination: {}", destination); + } catch (Exception e) { + log.error("Error handling room unsubscription: {}", destination, e); + } + } else { + log.debug("Unsubscribe event destination does not match room pattern: {}", destination); + } + } + + /** + * Listens for WebSocket disconnect events. + * When a client disconnects completely, removes them from all rooms. + */ + @EventListener + public void handleDisconnect(SessionDisconnectEvent event) { + StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); + String sessionId = accessor.getSessionId(); + + // Try to get user ID from principal first + Object principal = accessor.getUser(); + Integer userId = null; + if (principal instanceof WebSocketAuthInterceptor.StompPrincipal) { + userId = ((WebSocketAuthInterceptor.StompPrincipal) principal).getUserId(); + } + + if (userId != null && sessionId != null) { + log.info("Client disconnected (userId: {}, sessionId: {}), removing session from all rooms", userId, sessionId); + // Remove only this specific session from all rooms + roomConnectionService.removeUserFromAllRooms(userId, sessionId); + // Also remove session mapping + roomConnectionService.removeSession(sessionId); + } else if (sessionId != null) { + // Principal might be lost, try to get userId from session mapping + log.info("Client disconnected (sessionId: {}), principal lost, using session mapping", sessionId); + roomConnectionService.removeUserFromAllRoomsBySession(sessionId); + } else { + log.warn("Disconnect event: both userId and sessionId are null, cannot remove from rooms"); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/AdminAnalyticsController.java b/src/main/java/com/lottery/lottery/controller/AdminAnalyticsController.java new file mode 100644 index 0000000..e2b222b --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminAnalyticsController.java @@ -0,0 +1,206 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.model.Payment; +import com.lottery.lottery.model.Payout; +import com.lottery.lottery.repository.GameRoundRepository; +import com.lottery.lottery.repository.PaymentRepository; +import com.lottery.lottery.repository.PayoutRepository; +import com.lottery.lottery.repository.UserARepository; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/admin/analytics") +@RequiredArgsConstructor +@PreAuthorize("hasRole('ADMIN')") +public class AdminAnalyticsController { + + private final UserARepository userARepository; + private final PaymentRepository paymentRepository; + private final PayoutRepository payoutRepository; + private final GameRoundRepository gameRoundRepository; + + /** + * Get revenue and payout time series data for charts. + * @param range Time range: 7d, 30d, 90d, 1y, all + * @return Time series data with daily/weekly/monthly aggregation + */ + @GetMapping("/revenue") + public ResponseEntity> getRevenueAnalytics( + @RequestParam(defaultValue = "30d") String range) { + + Instant now = Instant.now(); + Instant startDate; + String granularity; + + // Determine start date and granularity based on range + switch (range.toLowerCase()) { + case "7d": + startDate = now.minus(7, ChronoUnit.DAYS); + granularity = "daily"; + break; + case "30d": + startDate = now.minus(30, ChronoUnit.DAYS); + granularity = "daily"; + break; + case "90d": + startDate = now.minus(90, ChronoUnit.DAYS); + granularity = "daily"; + break; + case "1y": + startDate = now.minus(365, ChronoUnit.DAYS); + granularity = "weekly"; + break; + case "all": + startDate = Instant.ofEpochSecond(0); // All time + granularity = "monthly"; + break; + default: + startDate = now.minus(30, ChronoUnit.DAYS); + granularity = "daily"; + } + + List> dataPoints = new ArrayList<>(); + Instant current = startDate; + + while (current.isBefore(now)) { + Instant periodEnd; + if (granularity.equals("daily")) { + periodEnd = current.plus(1, ChronoUnit.DAYS); + } else if (granularity.equals("weekly")) { + periodEnd = current.plus(7, ChronoUnit.DAYS); + } else { + periodEnd = current.plus(30, ChronoUnit.DAYS); + } + + if (periodEnd.isAfter(now)) { + periodEnd = now; + } + + // CRYPTO only: revenue and payouts in USD for this period + java.math.BigDecimal revenueUsd = paymentRepository.sumUsdAmountByStatusAndUsdAmountNotNullAndCreatedAtBetween( + Payment.PaymentStatus.COMPLETED, current, periodEnd).orElse(java.math.BigDecimal.ZERO); + java.math.BigDecimal payoutsUsd = payoutRepository.sumUsdAmountByTypeCryptoAndStatusAndCreatedAtBetween( + Payout.PayoutStatus.COMPLETED, current, periodEnd).orElse(java.math.BigDecimal.ZERO); + java.math.BigDecimal netRevenueUsd = revenueUsd.subtract(payoutsUsd); + + Map point = new HashMap<>(); + point.put("date", current.getEpochSecond()); + point.put("revenue", revenueUsd); + point.put("payouts", payoutsUsd); + point.put("netRevenue", netRevenueUsd); + + dataPoints.add(point); + + current = periodEnd; + } + + Map response = new HashMap<>(); + response.put("range", range); + response.put("granularity", granularity); + response.put("data", dataPoints); + + return ResponseEntity.ok(response); + } + + /** + * Get user activity time series data (registrations, active players, rounds). + * @param range Time range: 7d, 30d, 90d, 1y, all + * @return Time series data + */ + @GetMapping("/activity") + public ResponseEntity> getActivityAnalytics( + @RequestParam(defaultValue = "30d") String range) { + + Instant now = Instant.now(); + Instant startDate; + String granularity; + + switch (range.toLowerCase()) { + case "7d": + startDate = now.minus(7, ChronoUnit.DAYS); + granularity = "daily"; + break; + case "30d": + startDate = now.minus(30, ChronoUnit.DAYS); + granularity = "daily"; + break; + case "90d": + startDate = now.minus(90, ChronoUnit.DAYS); + granularity = "daily"; + break; + case "1y": + startDate = now.minus(365, ChronoUnit.DAYS); + granularity = "weekly"; + break; + case "all": + startDate = Instant.ofEpochSecond(0); + granularity = "monthly"; + break; + default: + startDate = now.minus(30, ChronoUnit.DAYS); + granularity = "daily"; + } + + List> dataPoints = new ArrayList<>(); + Instant current = startDate; + + while (current.isBefore(now)) { + Instant periodEnd; + if (granularity.equals("daily")) { + periodEnd = current.plus(1, ChronoUnit.DAYS); + } else if (granularity.equals("weekly")) { + periodEnd = current.plus(7, ChronoUnit.DAYS); + } else { + periodEnd = current.plus(30, ChronoUnit.DAYS); + } + + if (periodEnd.isAfter(now)) { + periodEnd = now; + } + + // Convert to Unix timestamps for UserA queries + int periodStartTs = (int) current.getEpochSecond(); + int periodEndTs = (int) periodEnd.getEpochSecond(); + + // Count new registrations in this period (between current and periodEnd) + long newUsers = userARepository.countByDateRegBetween(periodStartTs, periodEndTs); + + // Count active players (logged in) in this period + long activePlayers = userARepository.countByDateLoginBetween(periodStartTs, periodEndTs); + + // Count rounds resolved in this period + long rounds = gameRoundRepository.countByResolvedAtBetween(current, periodEnd); + + Map point = new HashMap<>(); + point.put("date", current.getEpochSecond()); + point.put("newUsers", newUsers); + point.put("activePlayers", activePlayers); + point.put("rounds", rounds); + + dataPoints.add(point); + + current = periodEnd; + } + + Map response = new HashMap<>(); + response.put("range", range); + response.put("granularity", granularity); + response.put("data", dataPoints); + + return ResponseEntity.ok(response); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/AdminBotConfigController.java b/src/main/java/com/lottery/lottery/controller/AdminBotConfigController.java new file mode 100644 index 0000000..3bf508d --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminBotConfigController.java @@ -0,0 +1,96 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.AdminBotConfigDto; +import com.lottery.lottery.dto.AdminBotConfigRequest; +import com.lottery.lottery.service.AdminBotConfigService; +import com.lottery.lottery.service.ConfigurationService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@RestController +@RequestMapping("/api/admin/bots") +@RequiredArgsConstructor +@PreAuthorize("hasRole('ADMIN')") +public class AdminBotConfigController { + + private final AdminBotConfigService adminBotConfigService; + private final ConfigurationService configurationService; + + @GetMapping + public ResponseEntity> list() { + return ResponseEntity.ok(adminBotConfigService.listAll()); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional dto = adminBotConfigService.getById(id); + return dto.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); + } + + @PostMapping + public ResponseEntity create(@Valid @RequestBody AdminBotConfigRequest request) { + try { + AdminBotConfigDto created = adminBotConfigService.create(request); + return ResponseEntity.status(HttpStatus.CREATED).body(created); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); + } + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Integer id, @Valid @RequestBody AdminBotConfigRequest request) { + try { + Optional updated = adminBotConfigService.update(id, request); + return updated.map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.notFound().build()); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); + } + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + boolean deleted = adminBotConfigService.delete(id); + return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build(); + } + + /** + * Shuffle time windows for bots that have the given room enabled. + * Redistributes the same set of time windows randomly across those bots. + */ + @PostMapping("/shuffle") + public ResponseEntity shuffleTimeWindows(@RequestParam int roomNumber) { + if (roomNumber != 2 && roomNumber != 3) { + return ResponseEntity.badRequest().body(Map.of("error", "roomNumber must be 2 or 3")); + } + try { + adminBotConfigService.shuffleTimeWindowsForRoom(roomNumber); + return ResponseEntity.ok().build(); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); + } + } + + @GetMapping("/settings") + public ResponseEntity> getBotSettings() { + return ResponseEntity.ok(Map.of("maxParticipantsBeforeBotJoin", configurationService.getMaxParticipantsBeforeBotJoin())); + } + + @PatchMapping("/settings") + public ResponseEntity updateBotSettings(@RequestBody Map body) { + Integer v = body != null ? body.get("maxParticipantsBeforeBotJoin") : null; + if (v == null) { + return ResponseEntity.badRequest().body(Map.of("error", "maxParticipantsBeforeBotJoin is required")); + } + int updated = configurationService.setMaxParticipantsBeforeBotJoin(v); + return ResponseEntity.ok(Map.of("maxParticipantsBeforeBotJoin", updated)); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/AdminConfigurationsController.java b/src/main/java/com/lottery/lottery/controller/AdminConfigurationsController.java new file mode 100644 index 0000000..7eee83f --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminConfigurationsController.java @@ -0,0 +1,49 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.AdminConfigurationsRequest; +import com.lottery.lottery.service.BotConfigService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Admin API for safe bots and flexible bots (winner-override config used e.g. with /remotebet). + * Configurations tab in admin panel uses GET/PUT /api/admin/configurations. + */ +@RestController +@RequestMapping("/api/admin/configurations") +@RequiredArgsConstructor +@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") +public class AdminConfigurationsController { + + private final BotConfigService botConfigService; + + @GetMapping + public ResponseEntity getConfig() { + return ResponseEntity.ok(botConfigService.getConfig()); + } + + @PutMapping + public ResponseEntity updateConfig( + @RequestBody AdminConfigurationsRequest request + ) { + List safeIds = request.getSafeBotUserIds() != null + ? request.getSafeBotUserIds() + : Collections.emptyList(); + List flexibleBots = Collections.emptyList(); + if (request.getFlexibleBots() != null) { + flexibleBots = request.getFlexibleBots().stream() + .filter(e -> e != null && e.getUserId() != null && e.getWinRate() != null) + .map(e -> new BotConfigService.FlexibleBotEntryDto(e.getUserId(), e.getWinRate())) + .collect(Collectors.toList()); + } + botConfigService.setSafeBotUserIds(safeIds); + botConfigService.setFlexibleBots(flexibleBots); + return ResponseEntity.ok(botConfigService.getConfig()); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/AdminDashboardController.java b/src/main/java/com/lottery/lottery/controller/AdminDashboardController.java new file mode 100644 index 0000000..fd57d7f --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminDashboardController.java @@ -0,0 +1,194 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.model.Payment; +import com.lottery.lottery.model.Payout; +import com.lottery.lottery.model.SupportTicket; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.GameRoundRepository; +import com.lottery.lottery.repository.PaymentRepository; +import com.lottery.lottery.repository.PayoutRepository; +import com.lottery.lottery.repository.SupportTicketRepository; +import com.lottery.lottery.repository.UserARepository; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/api/admin/dashboard") +@RequiredArgsConstructor +@PreAuthorize("hasRole('ADMIN')") +public class AdminDashboardController { + + private final UserARepository userARepository; + private final PaymentRepository paymentRepository; + private final PayoutRepository payoutRepository; + private final GameRoundRepository gameRoundRepository; + private final SupportTicketRepository supportTicketRepository; + + @GetMapping("/stats") + public ResponseEntity> getDashboardStats() { + Instant now = Instant.now(); + Instant todayStart = now.truncatedTo(ChronoUnit.DAYS); + Instant weekStart = now.minus(7, ChronoUnit.DAYS); + Instant monthStart = now.minus(30, ChronoUnit.DAYS); + Instant dayAgo = now.minus(24, ChronoUnit.HOURS); + Instant weekAgo = now.minus(7, ChronoUnit.DAYS); + Instant monthAgo = now.minus(30, ChronoUnit.DAYS); + + // Convert to Unix timestamps (seconds) for UserA date fields + int todayStartTs = (int) todayStart.getEpochSecond(); + int weekStartTs = (int) weekStart.getEpochSecond(); + int monthStartTs = (int) monthStart.getEpochSecond(); + int dayAgoTs = (int) dayAgo.getEpochSecond(); + int weekAgoTs = (int) weekAgo.getEpochSecond(); + int monthAgoTs = (int) monthAgo.getEpochSecond(); + + Map stats = new HashMap<>(); + + // Total Users + long totalUsers = userARepository.count(); + long newUsersToday = userARepository.countByDateRegAfter(todayStartTs); + long newUsersWeek = userARepository.countByDateRegAfter(weekStartTs); + long newUsersMonth = userARepository.countByDateRegAfter(monthStartTs); + + // Active Players (users who logged in recently) + long activePlayers24h = userARepository.countByDateLoginAfter(dayAgoTs); + long activePlayers7d = userARepository.countByDateLoginAfter(weekAgoTs); + long activePlayers30d = userARepository.countByDateLoginAfter(monthAgoTs); + + // Revenue (from completed payments) - in Stars + int totalRevenue = paymentRepository.sumStarsAmountByStatus(Payment.PaymentStatus.COMPLETED) + .orElse(0); + int revenueToday = paymentRepository.sumStarsAmountByStatusAndCreatedAtAfter( + Payment.PaymentStatus.COMPLETED, todayStart).orElse(0); + int revenueWeek = paymentRepository.sumStarsAmountByStatusAndCreatedAtAfter( + Payment.PaymentStatus.COMPLETED, weekStart).orElse(0); + int revenueMonth = paymentRepository.sumStarsAmountByStatusAndCreatedAtAfter( + Payment.PaymentStatus.COMPLETED, monthStart).orElse(0); + + // Payouts (from completed payouts) - in Stars + int totalPayouts = payoutRepository.sumStarsAmountByStatus(Payout.PayoutStatus.COMPLETED) + .orElse(0); + int payoutsToday = payoutRepository.sumStarsAmountByStatusAndCreatedAtAfter( + Payout.PayoutStatus.COMPLETED, todayStart).orElse(0); + int payoutsWeek = payoutRepository.sumStarsAmountByStatusAndCreatedAtAfter( + Payout.PayoutStatus.COMPLETED, weekStart).orElse(0); + int payoutsMonth = payoutRepository.sumStarsAmountByStatusAndCreatedAtAfter( + Payout.PayoutStatus.COMPLETED, monthStart).orElse(0); + + // Net Revenue (in Stars) + int netRevenue = totalRevenue - totalPayouts; + int netRevenueToday = revenueToday - payoutsToday; + int netRevenueWeek = revenueWeek - payoutsWeek; + int netRevenueMonth = revenueMonth - payoutsMonth; + + // CRYPTO only: revenue and payouts in USD (for dashboard / financial analytics) + BigDecimal cryptoRevenueTotal = paymentRepository.sumUsdAmountByStatusAndUsdAmountNotNull(Payment.PaymentStatus.COMPLETED).orElse(BigDecimal.ZERO); + BigDecimal cryptoRevenueToday = paymentRepository.sumUsdAmountByStatusAndUsdAmountNotNullAndCreatedAtAfter(Payment.PaymentStatus.COMPLETED, todayStart).orElse(BigDecimal.ZERO); + BigDecimal cryptoRevenueWeek = paymentRepository.sumUsdAmountByStatusAndUsdAmountNotNullAndCreatedAtAfter(Payment.PaymentStatus.COMPLETED, weekStart).orElse(BigDecimal.ZERO); + BigDecimal cryptoRevenueMonth = paymentRepository.sumUsdAmountByStatusAndUsdAmountNotNullAndCreatedAtAfter(Payment.PaymentStatus.COMPLETED, monthStart).orElse(BigDecimal.ZERO); + BigDecimal cryptoPayoutsTotal = payoutRepository.sumUsdAmountByTypeCryptoAndStatus(Payout.PayoutStatus.COMPLETED).orElse(BigDecimal.ZERO); + BigDecimal cryptoPayoutsToday = payoutRepository.sumUsdAmountByTypeCryptoAndStatusAndCreatedAtAfter(Payout.PayoutStatus.COMPLETED, todayStart).orElse(BigDecimal.ZERO); + BigDecimal cryptoPayoutsWeek = payoutRepository.sumUsdAmountByTypeCryptoAndStatusAndCreatedAtAfter(Payout.PayoutStatus.COMPLETED, weekStart).orElse(BigDecimal.ZERO); + BigDecimal cryptoPayoutsMonth = payoutRepository.sumUsdAmountByTypeCryptoAndStatusAndCreatedAtAfter(Payout.PayoutStatus.COMPLETED, monthStart).orElse(BigDecimal.ZERO); + BigDecimal cryptoNetRevenueTotal = cryptoRevenueTotal.subtract(cryptoPayoutsTotal); + BigDecimal cryptoNetRevenueToday = cryptoRevenueToday.subtract(cryptoPayoutsToday); + BigDecimal cryptoNetRevenueWeek = cryptoRevenueWeek.subtract(cryptoPayoutsWeek); + BigDecimal cryptoNetRevenueMonth = cryptoRevenueMonth.subtract(cryptoPayoutsMonth); + + // Game Rounds + long totalRounds = gameRoundRepository.count(); + long roundsToday = gameRoundRepository.countByResolvedAtAfter(todayStart); + long roundsWeek = gameRoundRepository.countByResolvedAtAfter(weekStart); + long roundsMonth = gameRoundRepository.countByResolvedAtAfter(monthStart); + + // Average Round Pool (from resolved rounds) - round to int + Double avgPoolDouble = gameRoundRepository.avgTotalBetByResolvedAtAfter(monthStart) + .orElse(0.0); + int avgPool = (int) Math.round(avgPoolDouble); + + // Support Tickets + long openTickets = supportTicketRepository.countByStatus(SupportTicket.TicketStatus.OPENED); + // Count tickets closed today + long ticketsResolvedToday = supportTicketRepository.findAll().stream() + .filter(t -> t.getStatus() == SupportTicket.TicketStatus.CLOSED && + t.getUpdatedAt() != null && + t.getUpdatedAt().isAfter(todayStart)) + .count(); + + // Build response + stats.put("users", Map.of( + "total", totalUsers, + "newToday", newUsersToday, + "newWeek", newUsersWeek, + "newMonth", newUsersMonth + )); + + stats.put("activePlayers", Map.of( + "last24h", activePlayers24h, + "last7d", activePlayers7d, + "last30d", activePlayers30d + )); + + stats.put("revenue", Map.of( + "total", totalRevenue, + "today", revenueToday, + "week", revenueWeek, + "month", revenueMonth + )); + + stats.put("payouts", Map.of( + "total", totalPayouts, + "today", payoutsToday, + "week", payoutsWeek, + "month", payoutsMonth + )); + + stats.put("netRevenue", Map.of( + "total", netRevenue, + "today", netRevenueToday, + "week", netRevenueWeek, + "month", netRevenueMonth + )); + + Map crypto = new HashMap<>(); + crypto.put("revenueUsd", cryptoRevenueTotal); + crypto.put("revenueUsdToday", cryptoRevenueToday); + crypto.put("revenueUsdWeek", cryptoRevenueWeek); + crypto.put("revenueUsdMonth", cryptoRevenueMonth); + crypto.put("payoutsUsd", cryptoPayoutsTotal); + crypto.put("payoutsUsdToday", cryptoPayoutsToday); + crypto.put("payoutsUsdWeek", cryptoPayoutsWeek); + crypto.put("payoutsUsdMonth", cryptoPayoutsMonth); + crypto.put("profitUsd", cryptoNetRevenueTotal); + crypto.put("profitUsdToday", cryptoNetRevenueToday); + crypto.put("profitUsdWeek", cryptoNetRevenueWeek); + crypto.put("profitUsdMonth", cryptoNetRevenueMonth); + stats.put("crypto", crypto); + + stats.put("rounds", Map.of( + "total", totalRounds, + "today", roundsToday, + "week", roundsWeek, + "month", roundsMonth, + "avgPool", avgPool + )); + + stats.put("supportTickets", Map.of( + "open", openTickets, + "resolvedToday", ticketsResolvedToday + )); + + return ResponseEntity.ok(stats); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/AdminFeatureSwitchController.java b/src/main/java/com/lottery/lottery/controller/AdminFeatureSwitchController.java new file mode 100644 index 0000000..6e1743b --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminFeatureSwitchController.java @@ -0,0 +1,36 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.service.FeatureSwitchService; +import com.lottery.lottery.service.FeatureSwitchService.FeatureSwitchDto; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/admin/feature-switches") +@RequiredArgsConstructor +@PreAuthorize("hasRole('ADMIN')") +public class AdminFeatureSwitchController { + + private final FeatureSwitchService featureSwitchService; + + @GetMapping + public ResponseEntity> getAll() { + return ResponseEntity.ok(featureSwitchService.getAll()); + } + + @PatchMapping("/{key}") + public ResponseEntity update( + @PathVariable String key, + @RequestBody Map body) { + Boolean enabled = body != null ? body.get("enabled") : null; + if (enabled == null) { + return ResponseEntity.badRequest().build(); + } + return ResponseEntity.ok(featureSwitchService.setEnabled(key, enabled)); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/AdminLoginController.java b/src/main/java/com/lottery/lottery/controller/AdminLoginController.java new file mode 100644 index 0000000..879a403 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminLoginController.java @@ -0,0 +1,51 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.AdminLoginRequest; +import com.lottery.lottery.dto.AdminLoginResponse; +import com.lottery.lottery.service.AdminService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Optional; + +@RestController +@RequestMapping("/api/admin") +@RequiredArgsConstructor +public class AdminLoginController { + + private final AdminService adminService; + + @PostMapping("/login") + public ResponseEntity login(@RequestBody AdminLoginRequest request) { + if (request.getUsername() == null || request.getPassword() == null) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body("Username and password are required"); + } + + Optional tokenOpt = adminService.authenticate( + request.getUsername(), + request.getPassword() + ); + + if (tokenOpt.isEmpty()) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body("Invalid credentials"); + } + + // Get admin to retrieve role + var adminOpt = adminService.getAdminByUsername(request.getUsername()); + String role = adminOpt.map(admin -> admin.getRole()).orElse("ROLE_ADMIN"); + + return ResponseEntity.ok(new AdminLoginResponse( + tokenOpt.get(), + request.getUsername(), + role + )); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/AdminMasterController.java b/src/main/java/com/lottery/lottery/controller/AdminMasterController.java new file mode 100644 index 0000000..a61b80c --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminMasterController.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.AdminMasterDto; +import com.lottery.lottery.service.AdminMasterService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/admin/masters") +@RequiredArgsConstructor +@PreAuthorize("hasRole('ADMIN')") +public class AdminMasterController { + + private final AdminMasterService adminMasterService; + + @GetMapping + public ResponseEntity> getMasters() { + return ResponseEntity.ok(adminMasterService.getMasters()); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/AdminNotificationController.java b/src/main/java/com/lottery/lottery/controller/AdminNotificationController.java new file mode 100644 index 0000000..fb85743 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminNotificationController.java @@ -0,0 +1,42 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.NotifyBroadcastRequest; +import com.lottery.lottery.service.NotificationBroadcastService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +/** + * Admin API to trigger or stop notification broadcast (ADMIN only). + */ +@Slf4j +@RestController +@RequestMapping("/api/admin/notifications") +@RequiredArgsConstructor +@PreAuthorize("hasRole('ADMIN')") +public class AdminNotificationController { + + private final NotificationBroadcastService notificationBroadcastService; + + @PostMapping("/send") + public ResponseEntity send(@RequestBody(required = false) NotifyBroadcastRequest body) { + NotifyBroadcastRequest req = body != null ? body : new NotifyBroadcastRequest(); + notificationBroadcastService.runBroadcast( + req.getMessage(), + req.getImageUrl(), + req.getVideoUrl(), + req.getUserIdFrom(), + req.getUserIdTo(), + req.getButtonText(), + req.getIgnoreBlocked()); + return ResponseEntity.accepted().build(); + } + + @PostMapping("/stop") + public ResponseEntity stop() { + notificationBroadcastService.requestStop(); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/AdminPaymentController.java b/src/main/java/com/lottery/lottery/controller/AdminPaymentController.java new file mode 100644 index 0000000..cb358ff --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminPaymentController.java @@ -0,0 +1,147 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.AdminPaymentDto; +import com.lottery.lottery.model.Payment; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.PaymentRepository; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.repository.UserDRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.persistence.criteria.Predicate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/admin/payments") +@RequiredArgsConstructor +@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") +public class AdminPaymentController { + + private final PaymentRepository paymentRepository; + private final UserARepository userARepository; + private final UserDRepository userDRepository; + + private boolean isGameAdmin() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null || auth.getAuthorities() == null) return false; + return auth.getAuthorities().stream() + .anyMatch(a -> "ROLE_GAME_ADMIN".equals(a.getAuthority())); + } + + @GetMapping + public ResponseEntity> getPayments( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "50") int size, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir, + // Filters + @RequestParam(required = false) String status, + @RequestParam(required = false) Integer userId, + @RequestParam(required = false) String dateFrom, + @RequestParam(required = false) String dateTo, + @RequestParam(required = false) Long amountMin, + @RequestParam(required = false) Long amountMax) { + + // Build sort + Sort sort = Sort.by("createdAt").descending(); // Default sort + if (sortBy != null && !sortBy.trim().isEmpty()) { + Sort.Direction direction = "asc".equalsIgnoreCase(sortDir) + ? Sort.Direction.ASC + : Sort.Direction.DESC; + sort = Sort.by(direction, sortBy); + } + + Pageable pageable = PageRequest.of(page, size, sort); + + List masterIds = isGameAdmin() ? userDRepository.findMasterUserIds() : List.of(); + + // Build specification + Specification spec = (root, query, cb) -> { + List predicates = new ArrayList<>(); + + if (!masterIds.isEmpty()) { + predicates.add(cb.not(root.get("userId").in(masterIds))); + } + if (status != null && !status.trim().isEmpty()) { + try { + Payment.PaymentStatus paymentStatus = Payment.PaymentStatus.valueOf(status.toUpperCase()); + predicates.add(cb.equal(root.get("status"), paymentStatus)); + } catch (IllegalArgumentException e) { + // Invalid status, ignore + } + } + + if (userId != null) { + predicates.add(cb.equal(root.get("userId"), userId)); + } + + // Date range filters would need Instant conversion + // For now, we'll skip them or implement if needed + + return cb.and(predicates.toArray(new Predicate[0])); + }; + + Page paymentPage = paymentRepository.findAll(spec, pageable); + + // Fetch user names + List userIds = paymentPage.getContent().stream() + .map(Payment::getUserId) + .distinct() + .collect(Collectors.toList()); + + Map userNameMap = userARepository.findAllById(userIds).stream() + .collect(Collectors.toMap( + UserA::getId, + u -> u.getTelegramName() != null && !u.getTelegramName().equals("-") + ? u.getTelegramName() + : u.getScreenName() + )); + + // Convert to DTOs + Page dtoPage = paymentPage.map(payment -> { + String userName = userNameMap.getOrDefault(payment.getUserId(), "Unknown"); + return AdminPaymentDto.builder() + .id(payment.getId()) + .userId(payment.getUserId()) + .userName(userName) + .orderId(payment.getOrderId()) + .starsAmount(payment.getStarsAmount()) + .ticketsAmount(payment.getTicketsAmount()) + .status(payment.getStatus().name()) + .telegramPaymentChargeId(payment.getTelegramPaymentChargeId()) + .telegramProviderPaymentChargeId(payment.getTelegramProviderPaymentChargeId()) + .createdAt(payment.getCreatedAt()) + .completedAt(payment.getCompletedAt()) + .build(); + }); + + Map response = new HashMap<>(); + response.put("content", dtoPage.getContent()); + response.put("totalElements", dtoPage.getTotalElements()); + response.put("totalPages", dtoPage.getTotalPages()); + response.put("currentPage", dtoPage.getNumber()); + response.put("size", dtoPage.getSize()); + response.put("hasNext", dtoPage.hasNext()); + response.put("hasPrevious", dtoPage.hasPrevious()); + + return ResponseEntity.ok(response); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/AdminPayoutController.java b/src/main/java/com/lottery/lottery/controller/AdminPayoutController.java new file mode 100644 index 0000000..1adf3ea --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminPayoutController.java @@ -0,0 +1,204 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.AdminPayoutDto; +import com.lottery.lottery.model.Payout; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.repository.PayoutRepository; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.repository.UserBRepository; +import com.lottery.lottery.repository.UserDRepository; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.service.PayoutService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import jakarta.persistence.criteria.Predicate; +import org.springframework.http.HttpStatus; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/admin/payouts") +@RequiredArgsConstructor +@PreAuthorize("hasAnyRole('ADMIN', 'PAYOUT_SUPPORT', 'GAME_ADMIN')") +public class AdminPayoutController { + + private final PayoutRepository payoutRepository; + private final UserARepository userARepository; + private final UserBRepository userBRepository; + private final UserDRepository userDRepository; + private final PayoutService payoutService; + private final LocalizationService localizationService; + + private boolean isGameAdmin() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null || auth.getAuthorities() == null) return false; + return auth.getAuthorities().stream() + .anyMatch(a -> "ROLE_GAME_ADMIN".equals(a.getAuthority())); + } + + @GetMapping + public ResponseEntity> getPayouts( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "50") int size, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir, + // Filters + @RequestParam(required = false) String status, + @RequestParam(required = false) Integer userId, + @RequestParam(required = false) String type) { + + // Build sort + Sort sort = Sort.by("createdAt").descending(); // Default sort + if (sortBy != null && !sortBy.trim().isEmpty()) { + Sort.Direction direction = "asc".equalsIgnoreCase(sortDir) + ? Sort.Direction.ASC + : Sort.Direction.DESC; + sort = Sort.by(direction, sortBy); + } + + Pageable pageable = PageRequest.of(page, size, sort); + + List masterIds = isGameAdmin() ? userDRepository.findMasterUserIds() : List.of(); + + // Build specification + Specification spec = (root, query, cb) -> { + List predicates = new ArrayList<>(); + + if (!masterIds.isEmpty()) { + predicates.add(cb.not(root.get("userId").in(masterIds))); + } + if (status != null && !status.trim().isEmpty()) { + try { + Payout.PayoutStatus payoutStatus = Payout.PayoutStatus.valueOf(status.toUpperCase()); + predicates.add(cb.equal(root.get("status"), payoutStatus)); + } catch (IllegalArgumentException e) { + // Invalid status, ignore + } + } + + if (userId != null) { + predicates.add(cb.equal(root.get("userId"), userId)); + } + + if (type != null && !type.trim().isEmpty()) { + try { + Payout.PayoutType payoutType = Payout.PayoutType.valueOf(type.toUpperCase()); + predicates.add(cb.equal(root.get("type"), payoutType)); + } catch (IllegalArgumentException e) { + // Invalid type, ignore + } + } + + return cb.and(predicates.toArray(new Predicate[0])); + }; + + Page payoutPage = payoutRepository.findAll(spec, pageable); + + // Fetch user names + List userIds = payoutPage.getContent().stream() + .map(Payout::getUserId) + .distinct() + .collect(Collectors.toList()); + + Map userNameMap = userARepository.findAllById(userIds).stream() + .collect(Collectors.toMap( + UserA::getId, + u -> u.getTelegramName() != null && !u.getTelegramName().equals("-") + ? u.getTelegramName() + : u.getScreenName() + )); + + // Convert to DTOs + Page dtoPage = payoutPage.map(payout -> { + String userName = userNameMap.getOrDefault(payout.getUserId(), "Unknown"); + return AdminPayoutDto.builder() + .id(payout.getId()) + .userId(payout.getUserId()) + .userName(userName) + .username(payout.getUsername()) + .type(payout.getType().name()) + .giftName(payout.getGiftName() != null ? payout.getGiftName().name() : null) + .total(payout.getTotal()) + .starsAmount(payout.getStarsAmount()) + .quantity(payout.getQuantity()) + .status(payout.getStatus().name()) + .createdAt(payout.getCreatedAt()) + .resolvedAt(payout.getResolvedAt()) + .build(); + }); + + Map response = new HashMap<>(); + response.put("content", dtoPage.getContent()); + response.put("totalElements", dtoPage.getTotalElements()); + response.put("totalPages", dtoPage.getTotalPages()); + response.put("currentPage", dtoPage.getNumber()); + response.put("size", dtoPage.getSize()); + response.put("hasNext", dtoPage.hasNext()); + response.put("hasPrevious", dtoPage.hasPrevious()); + + return ResponseEntity.ok(response); + } + + @PostMapping("/{id}/complete") + @PreAuthorize("hasAnyRole('ADMIN', 'PAYOUT_SUPPORT')") + public ResponseEntity completePayout(@PathVariable Long id) { + try { + Optional payoutOpt = payoutRepository.findById(id); + if (payoutOpt.isEmpty()) { + return ResponseEntity.badRequest() + .body(Map.of("error", localizationService.getMessage("payout.error.notFound", String.valueOf(id)))); + } + Payout payout = payoutOpt.get(); + if (payout.getStatus() != Payout.PayoutStatus.PROCESSING) { + return ResponseEntity.badRequest() + .body(Map.of("error", localizationService.getMessage("payout.error.onlyProcessingCanComplete", payout.getStatus().name()))); + } + payoutService.markPayoutCompleted(id); + return ResponseEntity.ok(Map.of("message", "Payout marked as completed")); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("error", e.getMessage())); + } + } + + @PostMapping("/{id}/cancel") + @PreAuthorize("hasAnyRole('ADMIN', 'PAYOUT_SUPPORT')") + public ResponseEntity cancelPayout(@PathVariable Long id) { + try { + Optional payoutOpt = payoutRepository.findById(id); + if (payoutOpt.isEmpty()) { + return ResponseEntity.badRequest() + .body(Map.of("error", localizationService.getMessage("payout.error.notFound", String.valueOf(id)))); + } + Payout payout = payoutOpt.get(); + if (payout.getStatus() != Payout.PayoutStatus.PROCESSING) { + return ResponseEntity.badRequest() + .body(Map.of("error", localizationService.getMessage("payout.error.onlyProcessingCanCancel", payout.getStatus().name()))); + } + payoutService.markPayoutCancelled(id); + return ResponseEntity.ok(Map.of("message", "Payout cancelled")); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("error", e.getMessage())); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/AdminPromotionController.java b/src/main/java/com/lottery/lottery/controller/AdminPromotionController.java new file mode 100644 index 0000000..e5af27b --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminPromotionController.java @@ -0,0 +1,139 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.*; +import com.lottery.lottery.service.AdminPromotionService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@RestController +@RequestMapping("/api/admin/promotions") +@RequiredArgsConstructor +@PreAuthorize("hasRole('ADMIN')") +public class AdminPromotionController { + + private final AdminPromotionService adminPromotionService; + + @GetMapping + public ResponseEntity> listPromotions() { + return ResponseEntity.ok(adminPromotionService.listPromotions()); + } + + @GetMapping("/{id}") + public ResponseEntity getPromotion(@PathVariable int id) { + return adminPromotionService.getPromotion(id) + .map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.notFound().build()); + } + + @PostMapping + public ResponseEntity createPromotion(@Valid @RequestBody AdminPromotionRequest request) { + try { + AdminPromotionDto dto = adminPromotionService.createPromotion(request); + return ResponseEntity.status(HttpStatus.CREATED).body(dto); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } + + @PutMapping("/{id}") + public ResponseEntity updatePromotion( + @PathVariable int id, + @Valid @RequestBody AdminPromotionRequest request) { + try { + return adminPromotionService.updatePromotion(id, request) + .map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.notFound().build()); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } + + @DeleteMapping("/{id}") + public ResponseEntity deletePromotion(@PathVariable int id) { + return adminPromotionService.deletePromotion(id) + ? ResponseEntity.noContent().build() + : ResponseEntity.notFound().build(); + } + + // --- Rewards --- + + @GetMapping("/{promoId}/rewards") + public ResponseEntity> listRewards(@PathVariable int promoId) { + return ResponseEntity.ok(adminPromotionService.listRewards(promoId)); + } + + @PostMapping("/{promoId}/rewards") + public ResponseEntity createReward( + @PathVariable int promoId, + @Valid @RequestBody AdminPromotionRewardRequest request) { + try { + AdminPromotionRewardDto dto = adminPromotionService.createReward(promoId, request); + return ResponseEntity.status(HttpStatus.CREATED).body(dto); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } + + @PutMapping("/rewards/{rewardId}") + public ResponseEntity updateReward( + @PathVariable int rewardId, + @Valid @RequestBody AdminPromotionRewardRequest request) { + try { + return adminPromotionService.updateReward(rewardId, request) + .map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.notFound().build()); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } + + @DeleteMapping("/rewards/{rewardId}") + public ResponseEntity deleteReward(@PathVariable int rewardId) { + return adminPromotionService.deleteReward(rewardId) + ? ResponseEntity.noContent().build() + : ResponseEntity.notFound().build(); + } + + // --- Promotion users (leaderboard / results) --- + + @GetMapping("/{promoId}/users") + public ResponseEntity> getPromotionUsers( + @PathVariable int promoId, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "50") int size, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir, + @RequestParam(required = false) Integer userId) { + Page dtoPage = adminPromotionService.getPromotionUsers( + promoId, page, size, sortBy, sortDir, userId); + Map response = new HashMap<>(); + response.put("content", dtoPage.getContent()); + response.put("totalElements", dtoPage.getTotalElements()); + response.put("totalPages", dtoPage.getTotalPages()); + response.put("currentPage", dtoPage.getNumber()); + response.put("size", dtoPage.getSize()); + response.put("hasNext", dtoPage.hasNext()); + response.put("hasPrevious", dtoPage.hasPrevious()); + return ResponseEntity.ok(response); + } + + @PatchMapping("/{promoId}/users/{userId}/points") + public ResponseEntity updatePromotionUserPoints( + @PathVariable int promoId, + @PathVariable int userId, + @Valid @RequestBody AdminPromotionUserPointsRequest request) { + Optional updated = adminPromotionService.updatePromotionUserPoints( + promoId, userId, request.getPoints()); + return updated.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/AdminRoomController.java b/src/main/java/com/lottery/lottery/controller/AdminRoomController.java new file mode 100644 index 0000000..58ba3d7 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminRoomController.java @@ -0,0 +1,57 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.AdminRoomDetailDto; +import com.lottery.lottery.dto.AdminRoomOnlineUserDto; +import com.lottery.lottery.dto.AdminRoomSummaryDto; +import com.lottery.lottery.service.GameRoomService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/admin/rooms") +@RequiredArgsConstructor +public class AdminRoomController { + + private final GameRoomService gameRoomService; + + @GetMapping + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> listRooms() { + return ResponseEntity.ok(gameRoomService.getAdminRoomSummaries()); + } + + @GetMapping("/online-users") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> getOnlineUsers() { + return ResponseEntity.ok(gameRoomService.getAdminOnlineUsersAcrossRooms()); + } + + @GetMapping("/{roomNumber}") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity getRoomDetail(@PathVariable Integer roomNumber) { + if (roomNumber == null || roomNumber < 1 || roomNumber > 3) { + return ResponseEntity.badRequest().build(); + } + return ResponseEntity.ok(gameRoomService.getAdminRoomDetail(roomNumber)); + } + + @PostMapping("/{roomNumber}/repair") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity> repairRoom(@PathVariable Integer roomNumber) { + if (roomNumber == null || roomNumber < 1 || roomNumber > 3) { + return ResponseEntity.badRequest().build(); + } + try { + gameRoomService.repairRoom(roomNumber); + return ResponseEntity.ok(Map.of("success", true, "message", "Repair completed for room " + roomNumber)); + } catch (Exception e) { + return ResponseEntity.internalServerError() + .body(Map.of("success", false, "message", e.getMessage() != null ? e.getMessage() : "Repair failed")); + } + } +} diff --git a/src/main/java/com/lottery/lottery/controller/AdminSupportTicketController.java b/src/main/java/com/lottery/lottery/controller/AdminSupportTicketController.java new file mode 100644 index 0000000..02b51a8 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminSupportTicketController.java @@ -0,0 +1,316 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.*; +import com.lottery.lottery.model.Admin; +import com.lottery.lottery.model.SupportMessage; +import com.lottery.lottery.model.SupportTicket; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.AdminRepository; +import com.lottery.lottery.repository.SupportMessageRepository; +import com.lottery.lottery.repository.SupportTicketRepository; +import com.lottery.lottery.repository.UserARepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.*; + +import jakarta.persistence.criteria.Predicate; +import jakarta.validation.Valid; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/admin/tickets") +@RequiredArgsConstructor +@PreAuthorize("hasAnyRole('ADMIN', 'TICKETS_SUPPORT', 'GAME_ADMIN')") +public class AdminSupportTicketController { + + private final SupportTicketRepository supportTicketRepository; + private final SupportMessageRepository supportMessageRepository; + private final UserARepository userARepository; + private final AdminRepository adminRepository; + + @GetMapping + public ResponseEntity> getTickets( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "50") int size, + @RequestParam(required = false) String status, + @RequestParam(required = false) Integer userId, + @RequestParam(required = false) String search) { + + Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending()); + + // Build specification + Specification spec = (root, query, cb) -> { + List predicates = new ArrayList<>(); + + if (status != null && !status.trim().isEmpty()) { + try { + SupportTicket.TicketStatus ticketStatus = SupportTicket.TicketStatus.valueOf(status.toUpperCase()); + predicates.add(cb.equal(root.get("status"), ticketStatus)); + } catch (IllegalArgumentException e) { + // Invalid status, ignore + } + } + + if (userId != null) { + predicates.add(cb.equal(root.get("user").get("id"), userId)); + } + + if (search != null && !search.trim().isEmpty()) { + String searchPattern = "%" + search.trim() + "%"; + predicates.add(cb.or( + cb.like(cb.lower(root.get("subject")), searchPattern.toLowerCase()) + )); + } + + return cb.and(predicates.toArray(new Predicate[0])); + }; + + Page ticketPage = supportTicketRepository.findAll(spec, pageable); + + // Fetch user names and message counts + List userIds = ticketPage.getContent().stream() + .map(t -> t.getUser().getId()) + .distinct() + .collect(Collectors.toList()); + + Map userNameMap = userARepository.findAllById(userIds).stream() + .collect(Collectors.toMap( + UserA::getId, + u -> u.getTelegramName() != null && !u.getTelegramName().equals("-") + ? u.getTelegramName() + : u.getScreenName() + )); + + // Convert to DTOs + Page dtoPage = ticketPage.map(ticket -> { + String userName = userNameMap.getOrDefault(ticket.getUser().getId(), "Unknown"); + long messageCount = supportMessageRepository.countByTicketId(ticket.getId()); + + // Get last message preview + List lastMessages = supportMessageRepository.findByTicketIdOrderByCreatedAtAsc(ticket.getId()); + String lastMessagePreview = ""; + Instant lastMessageAt = null; + if (!lastMessages.isEmpty()) { + SupportMessage lastMsg = lastMessages.get(lastMessages.size() - 1); + lastMessagePreview = lastMsg.getMessage().length() > 100 + ? lastMsg.getMessage().substring(0, 100) + "..." + : lastMsg.getMessage(); + lastMessageAt = lastMsg.getCreatedAt(); + } + + return AdminSupportTicketDto.builder() + .id(ticket.getId()) + .userId(ticket.getUser().getId()) + .userName(userName) + .subject(ticket.getSubject()) + .status(ticket.getStatus().name()) + .createdAt(ticket.getCreatedAt()) + .updatedAt(ticket.getUpdatedAt()) + .messageCount(messageCount) + .lastMessagePreview(lastMessagePreview) + .lastMessageAt(lastMessageAt) + .build(); + }); + + Map response = new HashMap<>(); + response.put("content", dtoPage.getContent()); + response.put("totalElements", dtoPage.getTotalElements()); + response.put("totalPages", dtoPage.getTotalPages()); + response.put("currentPage", dtoPage.getNumber()); + response.put("size", dtoPage.getSize()); + response.put("hasNext", dtoPage.hasNext()); + response.put("hasPrevious", dtoPage.hasPrevious()); + + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}") + public ResponseEntity getTicketDetail(@PathVariable Long id) { + return supportTicketRepository.findById(id) + .map(ticket -> { + String userName = ticket.getUser().getTelegramName() != null && !ticket.getUser().getTelegramName().equals("-") + ? ticket.getUser().getTelegramName() + : ticket.getUser().getScreenName(); + + List messages = supportMessageRepository.findByTicketIdOrderByCreatedAtAsc(id); + + // Get all admin user IDs from admins table + List adminUserIds = adminRepository.findAll().stream() + .filter(admin -> admin.getUserId() != null) + .map(Admin::getUserId) + .collect(Collectors.toList()); + + List messageDtos = messages.stream() + .map(msg -> { + // Check if message is from admin by checking if user_id is in admins table + boolean isAdmin = adminUserIds.contains(msg.getUser().getId()); + String msgUserName = msg.getUser().getTelegramName() != null && !msg.getUser().getTelegramName().equals("-") + ? msg.getUser().getTelegramName() + : msg.getUser().getScreenName(); + + return AdminSupportMessageDto.builder() + .id(msg.getId()) + .userId(msg.getUser().getId()) + .userName(msgUserName) + .message(msg.getMessage()) + .createdAt(msg.getCreatedAt()) + .isAdmin(isAdmin) + .build(); + }) + .collect(Collectors.toList()); + + return AdminSupportTicketDetailDto.builder() + .id(ticket.getId()) + .userId(ticket.getUser().getId()) + .userName(userName) + .subject(ticket.getSubject()) + .status(ticket.getStatus().name()) + .createdAt(ticket.getCreatedAt()) + .updatedAt(ticket.getUpdatedAt()) + .messages(messageDtos) + .build(); + }) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + @PostMapping("/{id}/reply") + public ResponseEntity replyToTicket( + @PathVariable Long id, + @Valid @RequestBody SupportTicketReplyRequest request) { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String adminUsername; + + if (authentication.getPrincipal() instanceof UserDetails) { + adminUsername = ((UserDetails) authentication.getPrincipal()).getUsername(); + } else { + // Fallback if principal is a String + adminUsername = authentication.getName(); + } + + // Get admin entity to find user_id + Admin admin = adminRepository.findByUsername(adminUsername) + .orElseThrow(() -> new RuntimeException("Admin not found: " + adminUsername)); + + if (admin.getUserId() == null) { + return ResponseEntity.badRequest().body(Map.of("error", "Admin account is not linked to a user account")); + } + + // Get the admin's UserA entity + UserA adminUser = userARepository.findById(admin.getUserId()) + .orElseThrow(() -> new RuntimeException("Admin user account not found: " + admin.getUserId())); + + return supportTicketRepository.findById(id) + .map(ticket -> { + // Create message without prefix, using admin's user_id + SupportMessage message = SupportMessage.builder() + .ticket(ticket) + .user(adminUser) // Use admin's UserA entity + .message(request.getMessage()) // Save message as-is, no prefix + .build(); + + supportMessageRepository.save(message); + + // Update ticket updated_at + ticket.setUpdatedAt(java.time.Instant.now()); + if (ticket.getStatus() == SupportTicket.TicketStatus.CLOSED) { + ticket.setStatus(SupportTicket.TicketStatus.OPENED); // Reopen if admin replies + } + supportTicketRepository.save(ticket); + + return ResponseEntity.ok(Map.of("message", "Reply sent successfully")); + }) + .orElse(ResponseEntity.notFound().build()); + } + + @PostMapping("/{id}/close") + public ResponseEntity closeTicket(@PathVariable Long id) { + return supportTicketRepository.findById(id) + .map(ticket -> { + ticket.setStatus(SupportTicket.TicketStatus.CLOSED); + ticket.setUpdatedAt(java.time.Instant.now()); + supportTicketRepository.save(ticket); + return ResponseEntity.ok(Map.of("message", "Ticket closed")); + }) + .orElse(ResponseEntity.notFound().build()); + } + + @PostMapping("/{id}/reopen") + public ResponseEntity reopenTicket(@PathVariable Long id) { + return supportTicketRepository.findById(id) + .map(ticket -> { + ticket.setStatus(SupportTicket.TicketStatus.OPENED); + ticket.setUpdatedAt(java.time.Instant.now()); + supportTicketRepository.save(ticket); + return ResponseEntity.ok(Map.of("message", "Ticket reopened")); + }) + .orElse(ResponseEntity.notFound().build()); + } + + @PutMapping("/messages/{messageId}") + public ResponseEntity editMessage( + @PathVariable Long messageId, + @Valid @RequestBody SupportTicketReplyRequest request) { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String adminUsername; + + if (authentication.getPrincipal() instanceof UserDetails) { + adminUsername = ((UserDetails) authentication.getPrincipal()).getUsername(); + } else { + adminUsername = authentication.getName(); + } + + // Get admin entity to find user_id + Admin admin = adminRepository.findByUsername(adminUsername) + .orElseThrow(() -> new RuntimeException("Admin not found: " + adminUsername)); + + if (admin.getUserId() == null) { + return ResponseEntity.badRequest().body(Map.of("error", "Admin account is not linked to a user account")); + } + + return supportMessageRepository.findById(messageId) + .map(message -> { + // Check if message is from this admin + if (!message.getUser().getId().equals(admin.getUserId())) { + return ResponseEntity.badRequest().body(Map.of("error", "You can only edit your own messages")); + } + + // Check if user is an admin (verify in admins table) + boolean isAdmin = adminRepository.findAll().stream() + .anyMatch(a -> a.getUserId() != null && a.getUserId().equals(message.getUser().getId())); + + if (!isAdmin) { + return ResponseEntity.badRequest().body(Map.of("error", "Only admin messages can be edited")); + } + + // Update message + message.setMessage(request.getMessage()); + supportMessageRepository.save(message); + + // Update ticket updated_at + message.getTicket().setUpdatedAt(java.time.Instant.now()); + supportTicketRepository.save(message.getTicket()); + + return ResponseEntity.ok(Map.of("message", "Message updated successfully")); + }) + .orElse(ResponseEntity.notFound().build()); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/AdminUserController.java b/src/main/java/com/lottery/lottery/controller/AdminUserController.java new file mode 100644 index 0000000..ea3ed5a --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/AdminUserController.java @@ -0,0 +1,277 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.*; +import com.lottery.lottery.service.AdminUserService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import jakarta.validation.Valid; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@RestController +@RequestMapping("/api/admin/users") +@RequiredArgsConstructor +public class AdminUserController { + + /** Sortable fields: UserA properties plus UserB/UserD (handled via custom query in service). */ + private static final Set SORTABLE_FIELDS = Set.of( + "id", "screenName", "telegramId", "telegramName", "isPremium", + "languageCode", "countryCode", "deviceCode", "dateReg", "dateLogin", "banned", + "balanceA", "depositTotal", "withdrawTotal", "roundsPlayed", "referralCount", "profit" + ); + private static final Set DEPOSIT_SORT_FIELDS = Set.of("id", "usdAmount", "status", "orderId", "createdAt", "completedAt"); + private static final Set WITHDRAWAL_SORT_FIELDS = Set.of("id", "usdAmount", "cryptoName", "amountToSend", "txhash", "status", "paymentId", "createdAt", "resolvedAt"); + + private final AdminUserService adminUserService; + + private boolean isGameAdmin() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null || auth.getAuthorities() == null) return false; + return auth.getAuthorities().stream() + .anyMatch(a -> "ROLE_GAME_ADMIN".equals(a.getAuthority())); + } + + @GetMapping + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> getUsers( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "50") int size, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir, + // Filters + @RequestParam(required = false) String search, + @RequestParam(required = false) Integer banned, + @RequestParam(required = false) String countryCode, + @RequestParam(required = false) String languageCode, + @RequestParam(required = false) Integer dateRegFrom, + @RequestParam(required = false) Integer dateRegTo, + @RequestParam(required = false) Long balanceMin, + @RequestParam(required = false) Long balanceMax, + @RequestParam(required = false) Integer roundsPlayedMin, + @RequestParam(required = false) Integer roundsPlayedMax, + @RequestParam(required = false) Integer referralCountMin, + @RequestParam(required = false) Integer referralCountMax, + @RequestParam(required = false) Integer referrerId, + @RequestParam(required = false) Integer referralLevel, + @RequestParam(required = false) String ip) { + + // Build sort. Fields on UserB/UserD (balanceA, depositTotal, withdrawTotal, roundsPlayed, referralCount) + // are handled in service via custom query; others are applied to UserA. + Set sortRequiresJoin = Set.of("balanceA", "depositTotal", "withdrawTotal", "roundsPlayed", "referralCount", "profit"); + String effectiveSortBy = sortBy != null && sortBy.trim().isEmpty() ? null : (sortBy != null ? sortBy.trim() : null); + if (effectiveSortBy != null && sortRequiresJoin.contains(effectiveSortBy)) { + // Pass through; service will use custom ordered query + } else if (effectiveSortBy != null && !SORTABLE_FIELDS.contains(effectiveSortBy)) { + effectiveSortBy = null; + } + Sort sort = Sort.by("id").descending(); + if (effectiveSortBy != null && !sortRequiresJoin.contains(effectiveSortBy)) { + Sort.Direction direction = "asc".equalsIgnoreCase(sortDir) ? Sort.Direction.ASC : Sort.Direction.DESC; + sort = Sort.by(direction, effectiveSortBy); + } + Pageable pageable = PageRequest.of(page, size, sort); + + // Convert balance filters from tickets (divide by 1000000) to bigint format + Long balanceMinBigint = balanceMin != null ? balanceMin * 1000000L : null; + Long balanceMaxBigint = balanceMax != null ? balanceMax * 1000000L : null; + + boolean excludeMasters = isGameAdmin(); + Page dtoPage = adminUserService.getUsers( + pageable, + search, + banned, + countryCode, + languageCode, + dateRegFrom, + dateRegTo, + balanceMinBigint, + balanceMaxBigint, + roundsPlayedMin, + roundsPlayedMax, + referralCountMin, + referralCountMax, + referrerId, + referralLevel, + ip, + effectiveSortBy, + sortDir, + excludeMasters + ); + + Map response = new HashMap<>(); + response.put("content", dtoPage.getContent()); + response.put("totalElements", dtoPage.getTotalElements()); + response.put("totalPages", dtoPage.getTotalPages()); + response.put("currentPage", dtoPage.getNumber()); + response.put("size", dtoPage.getSize()); + response.put("hasNext", dtoPage.hasNext()); + response.put("hasPrevious", dtoPage.hasPrevious()); + + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity getUserDetail(@PathVariable Integer id) { + AdminUserDetailDto userDetail = adminUserService.getUserDetail(id, isGameAdmin()); + if (userDetail == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(userDetail); + } + + @GetMapping("/{id}/transactions") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> getUserTransactions( + @PathVariable Integer id, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "50") int size) { + + Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending()); + Page transactions = adminUserService.getUserTransactions(id, pageable); + + Map response = new HashMap<>(); + response.put("content", transactions.getContent()); + response.put("totalElements", transactions.getTotalElements()); + response.put("totalPages", transactions.getTotalPages()); + response.put("currentPage", transactions.getNumber()); + response.put("size", transactions.getSize()); + response.put("hasNext", transactions.hasNext()); + response.put("hasPrevious", transactions.hasPrevious()); + + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}/game-rounds") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> getUserGameRounds( + @PathVariable Integer id, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "50") int size) { + + Pageable pageable = PageRequest.of(page, size); + Page rounds = adminUserService.getUserGameRounds(id, pageable); + + Map response = new HashMap<>(); + response.put("content", rounds.getContent()); + response.put("totalElements", rounds.getTotalElements()); + response.put("totalPages", rounds.getTotalPages()); + response.put("currentPage", rounds.getNumber()); + response.put("size", rounds.getSize()); + response.put("hasNext", rounds.hasNext()); + response.put("hasPrevious", rounds.hasPrevious()); + + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}/payments") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> getUserPayments( + @PathVariable Integer id, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int size, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir) { + String field = sortBy != null && DEPOSIT_SORT_FIELDS.contains(sortBy.trim()) ? sortBy.trim() : "createdAt"; + Sort.Direction dir = "asc".equalsIgnoreCase(sortDir) ? Sort.Direction.ASC : Sort.Direction.DESC; + Pageable pageable = PageRequest.of(page, size, Sort.by(dir, field)); + Page deposits = adminUserService.getUserPayments(id, pageable); + Map response = new HashMap<>(); + response.put("content", deposits.getContent()); + response.put("totalElements", deposits.getTotalElements()); + response.put("totalPages", deposits.getTotalPages()); + response.put("currentPage", deposits.getNumber()); + response.put("size", deposits.getSize()); + response.put("hasNext", deposits.hasNext()); + response.put("hasPrevious", deposits.hasPrevious()); + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}/payouts") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> getUserPayouts( + @PathVariable Integer id, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int size, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir) { + String field = sortBy != null && WITHDRAWAL_SORT_FIELDS.contains(sortBy.trim()) ? sortBy.trim() : "createdAt"; + Sort.Direction dir = "asc".equalsIgnoreCase(sortDir) ? Sort.Direction.ASC : Sort.Direction.DESC; + Pageable pageable = PageRequest.of(page, size, Sort.by(dir, field)); + Page payouts = adminUserService.getUserPayouts(id, pageable); + Map response = new HashMap<>(); + response.put("content", payouts.getContent()); + response.put("totalElements", payouts.getTotalElements()); + response.put("totalPages", payouts.getTotalPages()); + response.put("currentPage", payouts.getNumber()); + response.put("size", payouts.getSize()); + response.put("hasNext", payouts.hasNext()); + response.put("hasPrevious", payouts.hasPrevious()); + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}/tasks") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity> getUserTasks(@PathVariable Integer id) { + Map tasks = adminUserService.getUserTasks(id); + return ResponseEntity.ok(tasks); + } + + @PatchMapping("/{id}/ban") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity setUserBanned( + @PathVariable Integer id, + @RequestBody Map body) { + Boolean banned = body != null ? body.get("banned") : null; + if (banned == null) { + return ResponseEntity.badRequest().body(Map.of("error", "banned is required (true/false)")); + } + try { + adminUserService.setBanned(id, banned); + return ResponseEntity.ok().build(); + } catch (IllegalArgumentException e) { + return ResponseEntity.notFound().build(); + } + } + + @PatchMapping("/{id}/withdrawals-enabled") + @PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')") + public ResponseEntity setWithdrawalsEnabled( + @PathVariable Integer id, + @RequestBody Map body) { + Boolean enabled = body != null ? body.get("enabled") : null; + if (enabled == null) { + return ResponseEntity.badRequest().body(Map.of("error", "enabled is required (true/false)")); + } + try { + adminUserService.setWithdrawalsEnabled(id, enabled); + return ResponseEntity.ok().build(); + } catch (IllegalArgumentException e) { + return ResponseEntity.notFound().build(); + } + } + + @PostMapping("/{id}/balance/adjust") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity adjustBalance( + @PathVariable Integer id, + @Valid @RequestBody com.lottery.lottery.dto.BalanceAdjustmentRequest request) { + try { + com.lottery.lottery.dto.BalanceAdjustmentResponse response = adminUserService.adjustBalance(id, request); + return ResponseEntity.ok(response); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); + } + } +} + diff --git a/src/main/java/com/honey/honey/controller/AuthController.java b/src/main/java/com/lottery/lottery/controller/AuthController.java similarity index 68% rename from src/main/java/com/honey/honey/controller/AuthController.java rename to src/main/java/com/lottery/lottery/controller/AuthController.java index 3cedc89..a72cb77 100644 --- a/src/main/java/com/honey/honey/controller/AuthController.java +++ b/src/main/java/com/lottery/lottery/controller/AuthController.java @@ -1,17 +1,21 @@ -package com.honey.honey.controller; +package com.lottery.lottery.controller; -import com.honey.honey.dto.CreateSessionRequest; -import com.honey.honey.dto.CreateSessionResponse; -import com.honey.honey.model.UserA; -import com.honey.honey.service.SessionService; -import com.honey.honey.service.TelegramAuthService; -import com.honey.honey.service.UserService; +import com.lottery.lottery.dto.CreateSessionRequest; +import com.lottery.lottery.dto.CreateSessionResponse; +import com.lottery.lottery.exception.BannedUserException; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.service.SessionService; +import com.lottery.lottery.service.TelegramAuthService; +import com.lottery.lottery.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; import java.util.Map; @Slf4j @@ -23,6 +27,7 @@ public class AuthController { private final TelegramAuthService telegramAuthService; private final SessionService sessionService; private final UserService userService; + private final LocalizationService localizationService; /** * Creates a session by validating Telegram initData. @@ -36,19 +41,25 @@ public class AuthController { String initData = request.getInitData(); if (initData == null || initData.isBlank()) { - throw new IllegalArgumentException("initData is required"); + throw new IllegalArgumentException(localizationService.getMessage("auth.error.initDataRequired")); } // Validate Telegram initData signature and parse data Map tgUserData = telegramAuthService.validateAndParseInitData(initData); // Get or create user (handles registration, login update, and referral system) + // Note: Referral handling is done via bot registration endpoint, not through WebApp initData UserA user = userService.getOrCreateUser(tgUserData, httpRequest); + if (user.getBanned() != null && user.getBanned() == 1) { + String message = localizationService.getMessageForUser(user.getId(), "auth.error.accessRestricted"); + throw new BannedUserException(message); + } + // Create session String sessionId = sessionService.createSession(user); - log.info("Session created for userId={}, telegramId={}", user.getId(), user.getTelegramId()); + log.debug("Session created: userId={}", user.getId()); return CreateSessionResponse.builder() .access_token(sessionId) @@ -71,7 +82,7 @@ public class AuthController { String sessionId = extractBearerToken(authHeader); if (sessionId != null) { sessionService.invalidateSession(sessionId); - log.info("Session invalidated via logout"); + log.debug("Session invalidated via logout"); } } diff --git a/src/main/java/com/lottery/lottery/controller/DepositWebhookController.java b/src/main/java/com/lottery/lottery/controller/DepositWebhookController.java new file mode 100644 index 0000000..22b8349 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/DepositWebhookController.java @@ -0,0 +1,56 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.ExternalDepositWebhookRequest; +import com.lottery.lottery.service.PaymentService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +/** + * Controller for 3rd party deposit completion webhook. + * Path: POST /api/deposit_webhook/{token}. Token must match app.deposit-webhook.token (APP_DEPOSIT_WEBHOOK_TOKEN). + * No session auth; token in path only. Set the token on VPS via environment variable. + */ +@Slf4j +@RestController +@RequestMapping("/api/deposit_webhook") +@RequiredArgsConstructor +public class DepositWebhookController { + + @Value("${app.deposit-webhook.token:}") + private String expectedToken; + + private final PaymentService paymentService; + + /** + * Called by 3rd party when a user's crypto deposit was successful. + * Body: user_id (internal id from db_users_a), usd_amount (decimal, e.g. 1.45). + */ + @PostMapping("/{token}") + public ResponseEntity onDepositCompleted( + @PathVariable String token, + @RequestBody ExternalDepositWebhookRequest request) { + if (expectedToken.isEmpty() || !expectedToken.equals(token)) { + log.warn("Deposit webhook rejected: invalid token"); + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + if (request == null || request.getUserId() == null || request.getUsdAmount() == null) { + return ResponseEntity.badRequest().build(); + } + try { + paymentService.processExternalDepositCompletion( + request.getUserId(), + request.getUsdAmount()); + return ResponseEntity.ok().build(); + } catch (IllegalArgumentException e) { + log.warn("Deposit webhook rejected: {}", e.getMessage()); + return ResponseEntity.badRequest().build(); + } catch (Exception e) { + log.error("Deposit webhook error: userId={}, usdAmount={}", request.getUserId(), request.getUsdAmount(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } +} diff --git a/src/main/java/com/lottery/lottery/controller/GameController.java b/src/main/java/com/lottery/lottery/controller/GameController.java new file mode 100644 index 0000000..a95ca38 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/GameController.java @@ -0,0 +1,99 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.CompletedRoundDto; +import com.lottery.lottery.dto.GameHistoryEntryDto; +import com.lottery.lottery.model.GameRound; +import com.lottery.lottery.repository.GameRoundRepository; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.service.AvatarService; +import com.lottery.lottery.service.GameHistoryService; +import com.lottery.lottery.repository.UserARepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@RestController +@RequestMapping("/api/game") +@RequiredArgsConstructor +public class GameController { + + private final GameRoundRepository gameRoundRepository; + private final UserARepository userARepository; + private final AvatarService avatarService; + private final GameHistoryService gameHistoryService; + + /** + * Gets the last 10 completed rounds for a specific room. + * Fetches data from game_rounds table only. + */ + @GetMapping("/room/{roomNumber}/completed-rounds") + public ResponseEntity> getCompletedRounds( + @PathVariable Integer roomNumber + ) { + List rounds = gameRoundRepository.findLastCompletedRoundsByRoomNumber( + roomNumber, + PageRequest.of(0, 10) + ); + + List completedRounds = rounds.stream() + .map(round -> { + // Calculate winner's chance from game_rounds table data + Double winChance = null; + if (round.getWinnerBet() != null && round.getTotalBet() != null && round.getTotalBet() > 0) { + winChance = ((double) round.getWinnerBet() / round.getTotalBet()) * 100.0; + } + + // Get winner's screen name and avatar + String screenName = null; + String avatarUrl = null; + if (round.getWinnerUserId() != null) { + screenName = userARepository.findById(round.getWinnerUserId()) + .map(userA -> userA.getScreenName()) + .orElse(null); + avatarUrl = avatarService.getAvatarUrl(round.getWinnerUserId()); + } + + return CompletedRoundDto.builder() + .roundId(round.getId()) + .winnerUserId(round.getWinnerUserId()) + .winnerScreenName(screenName) + .winnerAvatarUrl(avatarUrl) + .winnerBet(round.getWinnerBet()) + .payout(round.getPayout()) + .totalBet(round.getTotalBet()) + .winChance(winChance) + .resolvedAt(round.getResolvedAt() != null ? round.getResolvedAt().toEpochMilli() : null) + .build(); + }) + .collect(Collectors.toList()); + + return ResponseEntity.ok(completedRounds); + } + + /** + * Gets WIN transactions for the current user from the last 30 days with pagination. + * + * @param page Page number (0-indexed, default 0) + * @param timezone Optional timezone (e.g., "Europe/London"). If not provided, uses UTC. + */ + @GetMapping("/history") + public ResponseEntity> getUserGameHistory( + @RequestParam(defaultValue = "0") int page, + @RequestParam(required = false) String timezone) { + Integer userId = UserContext.get().getId(); + com.lottery.lottery.model.UserA user = UserContext.get(); + String languageCode = user.getLanguageCode(); + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + org.springframework.data.domain.Page history = gameHistoryService.getUserGameHistory(userId, page, timezone, languageCode); + return ResponseEntity.ok(history); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/GameWebSocketController.java b/src/main/java/com/lottery/lottery/controller/GameWebSocketController.java new file mode 100644 index 0000000..cbea96f --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/GameWebSocketController.java @@ -0,0 +1,184 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.config.WebSocketAuthInterceptor; +import com.lottery.lottery.dto.BalanceUpdateDto; +import com.lottery.lottery.dto.GameRoomStateDto; +import com.lottery.lottery.dto.JoinRoundRequest; +import com.lottery.lottery.exception.GameException; +import com.lottery.lottery.service.GameRoomService; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.service.UserService; +import jakarta.annotation.PostConstruct; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import org.springframework.messaging.handler.annotation.MessageExceptionHandler; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.messaging.simp.annotation.SubscribeMapping; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class GameWebSocketController { + + private final GameRoomService gameRoomService; + private final SimpMessagingTemplate messagingTemplate; + private final UserService userService; + private final LocalizationService localizationService; + + // Track which users are subscribed to which rooms + private final Map userRoomSubscriptions = new ConcurrentHashMap<>(); + + // Track winners who have already received balance updates (to avoid duplicates) + private final Map notifiedWinners = new ConcurrentHashMap<>(); // roomNumber -> winnerUserId + + /** + * Initializes the controller and sets up balance update callback. + */ + @PostConstruct + public void init() { + // Set callback for balance update notifications + gameRoomService.setBalanceUpdateCallback(this::notifyBalanceUpdate); + + // Set callback for state broadcast notifications (event-driven) + gameRoomService.setStateBroadcastCallback(this::broadcastRoomState); + } + + /** + * Notifies a user about balance update. + * Called by GameRoomService for single participant refunds (no spin, so immediate update is fine). + */ + private void notifyBalanceUpdate(Integer userId) { + String username = String.valueOf(userId); + sendBalanceUpdate(username, userId); + } + + /** + * Handles join round request from client. + */ + @MessageMapping("/game/join") + public void joinRound(@Valid @Payload JoinRoundRequest request, WebSocketAuthInterceptor.StompPrincipal principal) { + Integer userId = principal.getUserId(); + + // Additional validation beyond @Valid annotations + // @Valid handles null checks and basic constraints, but we add explicit checks for clarity + if (request == null) { + throw new GameException(localizationService.getMessage("game.error.invalidRequest")); + } + + // Validate room number range (1-3) + // This is also covered by @Min/@Max, but explicit check provides better error message + if (request.getRoomNumber() == null || request.getRoomNumber() < 1 || request.getRoomNumber() > 3) { + throw new GameException(localizationService.getMessage("game.error.roomNumberInvalid")); + } + + // Validate bet amount is positive (also covered by @Positive, but explicit for clarity) + if (request.getBetAmount() == null || request.getBetAmount() <= 0) { + throw new GameException(localizationService.getMessage("game.error.betMustBePositive")); + } + + try { + // Join the round + GameRoomStateDto state = gameRoomService.joinRound(userId, request.getRoomNumber(), request.getBetAmount()); + + // Track subscription + userRoomSubscriptions.put(userId, request.getRoomNumber()); + + // Send balance update to the user who joined + sendBalanceUpdate(principal.getName(), userId); + + // State is already broadcast by GameRoomService.joinRound() via callback (event-driven) + // No need to broadcast again here + + } catch (GameException e) { + // User-friendly error message + sendErrorToUser(principal.getName(), e.getUserMessage()); + } catch (Exception e) { + // Generic error - don't expose technical details + log.error("Unexpected error joining round for user {}", userId, e); + sendErrorToUser(principal.getName(), localizationService.getMessage("common.error.unknown")); + } + } + + /** + * Sends error message to user. + */ + private void sendErrorToUser(String username, String errorMessage) { + messagingTemplate.convertAndSendToUser( + username, + "/queue/errors", + Map.of("error", errorMessage) + ); + } + + /** + * Global exception handler for WebSocket messages. + */ + @MessageExceptionHandler + public void handleException(Exception ex, WebSocketAuthInterceptor.StompPrincipal principal) { + String userMessage; + + if (ex instanceof GameException) { + userMessage = ((GameException) ex).getUserMessage(); + } else if (ex instanceof ConstraintViolationException) { + // Handle validation errors from @Valid annotation + ConstraintViolationException cve = (ConstraintViolationException) ex; + userMessage = cve.getConstraintViolations().stream() + .map(ConstraintViolation::getMessage) + .findFirst() + .orElse("Validation failed. Please check your input."); + log.warn("Validation error for user {}: {}", principal.getUserId(), userMessage); + } else { + log.error("Unexpected WebSocket error", ex); + userMessage = localizationService.getMessage("common.error.unknown"); + } + + sendErrorToUser(principal.getName(), userMessage); + } + + /** + * Sends current room state when client subscribes. + * Note: SubscribeMapping doesn't support path variables well, so we'll handle subscription in joinRound + */ + + /** + * Broadcasts room state to all subscribers. + * Called by GameRoomService via callback (event-driven). + */ + public void broadcastRoomState(Integer roomNumber, GameRoomStateDto state) { + messagingTemplate.convertAndSend("/topic/room/" + roomNumber, state); + } + + /** + * Sends balance update to a specific user. + */ + private void sendBalanceUpdate(String username, Integer userId) { + try { + // Get current balance from database + Long balance = userService.getUserBalance(userId); + if (balance != null) { + BalanceUpdateDto balanceUpdate = BalanceUpdateDto.builder() + .balanceA(balance) + .build(); + messagingTemplate.convertAndSendToUser( + username, + "/queue/balance", + balanceUpdate + ); + } + } catch (Exception e) { + log.error("Failed to send balance update to user {}", userId, e); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/NotifyBroadcastController.java b/src/main/java/com/lottery/lottery/controller/NotifyBroadcastController.java new file mode 100644 index 0000000..931839a --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/NotifyBroadcastController.java @@ -0,0 +1,56 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.NotifyBroadcastRequest; +import com.lottery.lottery.service.NotificationBroadcastService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +/** + * Public API to trigger or stop notification broadcast. Token in path; no secrets in codebase. + * Set APP_NOTIFY_BROADCAST_TOKEN on VPS. + */ +@Slf4j +@RestController +@RequestMapping("/api/notify_broadcast") +@RequiredArgsConstructor +public class NotifyBroadcastController { + + @Value("${app.notify-broadcast.token:}") + private String expectedToken; + + private final NotificationBroadcastService notificationBroadcastService; + + @PostMapping("/{token}") + public ResponseEntity start( + @PathVariable String token, + @RequestBody(required = false) NotifyBroadcastRequest body) { + if (expectedToken.isEmpty() || !expectedToken.equals(token)) { + log.warn("Notify broadcast rejected: invalid token"); + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + NotifyBroadcastRequest req = body != null ? body : new NotifyBroadcastRequest(); + notificationBroadcastService.runBroadcast( + req.getMessage(), + req.getImageUrl(), + req.getVideoUrl(), + req.getUserIdFrom(), + req.getUserIdTo(), + req.getButtonText(), + req.getIgnoreBlocked()); + return ResponseEntity.accepted().build(); + } + + @PostMapping("/{token}/stop") + public ResponseEntity stop(@PathVariable String token) { + if (expectedToken.isEmpty() || !expectedToken.equals(token)) { + log.warn("Notify broadcast stop rejected: invalid token"); + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + notificationBroadcastService.requestStop(); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/PaymentController.java b/src/main/java/com/lottery/lottery/controller/PaymentController.java new file mode 100644 index 0000000..ca5e496 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/PaymentController.java @@ -0,0 +1,237 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.CryptoWithdrawalResponse; +import com.lottery.lottery.dto.CreateCryptoWithdrawalRequest; +import com.lottery.lottery.dto.CreatePaymentRequest; +import com.lottery.lottery.dto.DepositAddressRequest; +import com.lottery.lottery.dto.DepositAddressResultDto; +import com.lottery.lottery.dto.DepositMethodsDto; +import com.lottery.lottery.dto.ErrorResponse; +import com.lottery.lottery.dto.PaymentInvoiceResponse; +import com.lottery.lottery.model.Payout; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.dto.WithdrawalMethodDetailsDto; +import com.lottery.lottery.dto.WithdrawalMethodsDto; +import com.lottery.lottery.service.CryptoDepositService; +import com.lottery.lottery.service.CryptoWithdrawalService; +import com.lottery.lottery.service.FeatureSwitchService; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.service.PaymentService; +import com.lottery.lottery.service.PayoutService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequestMapping("/api/payments") +@RequiredArgsConstructor +public class PaymentController { + + private final PaymentService paymentService; + private final CryptoDepositService cryptoDepositService; + private final CryptoWithdrawalService cryptoWithdrawalService; + private final PayoutService payoutService; + private final FeatureSwitchService featureSwitchService; + private final LocalizationService localizationService; + + /** + * Returns minimum deposit from DB only (no sync). Used by Store screen for validation. + */ + @GetMapping("/minimum-deposit") + public ResponseEntity getMinimumDeposit() { + if (!featureSwitchService.isPaymentEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable"))); + } + return ResponseEntity.ok(java.util.Map.of("minimumDeposit", cryptoDepositService.getMinimumDeposit())); + } + + /** + * Returns crypto deposit methods and minimum_deposit from DB only (sync is done every 10 min). + * Called when user opens Payment Options screen. + */ + @GetMapping("/deposit-methods") + public ResponseEntity getDepositMethods() { + if (!featureSwitchService.isPaymentEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable"))); + } + DepositMethodsDto dto = cryptoDepositService.getDepositMethodsFromDb(); + return ResponseEntity.ok(dto); + } + + /** + * Returns crypto withdrawal methods from DB only (sync is done every 30 min). + * Called when user opens Payout screen. + */ + @GetMapping("/withdrawal-methods") + public ResponseEntity getWithdrawalMethods() { + if (!featureSwitchService.isPayoutEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse(localizationService.getMessage("feature.payoutsUnavailable"))); + } + WithdrawalMethodsDto dto = cryptoWithdrawalService.getWithdrawalMethodsFromDb(); + return ResponseEntity.ok(dto); + } + + /** + * Returns withdrawal method details (rate_usd, misha_fee_usd) from external API for the given pid. + * Called when user opens Payout Confirmation screen to show network fee and compute "You will receive". + */ + @GetMapping("/withdrawal-method-details") + public ResponseEntity getWithdrawalMethodDetails(@RequestParam("pid") int pid) { + if (!featureSwitchService.isPayoutEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse(localizationService.getMessage("feature.payoutsUnavailable"))); + } + return cryptoWithdrawalService.getWithdrawalMethodDetails(pid) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * Creates a crypto withdrawal: calls external API, then on success creates payout and deducts balance. + * Uses in-memory lock to prevent double-submit. Validates deposit total and maxWinAfterDeposit. + */ + @PostMapping("/crypto-withdrawal") + public ResponseEntity createCryptoWithdrawal(@RequestBody CreateCryptoWithdrawalRequest request) { + if (!featureSwitchService.isPayoutEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse(localizationService.getMessage("feature.payoutsUnavailable"))); + } + try { + UserA user = UserContext.get(); + if (user == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse("Authentication required")); + } + Payout payout = payoutService.createCryptoPayout(user.getId(), request); + return ResponseEntity.ok(CryptoWithdrawalResponse.builder() + .id(payout.getId()) + .status(payout.getStatus().name()) + .build()); + } catch (IllegalArgumentException e) { + log.warn("Crypto withdrawal validation failed: {}", e.getMessage()); + return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); + } catch (IllegalStateException e) { + log.warn("Crypto withdrawal failed: {}", e.getMessage()); + return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); + } catch (Exception e) { + log.error("Crypto withdrawal error: {}", e.getMessage(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("Withdrawal failed. Please try again later.")); + } + } + + /** + * Creates a payment invoice for the current user. + * Returns invoice data that frontend will use to open Telegram payment UI. + */ + @PostMapping("/create") + public ResponseEntity createPaymentInvoice(@RequestBody CreatePaymentRequest request) { + if (!featureSwitchService.isPaymentEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable"))); + } + try { + UserA user = UserContext.get(); + PaymentInvoiceResponse response = paymentService.createPaymentInvoice(user.getId(), request); + return ResponseEntity.ok(response); + } catch (IllegalArgumentException e) { + log.warn("Payment invoice creation failed: {}", e.getMessage()); + return ResponseEntity.badRequest() + .body(new ErrorResponse(e.getMessage())); + } catch (Exception e) { + log.error("Payment invoice creation error: {}", e.getMessage(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("Failed to create payment invoice: " + e.getMessage())); + } + } + + + /** + * Gets a crypto deposit address from the external API (no payment record is created). + * Call when user selects a payment method on Payment Options screen. + * Returns address, amount_coins, name, network for the Payment Confirmation screen. + */ + @PostMapping("/deposit-address") + public ResponseEntity getDepositAddress(@RequestBody DepositAddressRequest request) { + if (!featureSwitchService.isPaymentEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable"))); + } + try { + UserA user = UserContext.get(); + if (user == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse("Authentication required")); + } + DepositAddressResultDto result = paymentService.requestCryptoDepositAddress( + user.getId(), request.getPid(), request.getUsdAmount()); + return ResponseEntity.ok(result); + } catch (IllegalArgumentException e) { + log.warn("Deposit address request failed: {}", e.getMessage()); + return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); + } catch (Exception e) { + log.error("Deposit address error: {}", e.getMessage(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse(e.getMessage() != null ? e.getMessage() : "Failed to get deposit address")); + } + } + + /** + * Cancels a payment (e.g., when user cancels in Telegram UI). + */ + @PostMapping("/cancel") + public ResponseEntity cancelPayment(@RequestBody CancelPaymentRequest request) { + try { + String orderId = request.getOrderId(); + UserA caller = UserContext.get(); + log.info("Payment cancel requested: orderId={}, callerUserId={}", orderId, caller != null ? caller.getId() : null); + paymentService.cancelPayment(orderId); + return ResponseEntity.ok().body(new PaymentWebhookResponse(true, "Payment cancelled")); + } catch (IllegalArgumentException e) { + log.warn("Payment cancellation failed: {}", e.getMessage()); + return ResponseEntity.badRequest() + .body(new ErrorResponse(e.getMessage())); + } catch (Exception e) { + log.error("Payment cancellation error: {}", e.getMessage(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("Failed to cancel payment: " + e.getMessage())); + } + } + + // Response DTOs + private static class PaymentWebhookResponse { + private final boolean success; + private final String message; + + public PaymentWebhookResponse(boolean success, String message) { + this.success = success; + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public String getMessage() { + return message; + } + } + + private static class CancelPaymentRequest { + private String orderId; + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/PayoutController.java b/src/main/java/com/lottery/lottery/controller/PayoutController.java new file mode 100644 index 0000000..c57c89e --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/PayoutController.java @@ -0,0 +1,66 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.CreatePayoutRequest; +import com.lottery.lottery.dto.ErrorResponse; +import com.lottery.lottery.dto.PayoutHistoryEntryDto; +import com.lottery.lottery.dto.PayoutResponse; +import com.lottery.lottery.model.Payout; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.service.PayoutService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/api/payouts") +@RequiredArgsConstructor +public class PayoutController { + + private final PayoutService payoutService; + + /** + * Creates a payout request for the current user. + * Validates input and deducts balance if validation passes. + */ + @PostMapping + public ResponseEntity createPayout(@RequestBody CreatePayoutRequest request) { + try { + UserA user = UserContext.get(); + Payout payout = payoutService.createPayout(user.getId(), request); + PayoutResponse response = payoutService.toResponse(payout); + return ResponseEntity.ok(response); + } catch (IllegalArgumentException e) { + log.warn("Payout validation failed: {}", e.getMessage()); + return ResponseEntity.badRequest() + .body(new ErrorResponse(e.getMessage())); + } catch (IllegalStateException e) { + log.error("Payout creation failed: {}", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse(e.getMessage())); + } + } + + + /** + * Gets the last 20 payout history entries for the current user. + * + * @param timezone Optional timezone (e.g., "Europe/London"). If not provided, uses UTC. + */ + @GetMapping("/history") + public List getUserPayoutHistory( + @RequestParam(required = false) String timezone) { + UserA user = UserContext.get(); + String languageCode = user.getLanguageCode(); + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + return payoutService.getUserPayoutHistory(user.getId(), timezone, languageCode); + } +} + diff --git a/src/main/java/com/honey/honey/controller/PingController.java b/src/main/java/com/lottery/lottery/controller/PingController.java similarity index 90% rename from src/main/java/com/honey/honey/controller/PingController.java rename to src/main/java/com/lottery/lottery/controller/PingController.java index 7094247..dbb9cf1 100644 --- a/src/main/java/com/honey/honey/controller/PingController.java +++ b/src/main/java/com/lottery/lottery/controller/PingController.java @@ -1,4 +1,4 @@ -package com.honey.honey.controller; +package com.lottery.lottery.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -17,3 +17,4 @@ public class PingController { } } + diff --git a/src/main/java/com/lottery/lottery/controller/PromotionController.java b/src/main/java/com/lottery/lottery/controller/PromotionController.java new file mode 100644 index 0000000..f92f75c --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/PromotionController.java @@ -0,0 +1,43 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.PromotionDetailDto; +import com.lottery.lottery.dto.PromotionListItemDto; +import com.lottery.lottery.service.FeatureSwitchService; +import com.lottery.lottery.service.PublicPromotionService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * Public API for the lottery app: list and view promotion details (leaderboard, user progress). + * Excludes INACTIVE promotions. Requires Bearer auth (app user). + * When promotions feature switch is false, all endpoints return 404. + */ +@RestController +@RequestMapping("/api/promotions") +@RequiredArgsConstructor +public class PromotionController { + + private final PublicPromotionService publicPromotionService; + private final FeatureSwitchService featureSwitchService; + + @GetMapping + public ResponseEntity> list() { + if (!featureSwitchService.isPromotionsEnabled()) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(publicPromotionService.listForApp()); + } + + @GetMapping("/{id}") + public ResponseEntity getDetail(@PathVariable int id) { + if (!featureSwitchService.isPromotionsEnabled()) { + return ResponseEntity.notFound().build(); + } + return publicPromotionService.getDetailForApp(id) + .map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.notFound().build()); + } +} diff --git a/src/main/java/com/lottery/lottery/controller/QuickAnswerController.java b/src/main/java/com/lottery/lottery/controller/QuickAnswerController.java new file mode 100644 index 0000000..b1ba87a --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/QuickAnswerController.java @@ -0,0 +1,134 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.QuickAnswerCreateRequest; +import com.lottery.lottery.dto.QuickAnswerDto; +import com.lottery.lottery.model.Admin; +import com.lottery.lottery.model.QuickAnswer; +import com.lottery.lottery.repository.AdminRepository; +import com.lottery.lottery.repository.QuickAnswerRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/admin/quick-answers") +@RequiredArgsConstructor +@PreAuthorize("hasAnyRole('ADMIN', 'TICKETS_SUPPORT', 'GAME_ADMIN')") +public class QuickAnswerController { + + private final QuickAnswerRepository quickAnswerRepository; + private final AdminRepository adminRepository; + + /** + * Get current admin from authentication context + */ + private Admin getCurrentAdmin() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = authentication.getName(); + return adminRepository.findByUsername(username) + .orElseThrow(() -> new RuntimeException("Admin not found: " + username)); + } + + /** + * Get all quick answers for the current admin + */ + @GetMapping + public ResponseEntity> getQuickAnswers() { + Admin admin = getCurrentAdmin(); + List quickAnswers = quickAnswerRepository.findByAdminIdOrderByCreatedAtDesc(admin.getId()); + List dtos = quickAnswers.stream() + .map(qa -> new QuickAnswerDto( + qa.getId(), + qa.getText(), + qa.getCreatedAt(), + qa.getUpdatedAt() + )) + .collect(Collectors.toList()); + return ResponseEntity.ok(dtos); + } + + /** + * Create a new quick answer for the current admin + */ + @PostMapping + public ResponseEntity createQuickAnswer(@Valid @RequestBody QuickAnswerCreateRequest request) { + Admin admin = getCurrentAdmin(); + + if (request.getText() == null || request.getText().trim().isEmpty()) { + return ResponseEntity.badRequest().build(); + } + + QuickAnswer quickAnswer = QuickAnswer.builder() + .admin(admin) + .text(request.getText().trim()) + .build(); + + QuickAnswer saved = quickAnswerRepository.save(quickAnswer); + QuickAnswerDto dto = new QuickAnswerDto( + saved.getId(), + saved.getText(), + saved.getCreatedAt(), + saved.getUpdatedAt() + ); + return ResponseEntity.status(HttpStatus.CREATED).body(dto); + } + + /** + * Update a quick answer (only if it belongs to the current admin) + */ + @PutMapping("/{id}") + public ResponseEntity updateQuickAnswer( + @PathVariable Integer id, + @Valid @RequestBody QuickAnswerCreateRequest request) { + Admin admin = getCurrentAdmin(); + QuickAnswer quickAnswer = quickAnswerRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Quick answer not found")); + + // Verify that the quick answer belongs to the current admin + if (!quickAnswer.getAdmin().getId().equals(admin.getId())) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + if (request.getText() == null || request.getText().trim().isEmpty()) { + return ResponseEntity.badRequest().build(); + } + + quickAnswer.setText(request.getText().trim()); + QuickAnswer saved = quickAnswerRepository.save(quickAnswer); + + QuickAnswerDto dto = new QuickAnswerDto( + saved.getId(), + saved.getText(), + saved.getCreatedAt(), + saved.getUpdatedAt() + ); + return ResponseEntity.ok(dto); + } + + /** + * Delete a quick answer (only if it belongs to the current admin) + */ + @DeleteMapping("/{id}") + public ResponseEntity deleteQuickAnswer(@PathVariable Integer id) { + Admin admin = getCurrentAdmin(); + QuickAnswer quickAnswer = quickAnswerRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Quick answer not found")); + + // Verify that the quick answer belongs to the current admin + if (!quickAnswer.getAdmin().getId().equals(admin.getId())) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + quickAnswerRepository.delete(quickAnswer); + return ResponseEntity.noContent().build(); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/RemoteBetController.java b/src/main/java/com/lottery/lottery/controller/RemoteBetController.java new file mode 100644 index 0000000..78a9272 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/RemoteBetController.java @@ -0,0 +1,191 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.exception.GameException; +import com.lottery.lottery.service.FeatureSwitchService; +import com.lottery.lottery.service.GameRoomService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * Unauthenticated API for 3rd party to register a user into a round (remote bet). + * Protected by a shared token in the path and a runtime feature switch. + * Same business logic as in-app join (balance, commissions, transactions, visibility in app). + */ +@Slf4j +@RestController +@RequestMapping("/api/remotebet") +@RequiredArgsConstructor +public class RemoteBetController { + + private static final long TICKETS_TO_BIGINT = 1_000_000L; + + @Value("${app.remote-bet.token:}") + private String configuredToken; + + private final FeatureSwitchService featureSwitchService; + private final GameRoomService gameRoomService; + + /** + * Registers the user to the current round in the given room with the given bet. + * GET /api/remotebet/{token}?user_id=228&room=2&amount=5&unique=false + * Or with random range: at least one of rand_min or rand_max (amount ignored). + * - user_id: db_users_a.id + * - room: room number (1, 2, or 3) + * - amount: bet in tickets. Ignored when rand_min and/or rand_max are provided. + * - unique: optional. If true, user can only have one bet per room per round (repeated calls no-op). + * - rand_min: optional. If only rand_min: random between rand_min and room max. If both: random between rand_min and rand_max. + * - rand_max: optional. If only rand_max: random between room min and rand_max. If both: random between rand_min and rand_max. + * Params are validated against room min/max (rand_min >= room min, rand_max <= room max; when both, rand_min <= rand_max). + */ + @GetMapping("/{token}") + public ResponseEntity remoteBet( + @PathVariable String token, + @RequestParam(name = "user_id") Integer userId, + @RequestParam(name = "room") Integer room, + @RequestParam(name = "amount") Integer amountTickets, + @RequestParam(name = "unique", required = false) Boolean unique, + @RequestParam(name = "rand_min", required = false) Integer randMin, + @RequestParam(name = "rand_max", required = false) Integer randMax) { + + if (configuredToken == null || configuredToken.isEmpty() || !configuredToken.equals(token)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + if (!featureSwitchService.isRemoteBetEnabled()) { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build(); + } + + boolean useRandomRange = randMin != null || randMax != null; + long betAmount; + int amountTicketsForLog; + + if (useRandomRange) { + GameRoomService.BetLimits limits = GameRoomService.getBetLimitsForRoom(room); + long roomMinTickets = limits.minBet() / TICKETS_TO_BIGINT; + long roomMaxTickets = limits.maxBet() / TICKETS_TO_BIGINT; + + long effectiveMinTickets; + long effectiveMaxTickets; + + if (randMin != null && randMax != null) { + if (randMin < roomMinTickets) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "rand_min must not be lower than room min bet (" + roomMinTickets + " tickets)")); + } + if (randMax > roomMaxTickets) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "rand_max must not be higher than room max bet (" + roomMaxTickets + " tickets)")); + } + if (randMin > randMax) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "rand_min must be less than or equal to rand_max")); + } + effectiveMinTickets = randMin; + effectiveMaxTickets = randMax; + } else if (randMin != null) { + if (randMin < roomMinTickets) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "rand_min must not be lower than room min bet (" + roomMinTickets + " tickets)")); + } + if (randMin > roomMaxTickets) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "rand_min must not be higher than room max bet (" + roomMaxTickets + " tickets)")); + } + effectiveMinTickets = randMin; + effectiveMaxTickets = roomMaxTickets; + } else { + if (randMax < roomMinTickets) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "rand_max must not be lower than room min bet (" + roomMinTickets + " tickets)")); + } + if (randMax > roomMaxTickets) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "rand_max must not be higher than room max bet (" + roomMaxTickets + " tickets)")); + } + effectiveMinTickets = roomMinTickets; + effectiveMaxTickets = randMax; + } + + long currentUserBetBigint = gameRoomService.getCurrentUserBetInRoom(userId, room); + long maxAdditionalBigint = Math.max(0L, limits.maxBet() - currentUserBetBigint); + long maxAdditionalTickets = maxAdditionalBigint / TICKETS_TO_BIGINT; + if (maxAdditionalTickets < limits.minBet() / TICKETS_TO_BIGINT) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "Max bet for this room already reached")); + } + + effectiveMaxTickets = Math.min(effectiveMaxTickets, maxAdditionalTickets); + if (effectiveMinTickets > effectiveMaxTickets) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "Random range exceeds remaining bet capacity for this room")); + } + + // Room 1: any integer; Room 2: divisible by 10; Room 3: divisible by 100 + long step = room == 2 ? 10L : (room == 3 ? 100L : 1L); + long minAligned = roundUpToMultiple(effectiveMinTickets, step); + long maxAligned = roundDownToMultiple(effectiveMaxTickets, step); + if (minAligned > maxAligned) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, 0, + "No valid random value in range for room " + room + " (room 2 must be multiple of 10, room 3 multiple of 100)")); + } + long randomTickets = minAligned >= maxAligned + ? minAligned + : minAligned + step * ThreadLocalRandom.current().nextLong(0, (maxAligned - minAligned) / step + 1); + betAmount = randomTickets * TICKETS_TO_BIGINT; + amountTicketsForLog = (int) randomTickets; + } else { + betAmount = (long) amountTickets * TICKETS_TO_BIGINT; + amountTicketsForLog = amountTickets; + } + + boolean uniqueBet = Boolean.TRUE.equals(unique); + try { + var result = gameRoomService.joinRoundWithResult(userId, room, betAmount, uniqueBet); + var state = result.getState(); + Long roundId = state.getRoundId(); + int betTicketsForResponse = result.getBetTicketsForResponse(); + String randRangeLog = useRandomRange ? (randMin != null ? randMin : "roomMin") + "-" + (randMax != null ? randMax : "roomMax") : "no"; + log.info("Remote bet: user connected to round remotely, userId={}, roundId={}, roomId={}, betTickets={}, unique={}, randRange={}", + userId, roundId, room, betTicketsForResponse, uniqueBet, randRangeLog); + return ResponseEntity.ok(new RemoteBetResponse(true, roundId != null ? roundId.intValue() : null, room, betTicketsForResponse)); + } catch (GameException e) { + return ResponseEntity.badRequest().body(new RemoteBetResponse(false, null, room, amountTicketsForLog, e.getUserMessage())); + } catch (Exception e) { + log.warn("Remote bet failed for userId={}, room={}, amount={}", userId, room, amountTicketsForLog, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new RemoteBetResponse(false, null, room, amountTicketsForLog, "Internal error")); + } + } + + @lombok.Data + @lombok.AllArgsConstructor + @lombok.NoArgsConstructor + public static class RemoteBetResponse { + private boolean success; + private Integer roundId; + private Integer room; + private Integer betTickets; + private String error; + + public RemoteBetResponse(boolean success, Integer roundId, Integer room, Integer betTickets) { + this(success, roundId, room, betTickets, null); + } + } + + /** Round value up to next multiple of step (e.g. 23, 10 -> 30). */ + private static long roundUpToMultiple(long value, long step) { + if (step <= 0) return value; + return ((value + step - 1) / step) * step; + } + + /** Round value down to previous multiple of step (e.g. 197, 10 -> 190). */ + private static long roundDownToMultiple(long value, long step) { + if (step <= 0) return value; + return (value / step) * step; + } +} diff --git a/src/main/java/com/lottery/lottery/controller/SupportController.java b/src/main/java/com/lottery/lottery/controller/SupportController.java new file mode 100644 index 0000000..636225b --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/SupportController.java @@ -0,0 +1,104 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.*; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.service.SupportTicketService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/api/support") +@RequiredArgsConstructor +public class SupportController { + + private final SupportTicketService supportTicketService; + + /** + * Creates a new support ticket with the first message. + */ + @PostMapping("/tickets") + public ResponseEntity createTicket( + @Valid @RequestBody CreateTicketRequest request) { + + UserA user = UserContext.get(); + TicketDto ticket = supportTicketService.createTicket( + user.getId(), + request + ); + + return ResponseEntity.status(HttpStatus.CREATED).body(ticket); + } + + /** + * Gets ticket history for the authenticated user (last 20 tickets). + */ + @GetMapping("/tickets") + public ResponseEntity> getTicketHistory() { + + UserA user = UserContext.get(); + List tickets = supportTicketService.getTicketHistory( + user.getId() + ); + + return ResponseEntity.ok(tickets); + } + + /** + * Gets ticket details with all messages. + */ + @GetMapping("/tickets/{ticketId}") + public ResponseEntity getTicketDetail( + @PathVariable Long ticketId) { + + UserA user = UserContext.get(); + TicketDetailDto ticket = supportTicketService.getTicketDetail( + user.getId(), + ticketId + ); + + return ResponseEntity.ok(ticket); + } + + /** + * Adds a message to an existing ticket. + */ + @PostMapping("/tickets/{ticketId}/messages") + public ResponseEntity addMessage( + @PathVariable Long ticketId, + @Valid @RequestBody CreateMessageRequest request) { + + UserA user = UserContext.get(); + MessageDto message = supportTicketService.addMessage( + user.getId(), + ticketId, + request + ); + + return ResponseEntity.status(HttpStatus.CREATED).body(message); + } + + /** + * Closes a ticket. + */ + @PostMapping("/tickets/{ticketId}/close") + public ResponseEntity closeTicket( + @PathVariable Long ticketId) { + + UserA user = UserContext.get(); + supportTicketService.closeTicket( + user.getId(), + ticketId + ); + + return ResponseEntity.ok().build(); + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/TaskController.java b/src/main/java/com/lottery/lottery/controller/TaskController.java new file mode 100644 index 0000000..651928c --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/TaskController.java @@ -0,0 +1,100 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.ClaimTaskResponse; +import com.lottery.lottery.dto.DailyBonusStatusDto; +import com.lottery.lottery.dto.RecentBonusClaimDto; +import com.lottery.lottery.dto.TaskDto; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.service.TaskService; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/api/tasks") +@RequiredArgsConstructor +public class TaskController { + + private final TaskService taskService; + private final LocalizationService localizationService; + + /** + * Gets all tasks for a specific type (referral, follow, other). + * Includes user progress and claim status. + */ + @GetMapping + public List getTasks(@RequestParam String type) { + UserA user = UserContext.get(); + String languageCode = user.getLanguageCode(); + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + return taskService.getTasksByType(user.getId(), type, languageCode); + } + + /** + * Gets daily bonus status for the current user. + * Returns availability status and cooldown time if on cooldown. + */ + @GetMapping("/daily-bonus") + public ResponseEntity getDailyBonusStatus() { + UserA user = UserContext.get(); + DailyBonusStatusDto status = taskService.getDailyBonusStatus(user.getId()); + return ResponseEntity.ok(status); + } + + /** + * Gets the 50 most recent daily bonus claims. + * Returns claims ordered by claimed_at DESC (most recent first). + * Includes user avatar URL, screen name, and formatted claim timestamp with timezone and localized "at" word. + * + * @param timezone Optional timezone (e.g., "Europe/Kiev"). If not provided, uses UTC. + */ + @GetMapping("/daily-bonus/recent-claims") + public ResponseEntity> getRecentDailyBonusClaims( + @RequestParam(required = false) String timezone) { + UserA user = UserContext.get(); + String languageCode = user.getLanguageCode(); + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + List claims = taskService.getRecentDailyBonusClaims(timezone, languageCode); + return ResponseEntity.ok(claims); + } + + /** + * Claims a task for the current user. + * Checks if task is completed and gives reward if applicable. + * Returns 200 with success status and message. + */ + @PostMapping("/claim") + public ResponseEntity claimTask(@RequestBody ClaimTaskRequest request) { + UserA user = UserContext.get(); + boolean claimed = taskService.claimTask(user.getId(), request.getTaskId()); + + if (claimed) { + return ResponseEntity.ok(ClaimTaskResponse.builder() + .success(true) + .message(localizationService.getMessage("task.message.claimed")) + .build()); + } else { + return ResponseEntity.ok(ClaimTaskResponse.builder() + .success(false) + .message(localizationService.getMessage("task.message.notCompleted")) + .build()); + } + } + + @Data + public static class ClaimTaskRequest { + private Integer taskId; + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/TelegramWebhookController.java b/src/main/java/com/lottery/lottery/controller/TelegramWebhookController.java new file mode 100644 index 0000000..f0e32b6 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/TelegramWebhookController.java @@ -0,0 +1,868 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.config.TelegramProperties; +import com.lottery.lottery.dto.TelegramApiResponse; +import com.lottery.lottery.dto.PaymentWebhookRequest; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.service.PaymentService; +import com.lottery.lottery.service.TelegramBotApiService; +import com.lottery.lottery.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.HttpClientErrorException; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.api.objects.Message; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.CallbackQuery; +import org.telegram.telegrambots.meta.api.objects.payments.PreCheckoutQuery; +import org.telegram.telegrambots.meta.api.objects.payments.SuccessfulPayment; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardButton; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow; +import org.telegram.telegrambots.meta.api.objects.webapp.WebAppInfo; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.config.LocaleConfig; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Value; +import java.util.List; +import java.util.ArrayList; +import java.util.Locale; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.util.StreamUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.core.io.ByteArrayResource; + +/** + * Webhook controller for receiving Telegram updates directly. + * Path: POST /api/telegram/webhook/{token}. Token must match APP_TELEGRAM_WEBHOOK_TOKEN. + */ +@Slf4j +@RestController +@RequestMapping("/api/telegram/webhook") +@RequiredArgsConstructor +public class TelegramWebhookController { + + @Value("${app.telegram-webhook.token:}") + private String expectedWebhookToken; + + private final UserService userService; + private final PaymentService paymentService; + private final TelegramProperties telegramProperties; + private final LocalizationService localizationService; + private final TelegramBotApiService telegramBotApiService; + private final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * Webhook endpoint for receiving updates from Telegram. + * Path token must match app.telegram-webhook.token (APP_TELEGRAM_WEBHOOK_TOKEN). + */ + @PostMapping("/{token}") + public ResponseEntity handleWebhook(@PathVariable String token, @RequestBody Update update, HttpServletRequest httpRequest) { + if (expectedWebhookToken.isEmpty() || !expectedWebhookToken.equals(token)) { + log.warn("Webhook rejected: invalid or missing token (possible misconfiguration or wrong URL); update dropped"); + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + try { + // Handle callback queries (button clicks) + if (update.hasCallbackQuery()) { + handleCallbackQuery(update.getCallbackQuery()); + } + + // Handle message updates (e.g., /start command or Reply Keyboard button clicks) + if (update.hasMessage() && update.getMessage().hasText()) { + handleMessage(update.getMessage(), httpRequest); + } + + // Handle pre-checkout query (before payment confirmation) + if (update.hasPreCheckoutQuery()) { + handlePreCheckoutQuery(update.getPreCheckoutQuery()); + } + + // Handle successful payment + if (update.hasMessage() && update.getMessage().hasSuccessfulPayment()) { + handleSuccessfulPayment(update.getMessage().getSuccessfulPayment(), update.getMessage().getFrom().getId()); + } + + return ResponseEntity.ok().build(); + } catch (Exception e) { + log.error("Error processing Telegram webhook: {}", e.getMessage(), e); + if (update.hasMessage() && update.getMessage().hasText() && update.getMessage().getText().startsWith("/start")) { + Long telegramId = update.getMessage().getFrom() != null ? update.getMessage().getFrom().getId() : null; + log.warn("Registration attempt failed (webhook error), update dropped: telegramId={}", telegramId); + } + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + /** + * Handles /start command with optional referral parameter, and Reply Keyboard button clicks. + * Format: /start or /start 123 (where 123 is the referral user ID) + */ + private void handleMessage(Message message, HttpServletRequest httpRequest) { + String messageText = message.getText(); + if (messageText == null) { + return; + } + + User telegramUser = message.getFrom(); + Long telegramId = telegramUser.getId(); + Long chatId = message.getChatId(); + + // Handle /start command + if (messageText.startsWith("/start")) { + handleStartCommand(message, httpRequest, telegramUser, telegramId); + return; + } + + // Handle Reply Keyboard button clicks + // Priority: 1) saved user language, 2) Telegram user's language_code, 3) default EN + String languageCode = "EN"; // Default + try { + var userOpt = userService.getUserByTelegramId(telegramId); + if (userOpt.isPresent() && userOpt.get().getLanguageCode() != null) { + languageCode = userOpt.get().getLanguageCode(); + } else if (telegramUser.getLanguageCode() != null && !telegramUser.getLanguageCode().isEmpty()) { + languageCode = telegramUser.getLanguageCode(); + } + } catch (Exception e) { + log.warn("Could not get user language, using default: {}", e.getMessage()); + // Fallback to Telegram user's language_code if available + if (telegramUser.getLanguageCode() != null && !telegramUser.getLanguageCode().isEmpty()) { + languageCode = telegramUser.getLanguageCode(); + } + } + Locale locale = LocaleConfig.languageCodeToLocale(languageCode); + + // Check if message matches Reply Keyboard button text + String startSpinningText = "🎰 " + localizationService.getMessage(locale, "bot.button.startSpinning"); + String usersPayoutsText = "💸 " + localizationService.getMessage(locale, "bot.button.usersPayouts"); + String infoChannelText = "ℹ️ " + localizationService.getMessage(locale, "bot.button.infoChannel"); + + if (messageText.equals(startSpinningText)) { + sendStartSpinningMessage(chatId, locale); + } else if (messageText.equals(usersPayoutsText)) { + sendUsersPayoutsMessage(chatId, locale); + } else if (messageText.equals(infoChannelText)) { + sendInfoChannelMessage(chatId, locale); + } else { + // Unknown message (e.g. old "Start Spinning" button or free text): reply and refresh keyboard + sendUnrecognizedMessageAndUpdateKeyboard(chatId, locale); + } + } + + /** + * Handles /start command with optional referral parameter. + * Format: /start or /start 123 (where 123 is the referral user ID) + */ + private void handleStartCommand(Message message, HttpServletRequest httpRequest, User telegramUser, Long telegramId) { + String messageText = message.getText(); + + log.debug("Received /start command: telegramId={}", telegramId); + + Integer referralUserId = null; + + // Parse referral parameter from /start command + // Format: /start or /start 123 + String[] parts = messageText.split("\\s+", 2); + if (parts.length > 1 && !parts[1].trim().isEmpty()) { + try { + referralUserId = Integer.parseInt(parts[1].trim()); + log.debug("Parsed referral ID: {}", referralUserId); + } catch (NumberFormatException e) { + log.warn("Invalid referral parameter format: '{}'", parts[1]); + return; + } + } + + // Check if user already exists + boolean isNewUser = userService.getUserByTelegramId(telegramId).isEmpty(); + if (isNewUser) { + log.info("New user registration via bot - telegramId={}, referralUserId={}", + telegramId, referralUserId); + } + + // Build tgUserData map similar to what TelegramAuthService.parseInitData returns + Map tgUser = new HashMap<>(); + tgUser.put("id", telegramId); + tgUser.put("first_name", telegramUser.getFirstName()); + tgUser.put("last_name", telegramUser.getLastName()); + tgUser.put("username", telegramUser.getUserName()); + tgUser.put("is_premium", telegramUser.getIsPremium() != null && telegramUser.getIsPremium()); + tgUser.put("language_code", telegramUser.getLanguageCode()); + // Note: Telegram Bot API User object doesn't have photo_url (only available in WebApp initData) + // AvatarService will fetch avatar from Bot API first, photo_url is only used as fallback + tgUser.put("photo_url", null); + + Map tgUserData = new HashMap<>(); + tgUserData.put("user", tgUser); + + // Convert referralUserId to start parameter string (as expected by UserService) + String start = referralUserId != null ? String.valueOf(referralUserId) : null; + tgUserData.put("start", start); + + try { + // Get or create user (handles registration, login update, and referral system) + UserA user = userService.getOrCreateUser(tgUserData, httpRequest); + log.debug("Bot registration completed: userId={}, telegramId={}, isNewUser={}", + user.getId(), user.getTelegramId(), isNewUser); + + // Send welcome message with buttons + // Priority: 1) saved user language, 2) Telegram user's language_code, 3) default EN + String languageCode = "EN"; // Default + if (user.getLanguageCode() != null && !user.getLanguageCode().isEmpty()) { + languageCode = user.getLanguageCode(); + } else if (telegramUser.getLanguageCode() != null && !telegramUser.getLanguageCode().isEmpty()) { + languageCode = telegramUser.getLanguageCode(); + } + sendWelcomeMessage(telegramId, languageCode); + } catch (Exception e) { + log.warn("Registration failed for telegramId={}, user may not receive welcome message: {}", telegramId, e.getMessage()); + log.error("Error registering user via bot: telegramId={}", telegramId, e); + } + } + + /** + * Handles callback queries from inline keyboard buttons. + */ + private void handleCallbackQuery(CallbackQuery callbackQuery) { + String data = callbackQuery.getData(); + Long telegramUserId = callbackQuery.getFrom().getId(); + Long chatId = callbackQuery.getMessage().getChatId(); + + log.debug("Received callback query: data={}, telegramUserId={}", data, telegramUserId); + + // Get user's language for localization + // Priority: 1) saved user language, 2) Telegram user's language_code, 3) default EN + User telegramUser = callbackQuery.getFrom(); + String languageCode = "EN"; // Default + try { + var userOpt = userService.getUserByTelegramId(telegramUserId); + if (userOpt.isPresent() && userOpt.get().getLanguageCode() != null) { + languageCode = userOpt.get().getLanguageCode(); + } else if (telegramUser.getLanguageCode() != null && !telegramUser.getLanguageCode().isEmpty()) { + languageCode = telegramUser.getLanguageCode(); + } + } catch (Exception e) { + log.warn("Could not get user language, using default: {}", e.getMessage()); + // Fallback to Telegram user's language_code if available + if (telegramUser.getLanguageCode() != null && !telegramUser.getLanguageCode().isEmpty()) { + languageCode = telegramUser.getLanguageCode(); + } + } + + Locale locale = LocaleConfig.languageCodeToLocale(languageCode); + + try { + switch (data) { + case "start_spinning": + sendStartSpinningMessage(chatId, locale); + answerCallbackQuery(callbackQuery.getId(), null); + break; + case "users_payouts": + sendUsersPayoutsMessage(chatId, locale); + answerCallbackQuery(callbackQuery.getId(), null); + break; + case "info_channel": + sendInfoChannelMessage(chatId, locale); + answerCallbackQuery(callbackQuery.getId(), null); + break; + default: + log.warn("Unknown callback data: {}", data); + answerCallbackQuery(callbackQuery.getId(), "Unknown action"); + } + } catch (Exception e) { + log.error("Error handling callback query: data={}", data, e); + answerCallbackQuery(callbackQuery.getId(), "Error processing request"); + } + } + + /** + * Builds the current Reply Keyboard (Start Game, Users payouts, Info channel) for the given locale. + */ + private ReplyKeyboardMarkup buildReplyKeyboard(Locale locale) { + ReplyKeyboardMarkup replyKeyboard = new ReplyKeyboardMarkup(); + replyKeyboard.setResizeKeyboard(true); + replyKeyboard.setOneTimeKeyboard(false); + replyKeyboard.setSelective(false); + List keyboardRows = new ArrayList<>(); + KeyboardRow row1 = new KeyboardRow(); + KeyboardButton startButton = new KeyboardButton(); + startButton.setText("🎰 " + localizationService.getMessage(locale, "bot.button.startSpinning")); + row1.add(startButton); + keyboardRows.add(row1); + KeyboardRow row2 = new KeyboardRow(); + KeyboardButton payoutsButton = new KeyboardButton(); + payoutsButton.setText("💸 " + localizationService.getMessage(locale, "bot.button.usersPayouts")); + row2.add(payoutsButton); + KeyboardButton infoButton = new KeyboardButton(); + infoButton.setText("ℹ️ " + localizationService.getMessage(locale, "bot.button.infoChannel")); + row2.add(infoButton); + keyboardRows.add(row2); + replyKeyboard.setKeyboard(keyboardRows); + return replyKeyboard; + } + + /** + * Sends welcome messages: first message with reply keyboard, second message with inline button. + */ + private void sendWelcomeMessage(Long chatId, String languageCode) { + if (telegramProperties.getBotToken() == null || telegramProperties.getBotToken().isEmpty()) { + log.warn("Bot token not configured; welcome message not sent for chatId={} (registration flow affected)", chatId); + return; + } + Locale locale = LocaleConfig.languageCodeToLocale(languageCode != null ? languageCode : "EN"); + ReplyKeyboardMarkup replyKeyboard = buildReplyKeyboard(locale); + // Create inline keyboard with only START SPINNING button + InlineKeyboardMarkup inlineKeyboard = new InlineKeyboardMarkup(); + List> inlineRows = new ArrayList<>(); + List inlineRow = new ArrayList<>(); + + InlineKeyboardButton startInlineButton = new InlineKeyboardButton(); + // Add arrows on both sides like in the reference app (right arrow on left, left arrow on right) + String startSpinningButtonText = localizationService.getMessage(locale, "bot.button.startSpinningInline"); + startInlineButton.setText(startSpinningButtonText); + // Use WebAppInfo to open mini app instead of regular URL + WebAppInfo webAppInfo = new WebAppInfo(); + webAppInfo.setUrl("https://win-spin.live/auth"); + startInlineButton.setWebApp(webAppInfo); + inlineRow.add(startInlineButton); + inlineRows.add(inlineRow); + + inlineKeyboard.setKeyboard(inlineRows); + + // Send first message with GIF animation and reply keyboard + // Note: Telegram doesn't allow both inline and reply keyboards in the same message + String firstMessage = localizationService.getMessage(locale, "bot.welcome.firstMessage"); + sendAnimationWithReplyKeyboard(chatId, firstMessage, replyKeyboard); + + // Send second message with inline button (START SPINNING) + String welcomeText = localizationService.getMessage(locale, "bot.welcome.message"); + sendMessage(chatId, welcomeText, inlineKeyboard); + } + + /** + * Sends message with Start Spinning button. + */ + private void sendStartSpinningMessage(Long chatId, Locale locale) { + String message = localizationService.getMessage(locale, "bot.message.startSpinning"); + + InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup(); + List> rows = new ArrayList<>(); + List row = new ArrayList<>(); + + InlineKeyboardButton button = new InlineKeyboardButton(); + // Add arrows on both sides like in the reference app (right arrow on left, left arrow on right) + String startSpinningButtonText = localizationService.getMessage(locale, "bot.button.startSpinningInline"); + button.setText(startSpinningButtonText); + // Use WebAppInfo to open mini app instead of regular URL + WebAppInfo webAppInfo = new WebAppInfo(); + webAppInfo.setUrl("https://win-spin.live/auth"); + button.setWebApp(webAppInfo); + row.add(button); + rows.add(row); + + keyboard.setKeyboard(rows); + + sendMessage(chatId, message, keyboard); + } + + /** + * Sends a friendly "unrecognized message" reply and updates the user's reply keyboard to the current one. + * Used when the user sends unknown text (e.g. old "Start Spinning" button) so they get the new keyboard. + */ + private void sendUnrecognizedMessageAndUpdateKeyboard(Long chatId, Locale locale) { + String message = localizationService.getMessage(locale, "bot.message.unrecognized"); + ReplyKeyboardMarkup replyKeyboard = buildReplyKeyboard(locale); + sendMessageWithReplyKeyboard(chatId, message, replyKeyboard); + } + + /** + * Sends message with Users payouts button. + */ + private void sendUsersPayoutsMessage(Long chatId, Locale locale) { + String message = localizationService.getMessage(locale, "bot.message.usersPayouts"); + + InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup(); + List> rows = new ArrayList<>(); + List row = new ArrayList<>(); + + InlineKeyboardButton button = new InlineKeyboardButton(); + button.setText(localizationService.getMessage(locale, "bot.button.openChannel")); + button.setUrl("https://t.me/win_spin_withdrawals"); + row.add(button); + rows.add(row); + + keyboard.setKeyboard(rows); + + sendMessage(chatId, message, keyboard); + } + + /** + * Handles /paysupport command. + */ + private void handlePaySupportCommand(Long chatId, User telegramUser, Long telegramId) { + // Get user's language for localization + // Priority: 1) saved user language, 2) Telegram user's language_code, 3) default EN + String languageCode = "EN"; // Default + try { + var userOpt = userService.getUserByTelegramId(telegramId); + if (userOpt.isPresent() && userOpt.get().getLanguageCode() != null) { + languageCode = userOpt.get().getLanguageCode(); + } else if (telegramUser.getLanguageCode() != null && !telegramUser.getLanguageCode().isEmpty()) { + languageCode = telegramUser.getLanguageCode(); + } + } catch (Exception e) { + log.warn("Could not get user language for /paysupport, using default: {}", e.getMessage()); + // Fallback to Telegram user's language_code if available + if (telegramUser.getLanguageCode() != null && !telegramUser.getLanguageCode().isEmpty()) { + languageCode = telegramUser.getLanguageCode(); + } + } + + Locale locale = LocaleConfig.languageCodeToLocale(languageCode); + String message = localizationService.getMessage(locale, "bot.message.paySupport"); + sendMessage(chatId, message, null); + } + + /** + * Sends message with Info channel button. + */ + private void sendInfoChannelMessage(Long chatId, Locale locale) { + String message = localizationService.getMessage(locale, "bot.message.infoChannel"); + + InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup(); + List> rows = new ArrayList<>(); + List row = new ArrayList<>(); + + InlineKeyboardButton button = new InlineKeyboardButton(); + button.setText(localizationService.getMessage(locale, "bot.button.goToChannel")); + button.setUrl("https://t.me/win_spin_news"); + row.add(button); + rows.add(row); + + keyboard.setKeyboard(rows); + + sendMessage(chatId, message, keyboard); + } + + /** + * Sends a message to a chat with inline keyboard. + */ + private void sendMessage(Long chatId, String text, InlineKeyboardMarkup keyboard) { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured"); + return; + } + + String url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; + + Map requestBody = new HashMap<>(); + requestBody.put("chat_id", chatId); + requestBody.put("text", text); + if (keyboard != null) { + // Convert InlineKeyboardMarkup to Map for JSON serialization + try { + String keyboardJson = objectMapper.writeValueAsString(keyboard); + Map keyboardMap = objectMapper.readValue(keyboardJson, Map.class); + requestBody.put("reply_markup", keyboardMap); + } catch (Exception e) { + log.error("Error serializing keyboard: {}", e.getMessage(), e); + return; + } + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + try { + ResponseEntity response = telegramBotApiService.post(url, entity); + if (response != null && response.getBody() != null) { + if (!Boolean.TRUE.equals(response.getBody().getOk())) { + log.warn("Failed to send message: chatId={}, error={}", + chatId, response.getBody().getDescription()); + } + } else if (response != null && !response.getStatusCode().is2xxSuccessful()) { + log.error("Failed to send message: chatId={}, status={}", chatId, response.getStatusCode()); + } else if (response == null) { + log.warn("Message not sent (Telegram 429, retry scheduled): chatId={} – may affect registration welcome", chatId); + } + } catch (Exception e) { + if (isTelegramUserUnavailable(e)) { + log.warn("Cannot send message: user blocked bot or chat unavailable: chatId={}", chatId); + } else { + log.error("Error sending message: chatId={}", chatId, e); + } + } + } + + /** + * Returns true if the failure is due to user blocking the bot or chat being unavailable. + * These are expected and should be logged at WARN without stack trace. + */ + private boolean isTelegramUserUnavailable(Throwable t) { + if (t instanceof HttpClientErrorException e) { + if (e.getStatusCode().value() == 403) { + return true; + } + String body = e.getResponseBodyAsString(); + return body != null && ( + body.contains("blocked by the user") || + body.contains("user is deactivated") || + body.contains("chat not found") + ); + } + return false; + } + + private boolean isTelegramUserUnavailableDescription(String description) { + return description != null && ( + description.contains("blocked by the user") || + description.contains("user is deactivated") || + description.contains("chat not found") + ); + } + + /** + * Sends a message with text and reply keyboard. + */ + private void sendMessageWithReplyKeyboard(Long chatId, String text, ReplyKeyboardMarkup replyKeyboard) { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured"); + return; + } + + String url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; + + Map requestBody = new HashMap<>(); + requestBody.put("chat_id", chatId); + requestBody.put("text", text); + + try { + String keyboardJson = objectMapper.writeValueAsString(replyKeyboard); + Map keyboardMap = objectMapper.readValue(keyboardJson, Map.class); + requestBody.put("reply_markup", keyboardMap); + } catch (Exception e) { + log.error("Error serializing reply keyboard: {}", e.getMessage(), e); + return; + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + try { + ResponseEntity response = telegramBotApiService.post(url, entity); + if (response != null && response.getBody() != null) { + if (!Boolean.TRUE.equals(response.getBody().getOk())) { + String desc = response.getBody().getDescription(); + if (isTelegramUserUnavailableDescription(desc)) { + log.warn("Cannot send message: user blocked bot or chat unavailable: chatId={}", chatId); + } else { + log.error("Failed to send message with reply keyboard: chatId={}, error={}", chatId, desc); + } + } else { + log.info("Message with reply keyboard sent successfully: chatId={}", chatId); + } + } else if (response != null && !response.getStatusCode().is2xxSuccessful()) { + log.error("Failed to send message with reply keyboard: chatId={}, status={}", + chatId, response.getStatusCode()); + } + } catch (Exception e) { + if (isTelegramUserUnavailable(e)) { + log.warn("Cannot send message: user blocked bot or chat unavailable: chatId={}", chatId); + } else { + log.error("Error sending message with reply keyboard: chatId={}", chatId, e); + } + } + } + + /** + * Sends an animation (MP4 video) with caption text and reply keyboard. + * Uses MP4 format as Telegram handles silent MP4s better than GIF files. + */ + private void sendAnimationWithReplyKeyboard(Long chatId, String caption, ReplyKeyboardMarkup replyKeyboard) { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured"); + return; + } + + String url = "https://api.telegram.org/bot" + botToken + "/sendAnimation"; + + try { + // Load MP4 from resources (Telegram "GIFs" are actually silent MP4 videos) + Resource resource = new ClassPathResource("assets/winspin_5.mp4"); + if (!resource.exists()) { + log.error("MP4 file not found: assets/winspin_5.mp4"); + // Fallback to text message if MP4 not found + sendMessageWithReplyKeyboard(chatId, caption, replyKeyboard); + return; + } + + byte[] videoBytes = StreamUtils.copyToByteArray(resource.getInputStream()); + ByteArrayResource videoResource = new ByteArrayResource(videoBytes) { + @Override + public String getFilename() { + return "winspin_5.mp4"; + } + }; + + // Create multipart form data + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("chat_id", chatId.toString()); + body.add("caption", caption); + + // EXPLICITLY SET MIME TYPE FOR THE ANIMATION PART + // This is crucial - Telegram needs to know it's a video/mp4 + HttpHeaders fileHeaders = new HttpHeaders(); + fileHeaders.setContentType(MediaType.parseMediaType("video/mp4")); + HttpEntity filePart = new HttpEntity<>(videoResource, fileHeaders); + body.add("animation", filePart); + + // Add reply keyboard if provided + if (replyKeyboard != null) { + try { + String keyboardJson = objectMapper.writeValueAsString(replyKeyboard); + body.add("reply_markup", keyboardJson); + } catch (Exception e) { + log.error("Error serializing reply keyboard: {}", e.getMessage(), e); + } + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + HttpEntity> entity = new HttpEntity<>(body, headers); + + ResponseEntity response = telegramBotApiService.post(url, entity); + if (response != null && response.getBody() != null) { + if (!Boolean.TRUE.equals(response.getBody().getOk())) { + String desc = response.getBody().getDescription(); + if (isTelegramUserUnavailableDescription(desc)) { + log.warn("Cannot send animation: user blocked bot or chat unavailable: chatId={}", chatId); + } else { + log.error("Failed to send animation with reply keyboard: chatId={}, error={}", chatId, desc); + sendMessageWithReplyKeyboard(chatId, caption, replyKeyboard); + } + } else { + log.info("Animation with reply keyboard sent successfully: chatId={}", chatId); + } + } else if (response != null && !response.getStatusCode().is2xxSuccessful()) { + log.error("Failed to send animation with reply keyboard: chatId={}, status={}", + chatId, response.getStatusCode()); + sendMessageWithReplyKeyboard(chatId, caption, replyKeyboard); + } else if (response == null) { + log.warn("Welcome animation delayed (Telegram 429, retry scheduled): chatId={} – registration flow may appear incomplete", chatId); + } + } catch (Exception e) { + if (isTelegramUserUnavailable(e)) { + log.warn("Cannot send animation: user blocked bot or chat unavailable: chatId={}", chatId); + } else { + log.error("Error sending animation with reply keyboard: chatId={}", chatId, e); + sendMessageWithReplyKeyboard(chatId, caption, replyKeyboard); + } + } + } + + /** + * Sends a message with only reply keyboard (for setting up persistent keyboard). + */ + private void sendReplyKeyboardOnly(Long chatId, ReplyKeyboardMarkup replyKeyboard) { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured"); + return; + } + + String url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; + + Map requestBody = new HashMap<>(); + requestBody.put("chat_id", chatId); + // Telegram requires non-empty text for messages with reply keyboard + // Sending with a minimal message - this message won't be visible to users + // but is required to set up the persistent keyboard + requestBody.put("text", "."); + + try { + String keyboardJson = objectMapper.writeValueAsString(replyKeyboard); + Map keyboardMap = objectMapper.readValue(keyboardJson, Map.class); + requestBody.put("reply_markup", keyboardMap); + } catch (Exception e) { + log.error("Error serializing reply keyboard: {}", e.getMessage(), e); + return; + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + try { + ResponseEntity response = telegramBotApiService.post(url, entity); + if (response != null && response.getBody() != null) { + if (!Boolean.TRUE.equals(response.getBody().getOk())) { + String desc = response.getBody().getDescription(); + if (isTelegramUserUnavailableDescription(desc)) { + log.warn("Cannot send reply keyboard: user blocked bot or chat unavailable: chatId={}", chatId); + } else { + log.error("Failed to send reply keyboard: chatId={}, error={}", chatId, desc); + } + } else { + log.info("Reply keyboard sent successfully: chatId={}", chatId); + } + } else if (response != null && !response.getStatusCode().is2xxSuccessful()) { + log.error("Failed to send reply keyboard: chatId={}, status={}", + chatId, response.getStatusCode()); + } + } catch (Exception e) { + if (isTelegramUserUnavailable(e)) { + log.warn("Cannot send reply keyboard: user blocked bot or chat unavailable: chatId={}", chatId); + } else { + log.error("Error sending reply keyboard: chatId={}", chatId, e); + } + } + } + + /** + * Answers a callback query. + */ + private void answerCallbackQuery(String queryId, String text) { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured"); + return; + } + + String url = "https://api.telegram.org/bot" + botToken + "/answerCallbackQuery"; + + Map requestBody = new HashMap<>(); + requestBody.put("callback_query_id", queryId); + if (text != null) { + requestBody.put("text", text); + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + try { + ResponseEntity response = telegramBotApiService.post(url, entity); + if (response != null && response.getBody() != null && !Boolean.TRUE.equals(response.getBody().getOk())) { + log.warn("Failed to answer callback query: queryId={}, error={}", + queryId, response.getBody().getDescription()); + } + } catch (Exception e) { + log.error("Error answering callback query: queryId={}", queryId, e); + } + } + + /** + * Handles pre-checkout query (before payment confirmation). + * Telegram sends this to verify the payment before the user confirms. + * We must answer it to approve the payment, otherwise it will expire. + */ + private void handlePreCheckoutQuery(PreCheckoutQuery preCheckoutQuery) { + String queryId = preCheckoutQuery.getId(); + String invoicePayload = preCheckoutQuery.getInvoicePayload(); // This is our orderId + + log.debug("Pre-checkout query: queryId={}, orderId={}", queryId, invoicePayload); + + // Answer the pre-checkout query to approve the payment + // We always approve since we validate on successful payment + answerPreCheckoutQuery(queryId, true, null); + } + + /** + * Answers a pre-checkout query to approve or reject the payment. + * + * @param queryId The pre-checkout query ID + * @param ok True to approve, false to reject + * @param errorMessage Error message if rejecting (null if approving) + */ + private void answerPreCheckoutQuery(String queryId, boolean ok, String errorMessage) { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured"); + return; + } + + String url = "https://api.telegram.org/bot" + botToken + "/answerPreCheckoutQuery"; + + Map requestBody = new HashMap<>(); + requestBody.put("pre_checkout_query_id", queryId); + requestBody.put("ok", ok); + if (!ok && errorMessage != null) { + requestBody.put("error_message", errorMessage); + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + try { + log.debug("Answering pre-checkout query: queryId={}, ok={}", queryId, ok); + ResponseEntity response = telegramBotApiService.post(url, entity); + if (response != null && response.getBody() != null && !Boolean.TRUE.equals(response.getBody().getOk())) { + log.warn("Failed to answer pre-checkout query: queryId={}, error={}", + queryId, response.getBody().getDescription()); + } else if (response != null && !response.getStatusCode().is2xxSuccessful()) { + log.error("Failed to answer pre-checkout query: queryId={}, status={}", queryId, response.getStatusCode()); + } + } catch (Exception e) { + log.error("Error answering pre-checkout query: queryId={}", queryId, e); + } + } + + /** + * Handles successful payment from Telegram. + * Processes the payment and credits the user's balance. + */ + private void handleSuccessfulPayment(SuccessfulPayment successfulPayment, Long telegramUserId) { + String invoicePayload = successfulPayment.getInvoicePayload(); // This is our orderId + + // Extract stars amount from total amount + // Telegram sends amount in the smallest currency unit (for Stars, it's 1:1) + Integer starsAmount = successfulPayment.getTotalAmount().intValue(); + + log.info("Payment webhook received: orderId={}, telegramUserId={}, starsAmount={}", invoicePayload, telegramUserId, starsAmount); + + try { + // Create webhook request and process payment + PaymentWebhookRequest request = new PaymentWebhookRequest(); + request.setOrderId(invoicePayload); + request.setTelegramUserId(telegramUserId); + request.setTelegramPaymentChargeId(successfulPayment.getTelegramPaymentChargeId()); + request.setTelegramProviderPaymentChargeId(successfulPayment.getProviderPaymentChargeId()); + request.setStarsAmount(starsAmount); + + boolean processed = paymentService.processPaymentWebhook(request); + if (!processed) { + log.warn("Payment already processed: orderId={}", invoicePayload); + } + } catch (Exception e) { + log.error("Error processing payment webhook: orderId={}", invoicePayload, e); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/TransactionController.java b/src/main/java/com/lottery/lottery/controller/TransactionController.java new file mode 100644 index 0000000..848b575 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/TransactionController.java @@ -0,0 +1,50 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.TransactionDto; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.service.TransactionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +/** + * Controller for transaction history operations. + */ +@Slf4j +@RestController +@RequestMapping("/api/transactions") +@RequiredArgsConstructor +public class TransactionController { + + private final TransactionService transactionService; + + /** + * Gets transaction history for the current user. + * Returns 50 transactions per page, ordered by creation time descending (newest first). + * + * @param page Page number (0-indexed, defaults to 0) + * @param timezone Optional timezone (e.g., "Europe/London"). If not provided, uses UTC. + * @return Page of transactions + */ + @GetMapping + public ResponseEntity> getTransactions( + @RequestParam(defaultValue = "0") int page, + @RequestParam(required = false) String timezone) { + UserA user = UserContext.get(); + Integer userId = user.getId(); + String languageCode = user.getLanguageCode(); + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + // Note: languageCode is still used for date formatting (localized "at" word) + // Transaction type localization is now handled in the frontend + Page transactions = transactionService.getUserTransactions(userId, page, timezone, languageCode); + return ResponseEntity.ok(transactions); + } +} + + + diff --git a/src/main/java/com/lottery/lottery/controller/UserCheckController.java b/src/main/java/com/lottery/lottery/controller/UserCheckController.java new file mode 100644 index 0000000..e9cf991 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/UserCheckController.java @@ -0,0 +1,105 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.UserCheckDto; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.model.UserD; +import com.lottery.lottery.repository.PaymentRepository; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.repository.UserBRepository; +import com.lottery.lottery.repository.UserDRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Optional; + +/** + * Controller for user check endpoint (open endpoint for external applications). + * Path token is validated against app.check-user.token (APP_CHECK_USER_TOKEN). No user auth. + */ +@Slf4j +@RestController +@RequestMapping("/api/check_user") +@RequiredArgsConstructor +public class UserCheckController { + + @Value("${app.check-user.token:}") + private String expectedToken; + + private final UserARepository userARepository; + private final UserBRepository userBRepository; + private final UserDRepository userDRepository; + private final PaymentRepository paymentRepository; + + /** + * Gets user information by Telegram ID. + * Path: /api/check_user/{token}/{telegramId}. Token must match APP_CHECK_USER_TOKEN. + * + * @param token Secret token from path (must match config) + * @param telegramId The Telegram ID of the user + * @return 200 with user info (found=true) or 200 with found=false when user not found; 403 if token invalid + */ + @GetMapping("/{token}/{telegramId}") + public ResponseEntity checkUser(@PathVariable String token, @PathVariable Long telegramId) { + if (expectedToken.isEmpty() || !expectedToken.equals(token)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + try { + // Find user by telegram_id + Optional userAOpt = userARepository.findByTelegramId(telegramId); + + if (userAOpt.isEmpty()) { + log.debug("User not found for telegramId={}", telegramId); + UserCheckDto notFoundResponse = UserCheckDto.builder().found(false).build(); + return ResponseEntity.ok(notFoundResponse); + } + + UserA userA = userAOpt.get(); + Integer userId = userA.getId(); + + // Get balance_a from db_users_b + Optional userBOpt = userBRepository.findById(userId); + Long balanceA = userBOpt.map(UserB::getBalanceA).orElse(0L); + // Convert to tickets (balance_a / 1,000,000) + Double tickets = balanceA / 1_000_000.0; + + // Get rounds_played from db_users_b + Integer roundsPlayed = userBOpt.map(UserB::getRoundsPlayed).orElse(0); + + // Get referer_id_1 from db_users_d + Optional userDOpt = userDRepository.findById(userId); + Integer refererId = userDOpt.map(UserD::getRefererId1).orElse(0); + // Return 0 if refererId is 0 or negative (not set) + if (refererId <= 0) { + refererId = 0; + } + + // Sum completed payments stars_amount + Integer depositTotal = paymentRepository.sumCompletedStarsAmountByUserId(userId); + + // Build response + UserCheckDto response = UserCheckDto.builder() + .found(true) + .dateReg(userA.getDateReg()) + .tickets(tickets) + .depositTotal(depositTotal) + .refererId(refererId) + .roundsPlayed(roundsPlayed) + .build(); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + log.error("Error checking user for telegramId={}", telegramId, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/controller/UserController.java b/src/main/java/com/lottery/lottery/controller/UserController.java new file mode 100644 index 0000000..cf721d5 --- /dev/null +++ b/src/main/java/com/lottery/lottery/controller/UserController.java @@ -0,0 +1,146 @@ +package com.lottery.lottery.controller; + +import com.lottery.lottery.dto.ReferralDto; +import com.lottery.lottery.dto.UserDto; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.repository.UserBRepository; +import com.lottery.lottery.security.UserContext; +import com.lottery.lottery.service.AvatarService; +import com.lottery.lottery.service.FeatureSwitchService; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.service.UserService; +import com.lottery.lottery.util.IpUtils; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequestMapping("/api/users") +@RequiredArgsConstructor +public class UserController { + + private final UserService userService; + private final UserBRepository userBRepository; + private final AvatarService avatarService; + private final LocalizationService localizationService; + private final FeatureSwitchService featureSwitchService; + + @GetMapping("/current") + public UserDto getCurrentUser() { + UserA user = UserContext.get(); + + // Convert IP from byte[] to string for display + String ipAddress = IpUtils.bytesToIp(user.getIp()); + + // Get balance + Long balanceA = userBRepository.findById(user.getId()) + .map(UserB::getBalanceA) + .orElse(0L); + + // Generate avatar URL on-the-fly (deterministic from userId) + String avatarUrl = avatarService.getAvatarUrl(user.getId()); + + return UserDto.builder() + .id(user.getId()) + .telegram_id(user.getTelegramId()) + .username(user.getTelegramName()) + .screenName(user.getScreenName()) + .dateReg(user.getDateReg()) + .ip(ipAddress) + .balanceA(balanceA) + .avatarUrl(avatarUrl) + .languageCode(user.getLanguageCode()) + .paymentEnabled(featureSwitchService.isPaymentEnabled()) + .payoutEnabled(featureSwitchService.isPayoutEnabled()) + .promotionsEnabled(featureSwitchService.isPromotionsEnabled()) + .build(); + } + + /** + * Updates user's language code. + * Called when user changes language in app header. + */ + @PutMapping("/language") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void updateLanguage(@RequestBody UpdateLanguageRequest request) { + UserA user = UserContext.get(); + userService.updateLanguageCode(user.getId(), request.getLanguageCode()); + } + + /** + * Adds deposit amount to user's balance_a. + * For now, this is a mock implementation that directly adds to balance. + * Will be replaced with payment integration later. + */ + @PostMapping("/deposit") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deposit(@RequestBody DepositRequest request) { + UserA user = UserContext.get(); + + // Frontend sends amount already in bigint format (no conversion needed) + Long depositAmount = request.getAmount(); + if (depositAmount == null || depositAmount <= 0) { + throw new IllegalArgumentException(localizationService.getMessage("user.error.depositAmountInvalid")); + } + + UserB userB = userBRepository.findById(user.getId()) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + + // Add to balance + userB.setBalanceA(userB.getBalanceA() + depositAmount); + + // Update deposit statistics + userB.setDepositTotal(userB.getDepositTotal() + depositAmount); + userB.setDepositCount(userB.getDepositCount() + 1); + + userBRepository.save(userB); + } + + @Data + public static class UpdateLanguageRequest { + private String languageCode; + } + + /** + * Gets referrals for a specific level with pagination. + * Always returns 50 results per page. + * + * @param level The referral level (1, 2, or 3) + * @param page Page number (0-indexed, defaults to 0) + * @return Page of referrals with name and commission + */ + @GetMapping("/referrals") + public ReferralsResponse getReferrals( + @RequestParam Integer level, + @RequestParam(defaultValue = "0") Integer page) { + UserA user = UserContext.get(); + + Page referralsPage = userService.getReferrals(user.getId(), level, page); + + return new ReferralsResponse( + referralsPage.getContent(), + referralsPage.getNumber(), + referralsPage.getTotalPages(), + referralsPage.getTotalElements() + ); + } + + @Data + public static class DepositRequest { + private Long amount; // Amount in bigint format (frontend converts before sending) + } + + @Data + @RequiredArgsConstructor + public static class ReferralsResponse { + private final java.util.List referrals; + private final Integer currentPage; + private final Integer totalPages; + private final Long totalElements; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminBotConfigDto.java b/src/main/java/com/lottery/lottery/dto/AdminBotConfigDto.java new file mode 100644 index 0000000..2558288 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminBotConfigDto.java @@ -0,0 +1,34 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminBotConfigDto { + private Integer id; + private Integer userId; + /** User screen name from db_users_a (for display). */ + private String screenName; + private Boolean room1; + private Boolean room2; + private Boolean room3; + /** Time window start UTC, format HH:mm (e.g. "14:00"). */ + private String timeUtcStart; + /** Time window end UTC, format HH:mm (e.g. "17:00"). */ + private String timeUtcEnd; + /** Min bet in bigint (1 ticket = 1_000_000). */ + private Long betMin; + /** Max bet in bigint. */ + private Long betMax; + private String persona; + private Boolean active; + private Instant createdAt; + private Instant updatedAt; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminBotConfigRequest.java b/src/main/java/com/lottery/lottery/dto/AdminBotConfigRequest.java new file mode 100644 index 0000000..f0cf85d --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminBotConfigRequest.java @@ -0,0 +1,37 @@ +package com.lottery.lottery.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminBotConfigRequest { + @NotNull(message = "userId is required") + private Integer userId; + @NotNull + private Boolean room1; + @NotNull + private Boolean room2; + @NotNull + private Boolean room3; + /** Time window start UTC, format HH:mm (e.g. "14:00"). */ + @NotNull + private String timeUtcStart; + /** Time window end UTC, format HH:mm (e.g. "17:00"). */ + @NotNull + private String timeUtcEnd; + /** Min bet in bigint (1 ticket = 1_000_000). */ + @NotNull + private Long betMin; + /** Max bet in bigint. */ + @NotNull + private Long betMax; + private String persona; // conservative, aggressive, balanced; default balanced + @NotNull + private Boolean active; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminConfigurationsRequest.java b/src/main/java/com/lottery/lottery/dto/AdminConfigurationsRequest.java new file mode 100644 index 0000000..cf7032d --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminConfigurationsRequest.java @@ -0,0 +1,19 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class AdminConfigurationsRequest { + + private List safeBotUserIds = new ArrayList<>(); + private List flexibleBots = new ArrayList<>(); + + @Data + public static class FlexibleBotEntry { + private Integer userId; + private Double winRate; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminGameRoundDto.java b/src/main/java/com/lottery/lottery/dto/AdminGameRoundDto.java new file mode 100644 index 0000000..76008d1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminGameRoundDto.java @@ -0,0 +1,28 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminGameRoundDto { + private Long roundId; + private Integer roomNumber; + private String phase; + private Long totalBet; + private Long userBet; + private Integer winnerUserId; + private Long winnerBet; + private Long payout; + private Long commission; + private Instant startedAt; + private Instant resolvedAt; + private Boolean isWinner; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminLoginRequest.java b/src/main/java/com/lottery/lottery/dto/AdminLoginRequest.java new file mode 100644 index 0000000..958c37a --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminLoginRequest.java @@ -0,0 +1,10 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +@Data +public class AdminLoginRequest { + private String username; + private String password; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminLoginResponse.java b/src/main/java/com/lottery/lottery/dto/AdminLoginResponse.java new file mode 100644 index 0000000..5b77cd5 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminLoginResponse.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class AdminLoginResponse { + private String token; + private String username; + private String role; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminMasterDto.java b/src/main/java/com/lottery/lottery/dto/AdminMasterDto.java new file mode 100644 index 0000000..b45fbcf --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminMasterDto.java @@ -0,0 +1,31 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminMasterDto { + private Integer id; + private String screenName; + /** Level 1 referrals count (from master's UserD row). */ + private Integer referals1; + /** Level 2 referrals count. */ + private Integer referals2; + /** Level 3 referrals count. */ + private Integer referals3; + /** Total users with master_id = this master's id. */ + private Long totalReferrals; + /** Sum of deposit_total of all referrals, in USD (divided by 1e9). */ + private BigDecimal depositTotalUsd; + /** Sum of withdraw_total of all referrals, in USD (divided by 1e9). */ + private BigDecimal withdrawTotalUsd; + /** depositTotalUsd - withdrawTotalUsd. */ + private BigDecimal profitUsd; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminPaymentDto.java b/src/main/java/com/lottery/lottery/dto/AdminPaymentDto.java new file mode 100644 index 0000000..08b8168 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPaymentDto.java @@ -0,0 +1,27 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPaymentDto { + private Long id; + private Integer userId; + private String userName; + private String orderId; + private Integer starsAmount; + private Long ticketsAmount; + private String status; + private String telegramPaymentChargeId; + private String telegramProviderPaymentChargeId; + private Instant createdAt; + private Instant completedAt; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminPayoutDto.java b/src/main/java/com/lottery/lottery/dto/AdminPayoutDto.java new file mode 100644 index 0000000..6937649 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPayoutDto.java @@ -0,0 +1,28 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPayoutDto { + private Long id; + private Integer userId; + private String userName; + private String username; + private String type; + private String giftName; + private Long total; + private Integer starsAmount; + private Integer quantity; + private String status; + private Instant createdAt; + private Instant resolvedAt; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminPromotionDto.java b/src/main/java/com/lottery/lottery/dto/AdminPromotionDto.java new file mode 100644 index 0000000..a960395 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPromotionDto.java @@ -0,0 +1,23 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPromotionDto { + private Integer id; + private String type; + private Instant startTime; + private Instant endTime; + private String status; + private Long totalReward; + private Instant createdAt; + private Instant updatedAt; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminPromotionRequest.java b/src/main/java/com/lottery/lottery/dto/AdminPromotionRequest.java new file mode 100644 index 0000000..566982c --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPromotionRequest.java @@ -0,0 +1,25 @@ +package com.lottery.lottery.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPromotionRequest { + @NotNull + private String type; + @NotNull + private Instant startTime; + @NotNull + private Instant endTime; + @NotNull + private String status; + private Long totalReward; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminPromotionRewardDto.java b/src/main/java/com/lottery/lottery/dto/AdminPromotionRewardDto.java new file mode 100644 index 0000000..0b88bd3 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPromotionRewardDto.java @@ -0,0 +1,22 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPromotionRewardDto { + private Integer id; + private Integer promoId; + private Integer place; + /** Reward in bigint (1 ticket = 1_000_000). */ + private Long reward; + private Instant createdAt; + private Instant updatedAt; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminPromotionRewardRequest.java b/src/main/java/com/lottery/lottery/dto/AdminPromotionRewardRequest.java new file mode 100644 index 0000000..fb0d6ec --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPromotionRewardRequest.java @@ -0,0 +1,18 @@ +package com.lottery.lottery.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPromotionRewardRequest { + @NotNull + private Integer place; + @NotNull + private Long reward; // bigint, 1 ticket = 1_000_000 +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminPromotionUserDto.java b/src/main/java/com/lottery/lottery/dto/AdminPromotionUserDto.java new file mode 100644 index 0000000..fbbf4b0 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPromotionUserDto.java @@ -0,0 +1,21 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPromotionUserDto { + private Integer promoId; + private Integer userId; + /** Points as ticket count, 2 decimal places (e.g. 100.25). */ + private BigDecimal points; + private Instant updatedAt; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminPromotionUserPointsRequest.java b/src/main/java/com/lottery/lottery/dto/AdminPromotionUserPointsRequest.java new file mode 100644 index 0000000..313bf8b --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminPromotionUserPointsRequest.java @@ -0,0 +1,18 @@ +package com.lottery.lottery.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminPromotionUserPointsRequest { + @NotNull + private BigDecimal points; // ticket count, 2 decimal places +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminRoomDetailDto.java b/src/main/java/com/lottery/lottery/dto/AdminRoomDetailDto.java new file mode 100644 index 0000000..cb78f0a --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminRoomDetailDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminRoomDetailDto { + private Integer roomNumber; + private String phase; + private Long roundId; + private Long totalBetTickets; + private Double totalBetUsd; + private Integer registeredPlayers; + private Integer connectedUsers; + private List participants; + /** Viewers: same as participants section format but without tickets/chances (screen name + id). */ + private List connectedViewers; + private AdminRoomWinnerDto winner; // when phase is SPINNING or RESOLUTION +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminRoomOnlineUserDto.java b/src/main/java/com/lottery/lottery/dto/AdminRoomOnlineUserDto.java new file mode 100644 index 0000000..0ab24c5 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminRoomOnlineUserDto.java @@ -0,0 +1,29 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * One row for the "all online users across rooms" admin table. + * currentBetTickets null = viewer (not registered in current round). + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminRoomOnlineUserDto { + private Integer userId; + private String screenName; + private Integer roomNumber; + /** Current round bet in tickets; null if viewer (not registered). */ + private Long currentBetTickets; + /** balance_a in bigint (divide by 1_000_000 for display). */ + private Long balanceA; + private Long depositTotal; + private Integer depositCount; + private Long withdrawTotal; + private Integer withdrawCount; + private Integer roundsPlayed; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminRoomParticipantDto.java b/src/main/java/com/lottery/lottery/dto/AdminRoomParticipantDto.java new file mode 100644 index 0000000..6f294e1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminRoomParticipantDto.java @@ -0,0 +1,17 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminRoomParticipantDto { + private Integer userId; + private String screenName; + private Long betTickets; + private Double chancePct; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminRoomSummaryDto.java b/src/main/java/com/lottery/lottery/dto/AdminRoomSummaryDto.java new file mode 100644 index 0000000..4dc9b97 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminRoomSummaryDto.java @@ -0,0 +1,20 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminRoomSummaryDto { + private Integer roomNumber; + private String phase; // WAITING, COUNTDOWN, SPINNING, RESOLUTION + private Integer connectedUsers; + private Integer registeredPlayers; + private Long totalBetTickets; + private Double totalBetUsd; // tickets / 1000 for display + private Long roundId; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminRoomViewerDto.java b/src/main/java/com/lottery/lottery/dto/AdminRoomViewerDto.java new file mode 100644 index 0000000..561c421 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminRoomViewerDto.java @@ -0,0 +1,15 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminRoomViewerDto { + private Integer userId; + private String screenName; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminRoomWinnerDto.java b/src/main/java/com/lottery/lottery/dto/AdminRoomWinnerDto.java new file mode 100644 index 0000000..6d40935 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminRoomWinnerDto.java @@ -0,0 +1,17 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminRoomWinnerDto { + private Integer userId; + private String screenName; + private Long betTickets; + private Double winChancePct; +} diff --git a/src/main/java/com/lottery/lottery/dto/AdminSupportMessageDto.java b/src/main/java/com/lottery/lottery/dto/AdminSupportMessageDto.java new file mode 100644 index 0000000..f56338e --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminSupportMessageDto.java @@ -0,0 +1,22 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminSupportMessageDto { + private Long id; + private Integer userId; + private String userName; + private String message; + private Instant createdAt; + private Boolean isAdmin; // true if sent by admin +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminSupportTicketDetailDto.java b/src/main/java/com/lottery/lottery/dto/AdminSupportTicketDetailDto.java new file mode 100644 index 0000000..a5b747d --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminSupportTicketDetailDto.java @@ -0,0 +1,25 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminSupportTicketDetailDto { + private Long id; + private Integer userId; + private String userName; + private String subject; + private String status; + private Instant createdAt; + private Instant updatedAt; + private List messages; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminSupportTicketDto.java b/src/main/java/com/lottery/lottery/dto/AdminSupportTicketDto.java new file mode 100644 index 0000000..38cbbec --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminSupportTicketDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminSupportTicketDto { + private Long id; + private Integer userId; + private String userName; + private String subject; + private String status; + private Instant createdAt; + private Instant updatedAt; + private Long messageCount; + private String lastMessagePreview; + private Instant lastMessageAt; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminTaskClaimDto.java b/src/main/java/com/lottery/lottery/dto/AdminTaskClaimDto.java new file mode 100644 index 0000000..1c0e232 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminTaskClaimDto.java @@ -0,0 +1,21 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminTaskClaimDto { + private Long id; + private Integer taskId; + private String taskTitle; + private String taskType; + private Instant claimedAt; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminTransactionDto.java b/src/main/java/com/lottery/lottery/dto/AdminTransactionDto.java new file mode 100644 index 0000000..70b0c8c --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminTransactionDto.java @@ -0,0 +1,22 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminTransactionDto { + private Long id; + private Long amount; // In bigint format + private String type; + private Integer taskId; + private Long roundId; + private Instant createdAt; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminUserDetailDto.java b/src/main/java/com/lottery/lottery/dto/AdminUserDetailDto.java new file mode 100644 index 0000000..3ebe2af --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminUserDetailDto.java @@ -0,0 +1,55 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminUserDetailDto { + // Basic Info + private Integer id; + private String screenName; + private Long telegramId; + private String telegramName; + private Integer isPremium; + private String languageCode; + private String countryCode; + private String deviceCode; + private String avatarUrl; + private Integer dateReg; + private Integer dateLogin; + private Integer banned; + /** IP address as string (e.g. xxx.xxx.xxx.xxx), converted from varbinary in DB. */ + private String ipAddress; + + // Balance Info + private Long balanceA; + private Long depositTotal; + private Integer depositCount; + private Long withdrawTotal; + private Integer withdrawCount; + /** Total deposits in USD (CRYPTO only). */ + private java.math.BigDecimal depositTotalUsd; + /** Total withdrawals in USD (CRYPTO only). */ + private java.math.BigDecimal withdrawTotalUsd; + /** When true, the user cannot create any payout request. */ + private Boolean withdrawalsDisabled; + + // Game Stats + private Integer roundsPlayed; + + // Referral Info + private Integer referralCount; + private Long totalCommissionsEarned; + /** Total commissions earned in USD (converted from tickets). */ + private java.math.BigDecimal totalCommissionsEarnedUsd; + private Integer masterId; + private List referralLevels; +} + diff --git a/src/main/java/com/lottery/lottery/dto/AdminUserDto.java b/src/main/java/com/lottery/lottery/dto/AdminUserDto.java new file mode 100644 index 0000000..8167057 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/AdminUserDto.java @@ -0,0 +1,42 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminUserDto { + private Integer id; + private String screenName; + private Long telegramId; + private String telegramName; + private Long balanceA; + private Long balanceB; + private Long depositTotal; + private Integer depositCount; + private Long withdrawTotal; + private Integer withdrawCount; + private Integer roundsPlayed; + private Integer dateReg; + private Integer dateLogin; + private Integer banned; + private String countryCode; + private String languageCode; + private Integer referralCount; // Total referrals across all levels + private Long totalCommissionsEarned; // Total commissions earned from referrals + /** Profit in tickets (bigint): depositTotal - withdrawTotal */ + private Long profit; + /** USD from db_users_b: depositTotal (tickets/1000) */ + private BigDecimal depositTotalUsd; + /** USD from db_users_b: withdrawTotal (tickets/1000) */ + private BigDecimal withdrawTotalUsd; + /** USD from db_users_b: profit (tickets/1000) */ + private BigDecimal profitUsd; +} + diff --git a/src/main/java/com/lottery/lottery/dto/BalanceAdjustmentRequest.java b/src/main/java/com/lottery/lottery/dto/BalanceAdjustmentRequest.java new file mode 100644 index 0000000..3753ad1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/BalanceAdjustmentRequest.java @@ -0,0 +1,36 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BalanceAdjustmentRequest { + @NotNull(message = "Balance type is required") + private BalanceType balanceType; // A or B + + @NotNull(message = "Amount is required") + private Long amount; // In bigint format (tickets * 1,000,000) + + @NotNull(message = "Operation is required") + private OperationType operation; // ADD or SUBTRACT + + @NotBlank(message = "Reason is required") + private String reason; // Reason for adjustment (for audit log) + + public enum BalanceType { + A, B + } + + public enum OperationType { + ADD, SUBTRACT + } +} + diff --git a/src/main/java/com/lottery/lottery/dto/BalanceAdjustmentResponse.java b/src/main/java/com/lottery/lottery/dto/BalanceAdjustmentResponse.java new file mode 100644 index 0000000..9dd544a --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/BalanceAdjustmentResponse.java @@ -0,0 +1,20 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BalanceAdjustmentResponse { + private Long newBalanceA; + private Long newBalanceB; + private Long previousBalanceA; + private Long previousBalanceB; + private Long adjustmentAmount; + private String message; +} + diff --git a/src/main/java/com/lottery/lottery/dto/BalanceUpdateDto.java b/src/main/java/com/lottery/lottery/dto/BalanceUpdateDto.java new file mode 100644 index 0000000..65c9f19 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/BalanceUpdateDto.java @@ -0,0 +1,19 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BalanceUpdateDto { + private Long balanceA; // Balance in bigint format (database format) +} + + + + + diff --git a/src/main/java/com/lottery/lottery/dto/BotRegisterRequest.java b/src/main/java/com/lottery/lottery/dto/BotRegisterRequest.java new file mode 100644 index 0000000..92a61d2 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/BotRegisterRequest.java @@ -0,0 +1,37 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BotRegisterRequest { + @JsonProperty("telegram_id") + private Long telegramId; + + @JsonProperty("first_name") + private String firstName; + + @JsonProperty("last_name") + private String lastName; + + private String username; + + @JsonProperty("is_premium") + private Boolean isPremium; + + @JsonProperty("language_code") + private String languageCode; + + @JsonProperty("photo_url") + private String photoUrl; + + @JsonProperty("referral_user_id") + private Integer referralUserId; +} + diff --git a/src/main/java/com/lottery/lottery/dto/BotRegisterResponse.java b/src/main/java/com/lottery/lottery/dto/BotRegisterResponse.java new file mode 100644 index 0000000..32fe77e --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/BotRegisterResponse.java @@ -0,0 +1,25 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BotRegisterResponse { + @JsonProperty("user_id") + private Integer userId; + + @JsonProperty("is_new_user") + private Boolean isNewUser; + + private String message; +} + + + + diff --git a/src/main/java/com/honey/honey/dto/UserDto.java b/src/main/java/com/lottery/lottery/dto/ClaimTaskResponse.java similarity index 55% rename from src/main/java/com/honey/honey/dto/UserDto.java rename to src/main/java/com/lottery/lottery/dto/ClaimTaskResponse.java index 5152267..2248cb0 100644 --- a/src/main/java/com/honey/honey/dto/UserDto.java +++ b/src/main/java/com/lottery/lottery/dto/ClaimTaskResponse.java @@ -1,4 +1,4 @@ -package com.honey.honey.dto; +package com.lottery.lottery.dto; import lombok.AllArgsConstructor; import lombok.Builder; @@ -9,9 +9,11 @@ import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor -public class UserDto { - private Long telegram_id; - private String username; - private String ip; +public class ClaimTaskResponse { + private boolean success; + private String message; } + + + diff --git a/src/main/java/com/lottery/lottery/dto/CompletedRoundDto.java b/src/main/java/com/lottery/lottery/dto/CompletedRoundDto.java new file mode 100644 index 0000000..14ec720 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CompletedRoundDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CompletedRoundDto { + private Long roundId; + private Integer winnerUserId; + private String winnerScreenName; + private String winnerAvatarUrl; + private Long winnerBet; + private Long payout; + private Long totalBet; + private Double winChance; // winner's chance percentage + private Long resolvedAt; // timestamp +} + + + + diff --git a/src/main/java/com/lottery/lottery/dto/CreateCryptoWithdrawalRequest.java b/src/main/java/com/lottery/lottery/dto/CreateCryptoWithdrawalRequest.java new file mode 100644 index 0000000..0fed935 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CreateCryptoWithdrawalRequest.java @@ -0,0 +1,23 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * Request body for POST /api/payments/crypto-withdrawal. + */ +@Data +public class CreateCryptoWithdrawalRequest { + + @NotNull(message = "pid is required") + private Integer pid; + + @NotBlank(message = "wallet is required") + private String wallet; + + /** Tickets amount in bigint format (tickets * 1_000_000). */ + @NotNull(message = "total is required") + private Long total; +} diff --git a/src/main/java/com/lottery/lottery/dto/CreateMessageRequest.java b/src/main/java/com/lottery/lottery/dto/CreateMessageRequest.java new file mode 100644 index 0000000..8b9f12a --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CreateMessageRequest.java @@ -0,0 +1,22 @@ +package com.lottery.lottery.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateMessageRequest { + + @NotBlank(message = "Message is required") + @Size(min = 3, max = 2000, message = "Message must be between 3 and 2000 characters") + private String message; +} + + + diff --git a/src/main/java/com/lottery/lottery/dto/CreatePaymentRequest.java b/src/main/java/com/lottery/lottery/dto/CreatePaymentRequest.java new file mode 100644 index 0000000..ec2346c --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CreatePaymentRequest.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +@Data +public class CreatePaymentRequest { + private Integer starsAmount; // Amount in Stars (legacy) + private Double usdAmount; // USD as decimal, e.g. 3.25 (crypto) +} + + + + diff --git a/src/main/java/com/lottery/lottery/dto/CreatePayoutRequest.java b/src/main/java/com/lottery/lottery/dto/CreatePayoutRequest.java new file mode 100644 index 0000000..3500d51 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CreatePayoutRequest.java @@ -0,0 +1,14 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +@Data +public class CreatePayoutRequest { + private String username; + private Long total; // Tickets amount in bigint format + private Integer starsAmount; // Stars amount (for STARS type) + private String type; // "STARS" or "GIFT" + private String giftName; // Gift name (for GIFT type): "HEART", "BEAR", etc. + private Integer quantity; // Quantity of gifts/stars (1-100, default 1) +} + diff --git a/src/main/java/com/honey/honey/dto/CreateSessionRequest.java b/src/main/java/com/lottery/lottery/dto/CreateSessionRequest.java similarity index 74% rename from src/main/java/com/honey/honey/dto/CreateSessionRequest.java rename to src/main/java/com/lottery/lottery/dto/CreateSessionRequest.java index a1d0bbb..69d4812 100644 --- a/src/main/java/com/honey/honey/dto/CreateSessionRequest.java +++ b/src/main/java/com/lottery/lottery/dto/CreateSessionRequest.java @@ -1,4 +1,4 @@ -package com.honey.honey.dto; +package com.lottery.lottery.dto; import lombok.Data; diff --git a/src/main/java/com/honey/honey/dto/CreateSessionResponse.java b/src/main/java/com/lottery/lottery/dto/CreateSessionResponse.java similarity index 89% rename from src/main/java/com/honey/honey/dto/CreateSessionResponse.java rename to src/main/java/com/lottery/lottery/dto/CreateSessionResponse.java index 7f08372..970d954 100644 --- a/src/main/java/com/honey/honey/dto/CreateSessionResponse.java +++ b/src/main/java/com/lottery/lottery/dto/CreateSessionResponse.java @@ -1,4 +1,4 @@ -package com.honey.honey.dto; +package com.lottery.lottery.dto; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/src/main/java/com/lottery/lottery/dto/CreateTicketRequest.java b/src/main/java/com/lottery/lottery/dto/CreateTicketRequest.java new file mode 100644 index 0000000..a433aa1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CreateTicketRequest.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateTicketRequest { + + @NotBlank(message = "Subject is required") + @Size(min = 5, max = 100, message = "Subject must be between 5 and 100 characters") + private String subject; + + @NotBlank(message = "Message is required") + @Size(min = 3, max = 2000, message = "Message must be between 3 and 2000 characters") + private String message; +} + + + diff --git a/src/main/java/com/lottery/lottery/dto/CryptoDepositMethodsResponse.java b/src/main/java/com/lottery/lottery/dto/CryptoDepositMethodsResponse.java new file mode 100644 index 0000000..0871816 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CryptoDepositMethodsResponse.java @@ -0,0 +1,52 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** Response from external GET /api/v1/deposit-methods */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class CryptoDepositMethodsResponse { + + @JsonProperty("request_info") + private RequestInfo requestInfo; + + @JsonProperty("result") + private Result result; + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class RequestInfo { + @JsonProperty("error_code") + private Integer errorCode; + @JsonProperty("error_message") + private String errorMessage; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Result { + @JsonProperty("active_methods") + private List activeMethods; + @JsonProperty("hash") + private String hash; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ActiveMethod { + @JsonProperty("pid") + private Integer pid; + @JsonProperty("name") + private String name; + @JsonProperty("network") + private String network; + @JsonProperty("example") + private String example; + @JsonProperty("min_deposit_sum") + private Double minDepositSum; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/CryptoWithdrawalResponse.java b/src/main/java/com/lottery/lottery/dto/CryptoWithdrawalResponse.java new file mode 100644 index 0000000..fb4bbc8 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/CryptoWithdrawalResponse.java @@ -0,0 +1,19 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Minimal response for POST /api/payments/crypto-withdrawal. + * Exposes only id and status; no internal or PII fields. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CryptoWithdrawalResponse { + private Long id; + private String status; +} diff --git a/src/main/java/com/lottery/lottery/dto/DailyBonusStatusDto.java b/src/main/java/com/lottery/lottery/dto/DailyBonusStatusDto.java new file mode 100644 index 0000000..9f5c9fa --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/DailyBonusStatusDto.java @@ -0,0 +1,19 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DailyBonusStatusDto { + private Integer taskId; + private Boolean available; // true if bonus can be claimed, false if on cooldown + private Long cooldownSeconds; // Remaining cooldown time in seconds (null if available) + private Long rewardAmount; // Reward amount in bigint format (1 ticket = 1000000) +} + + diff --git a/src/main/java/com/lottery/lottery/dto/DepositAddressApiRequest.java b/src/main/java/com/lottery/lottery/dto/DepositAddressApiRequest.java new file mode 100644 index 0000000..e056058 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/DepositAddressApiRequest.java @@ -0,0 +1,43 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; + +/** + * Request body for external POST api/v1/deposit-address. + */ +@Data +@Builder +public class DepositAddressApiRequest { + + @JsonProperty("pid") + private Integer pid; + + @JsonProperty("amount_usd") + private Double amountUsd; + + @JsonProperty("user_data") + private UserData userData; + + @Data + @Builder + public static class UserData { + @JsonProperty("internal_id") + private Integer internalId; + @JsonProperty("screen_name") + private String screenName; + @JsonProperty("tg_username") + private String tgUsername; + @JsonProperty("tg_id") + private String tgId; + @JsonProperty("country_code") + private String countryCode; + @JsonProperty("device_code") + private String deviceCode; + @JsonProperty("language_code") + private String languageCode; + @JsonProperty("user_ip") + private String userIp; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/DepositAddressRequest.java b/src/main/java/com/lottery/lottery/dto/DepositAddressRequest.java new file mode 100644 index 0000000..ea86a6d --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/DepositAddressRequest.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +/** + * Request from frontend to get a crypto deposit address. + * usdAmount: decimal, e.g. 3.25 USD. + */ +@Data +public class DepositAddressRequest { + private Integer pid; // PID from deposit-methods + private Double usdAmount; // USD as decimal, e.g. 3.25 +} diff --git a/src/main/java/com/lottery/lottery/dto/DepositAddressResponse.java b/src/main/java/com/lottery/lottery/dto/DepositAddressResponse.java new file mode 100644 index 0000000..fca28cf --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/DepositAddressResponse.java @@ -0,0 +1,43 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * Response from external POST api/v1/deposit-address (and returned to frontend). + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class DepositAddressResponse { + + @JsonProperty("request_info") + private RequestInfo requestInfo; + + @JsonProperty("result") + private Result result; + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class RequestInfo { + @JsonProperty("error_code") + private Integer errorCode; + @JsonProperty("error_message") + private String errorMessage; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Result { + @JsonProperty("ps_id") + private Integer psId; + @JsonProperty("name") + private String name; + @JsonProperty("network") + private String network; + @JsonProperty("address") + private String address; + @JsonProperty("amount_coins") + private String amountCoins; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/DepositAddressResultDto.java b/src/main/java/com/lottery/lottery/dto/DepositAddressResultDto.java new file mode 100644 index 0000000..e9bc807 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/DepositAddressResultDto.java @@ -0,0 +1,20 @@ +package com.lottery.lottery.dto; + +import lombok.Builder; +import lombok.Data; + +/** + * Result returned to frontend after getting deposit address from crypto API. + * No payment record is created at this step. + */ +@Data +@Builder +public class DepositAddressResultDto { + private String address; + private String amountCoins; + private String name; + private String network; + private Integer psId; + /** Minimum deposit for this method (from crypto_deposit_methods.min_deposit_sum), for display on Payment Confirmation. Value as in DB, e.g. 2.50, 40.00. */ + private Double minAmount; +} diff --git a/src/main/java/com/lottery/lottery/dto/DepositMethodsDto.java b/src/main/java/com/lottery/lottery/dto/DepositMethodsDto.java new file mode 100644 index 0000000..b64b532 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/DepositMethodsDto.java @@ -0,0 +1,32 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +/** Response for GET /api/payments/deposit-methods */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DepositMethodsDto { + + private BigDecimal minimumDeposit; + private List activeMethods; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class DepositMethodItemDto { + private Integer pid; + private String name; + private String network; + private String example; + private BigDecimal minDepositSum; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/ErrorResponse.java b/src/main/java/com/lottery/lottery/dto/ErrorResponse.java new file mode 100644 index 0000000..bddcf8d --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/ErrorResponse.java @@ -0,0 +1,16 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ErrorResponse { + private String message; +} + + + + diff --git a/src/main/java/com/lottery/lottery/dto/ExternalDepositWebhookRequest.java b/src/main/java/com/lottery/lottery/dto/ExternalDepositWebhookRequest.java new file mode 100644 index 0000000..049dd19 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/ExternalDepositWebhookRequest.java @@ -0,0 +1,18 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * Request body for 3rd party deposit webhook: POST /api/deposit_webhook/{token}. + * usd_amount: decimal, e.g. 1.45 (3rd party sends as number). + */ +@Data +public class ExternalDepositWebhookRequest { + + @JsonProperty("user_id") + private Integer userId; + + @JsonProperty("usd_amount") + private Double usdAmount; +} diff --git a/src/main/java/com/lottery/lottery/dto/GameHistoryEntryDto.java b/src/main/java/com/lottery/lottery/dto/GameHistoryEntryDto.java new file mode 100644 index 0000000..02e26df --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/GameHistoryEntryDto.java @@ -0,0 +1,29 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * DTO for a single game history entry. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GameHistoryEntryDto { + /** + * Amount in bigint format (positive for wins, negative for losses). + * Example: +900000000 means +900.0000 (win of 900 tickets) + * Example: -100000000 means -100.0000 (loss of 100 tickets) + */ + private Long amount; + + /** + * Date formatted as dd.MM at HH:mm (e.g., "13.01 at 22:29") + */ + private String date; +} + + diff --git a/src/main/java/com/lottery/lottery/dto/GameRoomStateDto.java b/src/main/java/com/lottery/lottery/dto/GameRoomStateDto.java new file mode 100644 index 0000000..69be243 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/GameRoomStateDto.java @@ -0,0 +1,63 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GameRoomStateDto { + @JsonProperty("rN") + private Integer roomNumber; + + @JsonProperty("rI") + private Long roundId; // Current round id (null when no active round) + + @JsonProperty("p") + private Integer phase; // 1=WAITING, 2=COUNTDOWN, 3=SPINNING, 4=RESOLUTION + + @JsonProperty("tB") + private Long totalBet; // In tickets (not bigint) + + @JsonProperty("rP") + private Integer registeredPlayers; // Users registered in current round + + @JsonProperty("cU") + private Integer connectedUsers; // Total users connected to room (regardless of round participation) + + @JsonProperty("aR") + private Map allRoomsConnectedUsers; // Connected users count for all rooms (roomNumber -> count) + + @JsonProperty("mB") + private Long minBet; // Minimum bet for this room (in tickets, not bigint) + + @JsonProperty("mX") + private Long maxBet; // Maximum bet for this room (in tickets, not bigint) + + @JsonProperty("cE") + private Instant countdownEndAt; + + @JsonProperty("cR") + private Long countdownRemainingSeconds; + + @JsonProperty("ps") + private List participants; + + @JsonProperty("w") + private WinnerDto winner; + + @JsonProperty("sD") + private Long spinDuration; // milliseconds + + @JsonProperty("sI") + private Long stopIndex; // for spin animation +} + diff --git a/src/main/java/com/lottery/lottery/dto/JoinRoundRequest.java b/src/main/java/com/lottery/lottery/dto/JoinRoundRequest.java new file mode 100644 index 0000000..2577506 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/JoinRoundRequest.java @@ -0,0 +1,24 @@ +package com.lottery.lottery.dto; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class JoinRoundRequest { + @NotNull(message = "Room number is required") + @Min(value = 1, message = "Room number must be between 1 and 3") + @Max(value = 3, message = "Room number must be between 1 and 3") + private Integer roomNumber; + + @NotNull(message = "Bet amount is required") + @Positive(message = "Bet amount must be a positive integer") + private Long betAmount; +} + diff --git a/src/main/java/com/lottery/lottery/dto/JoinRoundResult.java b/src/main/java/com/lottery/lottery/dto/JoinRoundResult.java new file mode 100644 index 0000000..0765765 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/JoinRoundResult.java @@ -0,0 +1,17 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Result of joining a round (used by remote bet API to return state and bet amount for response). + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class JoinRoundResult { + private GameRoomStateDto state; + /** Bet tickets to report in API response (amount added this call, or current bet when unique=true no-op). */ + private int betTicketsForResponse; +} diff --git a/src/main/java/com/lottery/lottery/dto/MessageDto.java b/src/main/java/com/lottery/lottery/dto/MessageDto.java new file mode 100644 index 0000000..cc07b21 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/MessageDto.java @@ -0,0 +1,24 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageDto { + private Long id; + private Long ticketId; + private Integer userId; + private String message; + private Instant createdAt; + private Boolean isFromSupport; // true if message is from support agent (different user_id than ticket owner) +} + + + diff --git a/src/main/java/com/lottery/lottery/dto/NotifyBroadcastRequest.java b/src/main/java/com/lottery/lottery/dto/NotifyBroadcastRequest.java new file mode 100644 index 0000000..b9fc0f4 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/NotifyBroadcastRequest.java @@ -0,0 +1,21 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +@Data +public class NotifyBroadcastRequest { + /** HTML/text message. */ + private String message; + /** Optional image URL (ignored if videoUrl is set). */ + private String imageUrl; + /** Optional video URL (takes priority over imageUrl). */ + private String videoUrl; + /** Internal user id range start (default 1). */ + private Integer userIdFrom; + /** Internal user id range end (default max id). */ + private Integer userIdTo; + /** Optional button text; if set, adds an inline button that opens the mini app. */ + private String buttonText; + /** When true, skip users whose latest notification_audit record has status FAILED (e.g. blocked the bot). When false or null, send to all in range. */ + private Boolean ignoreBlocked; +} diff --git a/src/main/java/com/lottery/lottery/dto/ParticipantDto.java b/src/main/java/com/lottery/lottery/dto/ParticipantDto.java new file mode 100644 index 0000000..8991d36 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/ParticipantDto.java @@ -0,0 +1,23 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ParticipantDto { + @JsonProperty("uI") + private Integer userId; + + @JsonProperty("b") + private Long bet; // In tickets (not bigint) + + @JsonProperty("aU") + private String avatarUrl; +} + diff --git a/src/main/java/com/lottery/lottery/dto/PaymentInvoiceResponse.java b/src/main/java/com/lottery/lottery/dto/PaymentInvoiceResponse.java new file mode 100644 index 0000000..c6f7a9f --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/PaymentInvoiceResponse.java @@ -0,0 +1,19 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PaymentInvoiceResponse { + private String invoiceId; // Order ID to be used in Telegram invoice + private String invoiceUrl; // Invoice URL to open in Telegram (null for crypto) + private Integer starsAmount; // Amount in Stars (legacy) + private Double usdAmount; // USD as decimal, e.g. 3.25 (crypto) + private Long ticketsAmount; // Tickets amount in bigint format +} + diff --git a/src/main/java/com/lottery/lottery/dto/PaymentWebhookRequest.java b/src/main/java/com/lottery/lottery/dto/PaymentWebhookRequest.java new file mode 100644 index 0000000..5fc2716 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/PaymentWebhookRequest.java @@ -0,0 +1,16 @@ +package com.lottery.lottery.dto; + +import lombok.Data; + +@Data +public class PaymentWebhookRequest { + private String orderId; // Order ID from Telegram invoice + private Long telegramUserId; // Telegram user ID + private String telegramPaymentChargeId; // Telegram payment charge ID + private String telegramProviderPaymentChargeId; // Telegram provider payment charge ID + private Integer starsAmount; // Amount in Stars +} + + + + diff --git a/src/main/java/com/lottery/lottery/dto/PayoutHistoryEntryDto.java b/src/main/java/com/lottery/lottery/dto/PayoutHistoryEntryDto.java new file mode 100644 index 0000000..6178408 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/PayoutHistoryEntryDto.java @@ -0,0 +1,20 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * DTO for payout history table entries. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayoutHistoryEntryDto { + private Long amount; // Total in bigint format (will be converted to tickets on frontend) + private String date; // Formatted as dd.MM at HH:mm (e.g., "13.01 at 22:29") + private String status; // PROCESSING, COMPLETED, CANCELLED +} + diff --git a/src/main/java/com/lottery/lottery/dto/PayoutResponse.java b/src/main/java/com/lottery/lottery/dto/PayoutResponse.java new file mode 100644 index 0000000..45dc120 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/PayoutResponse.java @@ -0,0 +1,24 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayoutResponse { + private Long id; + private String username; + private String type; + private String giftName; + private Long total; + private Integer starsAmount; + private Integer quantity; + private String status; + private Long createdAt; // Unix timestamp in milliseconds + private Long resolvedAt; // Unix timestamp in milliseconds, null if not resolved +} + diff --git a/src/main/java/com/lottery/lottery/dto/PromotionDetailDto.java b/src/main/java/com/lottery/lottery/dto/PromotionDetailDto.java new file mode 100644 index 0000000..740fc6c --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/PromotionDetailDto.java @@ -0,0 +1,29 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PromotionDetailDto { + private Integer id; + private String type; + private String status; + private Instant startTime; + private Instant endTime; + private Long totalReward; + private List leaderboard; + /** 1-based position of current user (0 if not in leaderboard). */ + private int userPosition; + /** Total number of participants. */ + private int userTotal; + private BigDecimal userPoints; +} diff --git a/src/main/java/com/lottery/lottery/dto/PromotionLeaderboardEntryDto.java b/src/main/java/com/lottery/lottery/dto/PromotionLeaderboardEntryDto.java new file mode 100644 index 0000000..4f48d13 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/PromotionLeaderboardEntryDto.java @@ -0,0 +1,20 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PromotionLeaderboardEntryDto { + private int place; + private String screenName; + private BigDecimal points; + /** Reward for this place in tickets (null if no reward for this place). */ + private Long rewardTickets; +} diff --git a/src/main/java/com/lottery/lottery/dto/PromotionListItemDto.java b/src/main/java/com/lottery/lottery/dto/PromotionListItemDto.java new file mode 100644 index 0000000..0785505 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/PromotionListItemDto.java @@ -0,0 +1,22 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PromotionListItemDto { + private Integer id; + private String type; + private String status; + private Instant startTime; + private Instant endTime; + /** Total prize fund in bigint (1 ticket = 1_000_000). */ + private Long totalReward; +} diff --git a/src/main/java/com/lottery/lottery/dto/QuickAnswerCreateRequest.java b/src/main/java/com/lottery/lottery/dto/QuickAnswerCreateRequest.java new file mode 100644 index 0000000..72872fb --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/QuickAnswerCreateRequest.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class QuickAnswerCreateRequest { + private String text; +} + diff --git a/src/main/java/com/lottery/lottery/dto/QuickAnswerDto.java b/src/main/java/com/lottery/lottery/dto/QuickAnswerDto.java new file mode 100644 index 0000000..a694a2b --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/QuickAnswerDto.java @@ -0,0 +1,18 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class QuickAnswerDto { + private Integer id; + private String text; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} + diff --git a/src/main/java/com/lottery/lottery/dto/RecentBonusClaimDto.java b/src/main/java/com/lottery/lottery/dto/RecentBonusClaimDto.java new file mode 100644 index 0000000..367af8a --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/RecentBonusClaimDto.java @@ -0,0 +1,25 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * DTO for recent daily bonus claims. + * Contains user information and claim timestamp. + * The claimedAt field contains the raw timestamp, but the date field contains the formatted string. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RecentBonusClaimDto { + private String avatarUrl; + private String screenName; + private LocalDateTime claimedAt; + private String date; // Formatted date string (dd.MM 'at' HH:mm) with timezone +} + diff --git a/src/main/java/com/lottery/lottery/dto/ReferralDto.java b/src/main/java/com/lottery/lottery/dto/ReferralDto.java new file mode 100644 index 0000000..3acc6bc --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/ReferralDto.java @@ -0,0 +1,19 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ReferralDto { + private String name; // screen_name from db_users_a + private Long commission; // to_referer_1/2/3 from db_users_d (bigint, needs to be divided by 1,000,000 on frontend) +} + + + + diff --git a/src/main/java/com/lottery/lottery/dto/ReferralLevelDto.java b/src/main/java/com/lottery/lottery/dto/ReferralLevelDto.java new file mode 100644 index 0000000..2f21c4f --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/ReferralLevelDto.java @@ -0,0 +1,25 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ReferralLevelDto { + private Integer level; // 1-5 + private Integer refererId; + private Integer referralCount; + private Long commissionsEarned; + private Long commissionsPaid; + /** Commissions earned in USD (converted from tickets: 1000 tickets = 1 USD). */ + private BigDecimal commissionsEarnedUsd; + /** Commissions paid in USD (converted from tickets). */ + private BigDecimal commissionsPaidUsd; +} + diff --git a/src/main/java/com/lottery/lottery/dto/RoomUpdateDto.java b/src/main/java/com/lottery/lottery/dto/RoomUpdateDto.java new file mode 100644 index 0000000..9a08b80 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/RoomUpdateDto.java @@ -0,0 +1,49 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Incremental update DTO for room state changes. + * Used to send only what changed instead of full state. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RoomUpdateDto { + + /** + * Update type: USER_JOINED, USER_LEFT, FULL_STATE, PHASE_CHANGED + */ + private String type; + + /** + * Participant data (for USER_JOINED) + */ + private ParticipantDto participant; + + /** + * User ID (for USER_LEFT) + */ + private Integer userId; + + /** + * Full state (for FULL_STATE - fallback when incremental not possible) + */ + private GameRoomStateDto fullState; + + /** + * Phase change data (for PHASE_CHANGED) + */ + private String phase; + private Long countdownRemaining; + private WinnerDto winner; + private Long stopIndex; + private Long spinDuration; +} + + + diff --git a/src/main/java/com/lottery/lottery/dto/SupportTicketReplyRequest.java b/src/main/java/com/lottery/lottery/dto/SupportTicketReplyRequest.java new file mode 100644 index 0000000..7a5b515 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/SupportTicketReplyRequest.java @@ -0,0 +1,18 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotBlank; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SupportTicketReplyRequest { + @NotBlank(message = "Message is required") + private String message; +} + diff --git a/src/main/java/com/lottery/lottery/dto/TaskDto.java b/src/main/java/com/lottery/lottery/dto/TaskDto.java new file mode 100644 index 0000000..62f7883 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/TaskDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TaskDto { + private Integer id; + private String type; // referral, follow, other + private Long requirement; // For referral tasks: number of friends. For other tasks: deposit_total threshold in bigint (frontend converts) + private Long rewardAmount; // bigint format + private String rewardType; // Tickets (all tasks use Tickets as reward type) + private String title; + private String description; + private Integer displayOrder; + private Boolean claimed; // Whether user has claimed this task + private String progress; // Progress string like "1008 / 30" or "CLAIMED" (for referral) or null (for follow/other - frontend handles) + private Long currentValue; // Current value in bigint format (referals1 for referral, depositTotal for other). Frontend converts for display. + private String localizedRewardText; // Localized reward text like "+2 Билеты" or "+5 Билетов" (for proper grammar) +} + diff --git a/src/main/java/com/lottery/lottery/dto/TelegramApiResponse.java b/src/main/java/com/lottery/lottery/dto/TelegramApiResponse.java new file mode 100644 index 0000000..4927de1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/TelegramApiResponse.java @@ -0,0 +1,19 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * Telegram Bot API response wrapper. + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class TelegramApiResponse { + + @JsonProperty("ok") + private Boolean ok; + + @JsonProperty("description") + private String description; +} diff --git a/src/main/java/com/lottery/lottery/dto/TelegramSendResult.java b/src/main/java/com/lottery/lottery/dto/TelegramSendResult.java new file mode 100644 index 0000000..cbbdbe4 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/TelegramSendResult.java @@ -0,0 +1,17 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Result of a single Telegram send call for broadcast audit. + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TelegramSendResult { + private boolean success; + /** HTTP status code from Telegram API (e.g. 200, 403, 429). */ + private int statusCode; +} diff --git a/src/main/java/com/lottery/lottery/dto/TicketDetailDto.java b/src/main/java/com/lottery/lottery/dto/TicketDetailDto.java new file mode 100644 index 0000000..38826cc --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/TicketDetailDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import com.lottery.lottery.model.SupportTicket.TicketStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TicketDetailDto { + private Long id; + private String subject; + private TicketStatus status; + private Instant createdAt; + private Instant updatedAt; + private List messages; +} + + + diff --git a/src/main/java/com/lottery/lottery/dto/TicketDto.java b/src/main/java/com/lottery/lottery/dto/TicketDto.java new file mode 100644 index 0000000..b908e2c --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/TicketDto.java @@ -0,0 +1,25 @@ +package com.lottery.lottery.dto; + +import com.lottery.lottery.model.SupportTicket.TicketStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TicketDto { + private Long id; + private String subject; + private TicketStatus status; + private Instant createdAt; + private Instant updatedAt; + private Integer messageCount; +} + + + diff --git a/src/main/java/com/lottery/lottery/dto/TransactionDto.java b/src/main/java/com/lottery/lottery/dto/TransactionDto.java new file mode 100644 index 0000000..6c732e8 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/TransactionDto.java @@ -0,0 +1,44 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * DTO for a transaction entry in transaction history. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TransactionDto { + /** + * Amount in bigint format (positive for credits, negative for debits). + * Example: +900000000 means +900.0000 (credit) + * Example: -100000000 means -100.0000 (debit) + */ + private Long amount; + + /** + * Date formatted as dd.MM at HH:mm (e.g., "13.01 at 22:29") + */ + private String date; + + /** + * Transaction type: DEPOSIT, WITHDRAWAL, WIN, BET, TASK_BONUS, DAILY_BONUS + */ + private String type; + + /** + * Task ID for TASK_BONUS type (null for DAILY_BONUS and other types) + */ + private Integer taskId; + + /** + * Round ID for WIN/BET type (null for other types) + */ + private Long roundId; +} + + diff --git a/src/main/java/com/lottery/lottery/dto/UserCheckDto.java b/src/main/java/com/lottery/lottery/dto/UserCheckDto.java new file mode 100644 index 0000000..3e04a49 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/UserCheckDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * DTO for user check endpoint response. + * Contains user information for external applications. + * Always returned with HTTP 200; use {@code found} to distinguish user-not-found from success. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserCheckDto { + /** When false, user was not found by telegramId; other fields are null. */ + private Boolean found; + private Integer dateReg; + private Double tickets; // balance_a / 1,000,000 + private Integer depositTotal; // Sum of completed payments stars_amount + private Integer refererId; // referer_id_1 from db_users_d + private Integer roundsPlayed; // rounds_played from db_users_b +} + diff --git a/src/main/java/com/lottery/lottery/dto/UserDepositDto.java b/src/main/java/com/lottery/lottery/dto/UserDepositDto.java new file mode 100644 index 0000000..d4710fd --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/UserDepositDto.java @@ -0,0 +1,23 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.Instant; + +/** Deposit (payment) row for admin user detail Deposits tab. */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserDepositDto { + private Long id; + private BigDecimal usdAmount; + private String status; + private String orderId; + private Instant createdAt; + private Instant completedAt; +} diff --git a/src/main/java/com/lottery/lottery/dto/UserDto.java b/src/main/java/com/lottery/lottery/dto/UserDto.java new file mode 100644 index 0000000..190ffae --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/UserDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserDto { + private Integer id; // User ID + private Long telegram_id; + private String username; + private String screenName; // User's screen name + private Integer dateReg; // Registration date (Unix timestamp in seconds) + private String ip; + private Long balanceA; // Balance (stored as bigint, represents number with 6 decimal places) + private String avatarUrl; // Public URL of user's avatar + private String languageCode; // User's language preference (EN, RU, DE, IT, NL, PL, FR, ES, ID, TR) + private Boolean paymentEnabled; // Runtime toggle: deposits (Store, Payment Options, etc.) allowed + private Boolean payoutEnabled; // Runtime toggle: withdrawals (Payout, crypto withdrawal) allowed + private Boolean promotionsEnabled; // Runtime toggle: Promotions button and /api/promotions endpoints +} + diff --git a/src/main/java/com/lottery/lottery/dto/UserProgressDto.java b/src/main/java/com/lottery/lottery/dto/UserProgressDto.java new file mode 100644 index 0000000..7598599 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/UserProgressDto.java @@ -0,0 +1,15 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserProgressDto { + private Integer referals1; // Level 1 referrals count (used for referral tasks) +} + diff --git a/src/main/java/com/lottery/lottery/dto/UserWithdrawalDto.java b/src/main/java/com/lottery/lottery/dto/UserWithdrawalDto.java new file mode 100644 index 0000000..fbaad15 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/UserWithdrawalDto.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.Instant; + +/** Withdrawal (CRYPTO payout) row for admin user detail Withdrawals tab. */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserWithdrawalDto { + private Long id; + private BigDecimal usdAmount; + private String cryptoName; // ticker (e.g. USDT) + private String amountToSend; // coins amount + private String txhash; // transaction id + private String status; + private Integer paymentId; // external payment id + private Instant createdAt; + private Instant resolvedAt; +} diff --git a/src/main/java/com/lottery/lottery/dto/WinnerDto.java b/src/main/java/com/lottery/lottery/dto/WinnerDto.java new file mode 100644 index 0000000..40bd773 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/WinnerDto.java @@ -0,0 +1,35 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WinnerDto { + @JsonProperty("uI") + private Integer userId; + + @JsonProperty("sN") + private String screenName; + + @JsonProperty("aU") + private String avatarUrl; + + @JsonProperty("b") + private Long bet; // In tickets (not bigint) + + @JsonProperty("pO") + private Long payout; // In bigint format (with 6 decimal places) + + @JsonProperty("cO") + private Long commission; // In bigint format (with 6 decimal places) + + @JsonProperty("wC") + private Double winChance; // Winner's chance percentage (calculated from round's actual participant bets) +} + diff --git a/src/main/java/com/lottery/lottery/dto/WithdrawalApiRequest.java b/src/main/java/com/lottery/lottery/dto/WithdrawalApiRequest.java new file mode 100644 index 0000000..8d319e1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/WithdrawalApiRequest.java @@ -0,0 +1,55 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; + +/** + * Request body for external POST api/v1/withdrawal. + */ +@Data +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class WithdrawalApiRequest { + + @JsonProperty("pid") + private Integer pid; + + @JsonProperty("user_id") + private Integer userId; + + @JsonProperty("wallet") + private String wallet; + + @JsonProperty("amount_usd") + private Double amountUsd; + + /** When 1, crypto system treats payout as manual. Omitted when null. Set only if user completed referral task 50 or 100. */ + @JsonProperty("manual_pay") + private Integer manualPay; + + @JsonProperty("user_data") + private UserData userData; + + @Data + @Builder + public static class UserData { + @JsonProperty("internal_id") + private Integer internalId; + @JsonProperty("screen_name") + private String screenName; + @JsonProperty("tg_username") + private String tgUsername; + @JsonProperty("tg_id") + private String tgId; + @JsonProperty("country_code") + private String countryCode; + @JsonProperty("device_code") + private String deviceCode; + @JsonProperty("language_code") + private String languageCode; + @JsonProperty("user_ip") + private String userIp; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/WithdrawalApiResponse.java b/src/main/java/com/lottery/lottery/dto/WithdrawalApiResponse.java new file mode 100644 index 0000000..4999750 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/WithdrawalApiResponse.java @@ -0,0 +1,62 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * Response from external POST api/v1/withdrawal. + * Success: result.payment present. Business error: result.error with error_code 1 (wallet invalid) or other. + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class WithdrawalApiResponse { + + @JsonProperty("request_info") + private RequestInfo requestInfo; + + @JsonProperty("result") + private Result result; + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class RequestInfo { + @JsonProperty("error_code") + private Integer errorCode; + @JsonProperty("error_message") + private String errorMessage; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Result { + @JsonProperty("payment") + private Payment payment; + @JsonProperty("error") + private ResultError error; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Payment { + @JsonProperty("payment_id") + private Integer paymentId; + @JsonProperty("ticker") + private String ticker; + @JsonProperty("amount_coins") + private String amountCoins; + @JsonProperty("comission_coins") + private String comissionCoins; + @JsonProperty("amount_to_send") + private String amountToSend; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ResultError { + @JsonProperty("error_code") + private Integer errorCode; + @JsonProperty("error_text") + private String errorText; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/WithdrawalInfoApiResponse.java b/src/main/java/com/lottery/lottery/dto/WithdrawalInfoApiResponse.java new file mode 100644 index 0000000..f27917d --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/WithdrawalInfoApiResponse.java @@ -0,0 +1,71 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * Response from external GET api/v1/withdrawals-info/{payment_id}. + * status in payment_list: "-1" PROCESSING, "0" WAITING, "1" COMPLETED, "2" CANCELLED. + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class WithdrawalInfoApiResponse { + + @JsonProperty("request_info") + private RequestInfo requestInfo; + + @JsonProperty("result") + private Result result; + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class RequestInfo { + @JsonProperty("error_code") + private Integer errorCode; + @JsonProperty("error_message") + private String errorMessage; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Result { + @JsonProperty("payment_list") + private List paymentList; + @JsonProperty("hash") + private String hash; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class PaymentItem { + @JsonProperty("payment_id") + private Integer paymentId; + @JsonProperty("user_id") + private Integer userId; + @JsonProperty("name") + private String name; + @JsonProperty("ticker") + private String ticker; + @JsonProperty("wallet") + private String wallet; + @JsonProperty("amount_usd") + private Double amountUsd; + @JsonProperty("amount_coins") + private String amountCoins; + @JsonProperty("amount_coins_fee") + private String amountCoinsFee; + @JsonProperty("txhash") + private String txhash; + @JsonProperty("status") + private String status; // "-1" PROCESSING, "0" WAITING, "1" COMPLETED, "2" CANCELLED + @JsonProperty("status_text") + private String statusText; + @JsonProperty("date_added") + private String dateAdded; + @JsonProperty("last_update") + private String lastUpdate; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/WithdrawalMethodDetailsDto.java b/src/main/java/com/lottery/lottery/dto/WithdrawalMethodDetailsDto.java new file mode 100644 index 0000000..a21962a --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/WithdrawalMethodDetailsDto.java @@ -0,0 +1,30 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * Single withdrawal method details from external API (rate and fee for Payout Confirmation screen). + */ +@Data +@Builder +public class WithdrawalMethodDetailsDto { + + @JsonProperty("pid") + private Integer pid; + + @JsonProperty("name") + private String name; + + @JsonProperty("ticker") + private String ticker; + + @JsonProperty("rateUsd") + private BigDecimal rateUsd; + + @JsonProperty("totalFeeUsd") + private BigDecimal mishaFeeUsd; +} diff --git a/src/main/java/com/lottery/lottery/dto/WithdrawalMethodsApiResponse.java b/src/main/java/com/lottery/lottery/dto/WithdrawalMethodsApiResponse.java new file mode 100644 index 0000000..e9d4b95 --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/WithdrawalMethodsApiResponse.java @@ -0,0 +1,62 @@ +package com.lottery.lottery.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** Response from external GET /api/v1/withdrawal-methods */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class WithdrawalMethodsApiResponse { + + @JsonProperty("request_info") + private RequestInfo requestInfo; + + @JsonProperty("result") + private Result result; + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class RequestInfo { + @JsonProperty("error_code") + private Integer errorCode; + @JsonProperty("error_message") + private String errorMessage; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Result { + @JsonProperty("active_methods") + private List activeMethods; + @JsonProperty("hash") + private String hash; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ActiveMethod { + @JsonProperty("pid") + private Integer pid; + @JsonProperty("name") + private String name; + @JsonProperty("ticker") + private String ticker; + @JsonProperty("icon_id") + private String iconId; + @JsonProperty("fee_network") + private String feeNetwork; + @JsonProperty("fee_network_usd") + private String feeNetworkUsd; + @JsonProperty("min_amount_pay") + private String minAmountPay; + @JsonProperty("min_amount_pay_usd") + private String minAmountPayUsd; + @JsonProperty("rate_usd") + private String rateUsd; + @JsonProperty("misha_fee_usd") + private Double mishaFeeUsd; + } +} diff --git a/src/main/java/com/lottery/lottery/dto/WithdrawalMethodsDto.java b/src/main/java/com/lottery/lottery/dto/WithdrawalMethodsDto.java new file mode 100644 index 0000000..842250c --- /dev/null +++ b/src/main/java/com/lottery/lottery/dto/WithdrawalMethodsDto.java @@ -0,0 +1,31 @@ +package com.lottery.lottery.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +/** Response for GET /api/payments/withdrawal-methods */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WithdrawalMethodsDto { + + private List methods; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class WithdrawalMethodItemDto { + private Integer pid; + private String name; + private String network; + private String iconId; + private BigDecimal minWithdrawal; + } +} diff --git a/src/main/java/com/lottery/lottery/exception/BannedUserException.java b/src/main/java/com/lottery/lottery/exception/BannedUserException.java new file mode 100644 index 0000000..2391f95 --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/BannedUserException.java @@ -0,0 +1,12 @@ +package com.lottery.lottery.exception; + +/** + * Thrown when a banned user attempts to create a session or access the app. + * Handled by GlobalExceptionHandler to return 403 with code BANNED. + */ +public class BannedUserException extends RuntimeException { + + public BannedUserException(String message) { + super(message); + } +} diff --git a/src/main/java/com/lottery/lottery/exception/BetDecisionException.java b/src/main/java/com/lottery/lottery/exception/BetDecisionException.java new file mode 100644 index 0000000..ddce2ae --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/BetDecisionException.java @@ -0,0 +1,16 @@ +package com.lottery.lottery.exception; + +/** + * Thrown when bet amount cannot be decided (e.g. invalid bot range). + * Scheduler must not register the bot when this is thrown. + */ +public class BetDecisionException extends RuntimeException { + + public BetDecisionException(String message) { + super(message); + } + + public BetDecisionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/honey/honey/exception/ErrorResponse.java b/src/main/java/com/lottery/lottery/exception/ErrorResponse.java similarity index 84% rename from src/main/java/com/honey/honey/exception/ErrorResponse.java rename to src/main/java/com/lottery/lottery/exception/ErrorResponse.java index b0441ee..87cf256 100644 --- a/src/main/java/com/honey/honey/exception/ErrorResponse.java +++ b/src/main/java/com/lottery/lottery/exception/ErrorResponse.java @@ -1,4 +1,4 @@ -package com.honey.honey.exception; +package com.lottery.lottery.exception; import lombok.AllArgsConstructor; import lombok.Data; @@ -12,3 +12,4 @@ public class ErrorResponse { private String message; } + diff --git a/src/main/java/com/lottery/lottery/exception/GameException.java b/src/main/java/com/lottery/lottery/exception/GameException.java new file mode 100644 index 0000000..6529273 --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/GameException.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.exception; + +import lombok.Getter; + +/** + * Base exception for game-related errors with user-friendly messages. + */ +@Getter +public class GameException extends RuntimeException { + private final String userMessage; + + public GameException(String userMessage) { + super(userMessage); + this.userMessage = userMessage; + } + + public GameException(String userMessage, Throwable cause) { + super(userMessage, cause); + this.userMessage = userMessage; + } +} + + + + + diff --git a/src/main/java/com/lottery/lottery/exception/GlobalExceptionHandler.java b/src/main/java/com/lottery/lottery/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..e713d50 --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/GlobalExceptionHandler.java @@ -0,0 +1,115 @@ +package com.lottery.lottery.exception; + +import com.lottery.lottery.service.LocalizationService; +import jakarta.validation.ConstraintViolationException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.servlet.resource.NoResourceFoundException; + +@Slf4j +@RestControllerAdvice +@RequiredArgsConstructor +public class GlobalExceptionHandler { + + private final LocalizationService localizationService; + + @ExceptionHandler(BannedUserException.class) + public ResponseEntity handleBannedUser(BannedUserException ex) { + log.warn("Banned user access attempt: {}", ex.getMessage()); + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(new ErrorResponse("BANNED", ex.getMessage())); + } + + @ExceptionHandler(UnauthorizedException.class) + public ResponseEntity handleUnauthorized(UnauthorizedException ex) { + log.warn("Unauthorized: {}", ex.getMessage()); + String localizedMessage = ex.getMessage(); + // Try to localize if message looks like a message code + if (ex.getMessage() != null && ex.getMessage().contains(".")) { + try { + localizedMessage = localizationService.getMessage(ex.getMessage()); + } catch (Exception e) { + // Use original message if localization fails + } + } + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body(new ErrorResponse("UNAUTHORIZED", localizedMessage)); + } + + @ExceptionHandler(GameException.class) + public ResponseEntity handleGameException(GameException ex) { + log.warn("GameException: {}", ex.getUserMessage()); + String localizedMessage = ex.getUserMessage(); + // Try to localize if message looks like a message code (contains dots) + if (ex.getUserMessage() != null && ex.getUserMessage().contains(".") && !ex.getUserMessage().contains(" ")) { + try { + localizedMessage = localizationService.getMessage(ex.getUserMessage()); + } catch (Exception e) { + // Use original message if localization fails + } + } + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ErrorResponse("GAME_ERROR", localizedMessage)); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) { + String errorMessage = ex.getBindingResult().getFieldErrors().stream() + .map(error -> error.getDefaultMessage()) + .findFirst() + .orElse(localizationService.getMessage("validation.error.required", "Field")); + log.warn("Validation error: {}", errorMessage); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ErrorResponse("VALIDATION_ERROR", errorMessage)); + } + + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleConstraintViolation(ConstraintViolationException ex) { + String errorMessage = ex.getConstraintViolations().stream() + .map(violation -> violation.getMessage()) + .findFirst() + .orElse(localizationService.getMessage("validation.error.required", "Field")); + log.warn("Constraint violation: {}", errorMessage); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ErrorResponse("VALIDATION_ERROR", errorMessage)); + } + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseEntity handleMethodArgumentTypeMismatch(MethodArgumentTypeMismatchException ex) { + String paramName = ex.getName(); + String message = "telegramId".equals(paramName) + ? "Path parameter telegramId must be a numeric Telegram user ID (e.g. 757747558), not a placeholder." + : "Invalid value for parameter '" + paramName + "': expected " + (ex.getRequiredType() != null ? ex.getRequiredType().getSimpleName() : "valid type"); + log.warn("Invalid request parameter: {} = '{}'", paramName, ex.getValue()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ErrorResponse("INVALID_PARAMETER", message)); + } + + @ExceptionHandler(NoResourceFoundException.class) + public ResponseEntity handleNoResourceFound(NoResourceFoundException ex) { + // Only log avatar-related missing resources without stacktrace + String resourcePath = ex.getResourcePath(); + if (resourcePath != null && resourcePath.startsWith("/avatars/")) { + log.debug("Avatar not found: {}", resourcePath); + } else { + log.debug("Resource not found: {}", resourcePath); + } + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGeneric(Exception ex) { + log.error("Unexpected error", ex); + String localizedMessage = localizationService.getMessage("common.error.unknown"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("INTERNAL_ERROR", localizedMessage)); + } +} + + diff --git a/src/main/java/com/lottery/lottery/exception/InsufficientBalanceException.java b/src/main/java/com/lottery/lottery/exception/InsufficientBalanceException.java new file mode 100644 index 0000000..57900db --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/InsufficientBalanceException.java @@ -0,0 +1,8 @@ +package com.lottery.lottery.exception; + +public class InsufficientBalanceException extends GameException { + public InsufficientBalanceException(String localizedMessage) { + super(localizedMessage); + } +} + diff --git a/src/main/java/com/lottery/lottery/exception/InvalidBetAmountException.java b/src/main/java/com/lottery/lottery/exception/InvalidBetAmountException.java new file mode 100644 index 0000000..6dd2760 --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/InvalidBetAmountException.java @@ -0,0 +1,30 @@ +package com.lottery.lottery.exception; + +import com.lottery.lottery.service.LocalizationService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * Exception for invalid bet amounts. + * Uses localization service to get user's language preference. + */ +public class InvalidBetAmountException extends GameException { + public InvalidBetAmountException(String localizedMessage) { + super(localizedMessage); + } + + /** + * Factory method to create exception with localized message. + * Converts bigint values to display format (divide by 1,000,000). + */ + public static InvalidBetAmountException create(LocalizationService localizationService, long minBet, long maxBet) { + // Convert bigint to display format for user-friendly message + double minBetDisplay = minBet / 1_000_000.0; + double maxBetDisplay = maxBet / 1_000_000.0; + String message = localizationService.getMessage("game.error.invalidBetAmount", + String.format("%.2f", minBetDisplay), + String.format("%.2f", maxBetDisplay)); + return new InvalidBetAmountException(message); + } +} + diff --git a/src/main/java/com/lottery/lottery/exception/RoomNotJoinableException.java b/src/main/java/com/lottery/lottery/exception/RoomNotJoinableException.java new file mode 100644 index 0000000..e8543c9 --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/RoomNotJoinableException.java @@ -0,0 +1,14 @@ +package com.lottery.lottery.exception; + +import com.lottery.lottery.model.GamePhase; +import com.lottery.lottery.service.LocalizationService; + +public class RoomNotJoinableException extends GameException { + public RoomNotJoinableException(GamePhase currentPhase, LocalizationService localizationService) { + super(localizationService.getMessage("game.error.roomNotJoinable")); + } +} + + + + diff --git a/src/main/java/com/honey/honey/exception/UnauthorizedException.java b/src/main/java/com/lottery/lottery/exception/UnauthorizedException.java similarity index 78% rename from src/main/java/com/honey/honey/exception/UnauthorizedException.java rename to src/main/java/com/lottery/lottery/exception/UnauthorizedException.java index 1b2346c..b6dcc3c 100644 --- a/src/main/java/com/honey/honey/exception/UnauthorizedException.java +++ b/src/main/java/com/lottery/lottery/exception/UnauthorizedException.java @@ -1,4 +1,4 @@ -package com.honey.honey.exception; +package com.lottery.lottery.exception; public class UnauthorizedException extends RuntimeException { public UnauthorizedException(String message) { @@ -6,3 +6,4 @@ public class UnauthorizedException extends RuntimeException { } } + diff --git a/src/main/java/com/lottery/lottery/exception/UserAlreadyJoinedException.java b/src/main/java/com/lottery/lottery/exception/UserAlreadyJoinedException.java new file mode 100644 index 0000000..16363ab --- /dev/null +++ b/src/main/java/com/lottery/lottery/exception/UserAlreadyJoinedException.java @@ -0,0 +1,12 @@ +package com.lottery.lottery.exception; + +public class UserAlreadyJoinedException extends GameException { + public UserAlreadyJoinedException() { + super("You have already joined this round. Please wait for the next round."); + } +} + + + + + diff --git a/src/main/java/com/lottery/lottery/health/DatabaseHealthIndicator.java b/src/main/java/com/lottery/lottery/health/DatabaseHealthIndicator.java new file mode 100644 index 0000000..9df9af6 --- /dev/null +++ b/src/main/java/com/lottery/lottery/health/DatabaseHealthIndicator.java @@ -0,0 +1,130 @@ +package com.lottery.lottery.health; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Slf4j +@Component +@RequiredArgsConstructor +public class DatabaseHealthIndicator implements HealthIndicator { + + private final DataSource dataSource; + + @Override + public Health health() { + try (Connection connection = dataSource.getConnection()) { + if (!connection.isValid(1)) { + return Health.down() + .withDetail("database", "MySQL") + .withDetail("status", "Connection invalid") + .build(); + } + + // Check if Flyway schema history table exists and migrations are complete + // This ensures migrations have run before health check passes + boolean migrationsComplete = checkFlywayMigrations(connection); + + if (migrationsComplete) { + return Health.up() + .withDetail("database", "MySQL") + .withDetail("status", "Connected") + .withDetail("migrations", "Complete") + .build(); + } else { + // Migrations not complete yet - return DOWN to prevent health check from passing + return Health.down() + .withDetail("database", "MySQL") + .withDetail("status", "Connected") + .withDetail("migrations", "Pending") + .build(); + } + } catch (SQLException e) { + log.error("Database health check failed", e); + return Health.down() + .withDetail("database", "MySQL") + .withDetail("error", e.getMessage()) + .build(); + } + } + + /** + * Checks if Flyway migrations are complete by verifying: + * 1. The flyway_schema_history table exists + * 2. There are no pending migrations (all migrations have been applied) + * + * This prevents the health check from passing before migrations complete, + * which is important during rolling updates when both old and new containers + * might be running simultaneously. + */ + private boolean checkFlywayMigrations(Connection connection) { + try { + // Check if flyway_schema_history table exists + // Flyway creates this table in the same database as the application + String checkTableQuery = "SELECT COUNT(*) as count FROM information_schema.tables " + + "WHERE table_schema = DATABASE() AND table_name = 'flyway_schema_history'"; + + try (PreparedStatement stmt = connection.prepareStatement(checkTableQuery); + ResultSet rs = stmt.executeQuery()) { + + if (!rs.next() || rs.getInt("count") == 0) { + // Table doesn't exist yet - migrations haven't started + log.debug("Flyway schema history table not found - migrations not started"); + return false; + } + } + + // Check if there are any pending migrations + // Pending migrations have success = 0 or don't exist in the history + // We check by comparing the installed_rank with the expected count + // If all migrations are applied, the highest installed_rank should match the total + String checkPendingQuery = "SELECT " + + "(SELECT COUNT(*) FROM flyway_schema_history WHERE success = 1) as applied, " + + "(SELECT MAX(installed_rank) FROM flyway_schema_history WHERE success = 1) as max_rank, " + + "(SELECT COUNT(*) FROM flyway_schema_history) as total"; + + try (PreparedStatement stmt = connection.prepareStatement(checkPendingQuery); + ResultSet rs = stmt.executeQuery()) { + + if (rs.next()) { + int applied = rs.getInt("applied"); + int maxRank = rs.getInt("max_rank"); + int total = rs.getInt("total"); + + // If there are failed migrations (total > applied), migrations are not complete + if (total > applied) { + log.warn("Flyway migrations incomplete: {} applied, {} total (some failed)", applied, total); + return false; + } + + // If max_rank is 0 and total is 0, no migrations have run yet + if (maxRank == 0 && total == 0) { + log.debug("No Flyway migrations found in history"); + return false; + } + + // All migrations applied successfully + log.debug("Flyway migrations complete: {} migrations applied", applied); + return true; + } + } + + return false; + } catch (SQLException e) { + // If we can't check migrations, assume they're not complete + // This is safer than assuming they are complete + log.warn("Could not check Flyway migration status: {}", e.getMessage()); + return false; + } + } +} + + diff --git a/src/main/java/com/lottery/lottery/logging/GrafanaLoggingConfig.java b/src/main/java/com/lottery/lottery/logging/GrafanaLoggingConfig.java new file mode 100644 index 0000000..f3acad1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/logging/GrafanaLoggingConfig.java @@ -0,0 +1,30 @@ +package com.lottery.lottery.logging; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; + +import jakarta.annotation.PostConstruct; + +/** + * Configuration for Grafana integration. + * This class prepares the logging infrastructure for Grafana. + * + * In production (Inferno), logs will be sent to Grafana via: + * - Loki (log aggregation) + * - Prometheus (metrics) + * + * For now, this is a placeholder that ensures structured logging + * is ready for Grafana integration. + */ +/** + * Grafana logging configuration - DISABLED + * Remote logging is disabled for security and to prevent external connections. + * All logging is handled locally via standard SLF4J. + */ +// @Slf4j +// @Configuration +public class GrafanaLoggingConfig { + // Disabled - no remote logging configured +} + + diff --git a/src/main/java/com/lottery/lottery/model/Admin.java b/src/main/java/com/lottery/lottery/model/Admin.java new file mode 100644 index 0000000..db93112 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/Admin.java @@ -0,0 +1,48 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "admins") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Admin { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Integer id; + + @Column(name = "user_id", nullable = true) + private Integer userId; + + @Column(name = "username", nullable = false, unique = true, length = 50) + private String username; + + @Column(name = "password_hash", nullable = false, length = 255) + private String passwordHash; + + @Column(name = "role", nullable = false, length = 20) + @Builder.Default + private String role = "ROLE_ADMIN"; + + @Column(name = "created_at", nullable = false, updatable = false) + @Builder.Default + private LocalDateTime createdAt = LocalDateTime.now(); + + @Column(name = "updated_at", nullable = false) + @Builder.Default + private LocalDateTime updatedAt = LocalDateTime.now(); + + @PreUpdate + protected void onUpdate() { + updatedAt = LocalDateTime.now(); + } +} + diff --git a/src/main/java/com/lottery/lottery/model/Configuration.java b/src/main/java/com/lottery/lottery/model/Configuration.java new file mode 100644 index 0000000..1c37175 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/Configuration.java @@ -0,0 +1,21 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "configurations") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Configuration { + + @Id + @Column(name = "`key`", length = 128, nullable = false) + private String key; + + @Column(name = "value", length = 512, nullable = false) + private String value; +} diff --git a/src/main/java/com/lottery/lottery/model/CryptoDepositConfig.java b/src/main/java/com/lottery/lottery/model/CryptoDepositConfig.java new file mode 100644 index 0000000..6a320be --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/CryptoDepositConfig.java @@ -0,0 +1,30 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; +import java.time.Instant; + +@Entity +@Table(name = "crypto_deposit_config") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CryptoDepositConfig { + + @Id + @Column(name = "id") + private Integer id = 1; + + @Column(name = "methods_hash", length = 255) + private String methodsHash; + + @Column(name = "minimum_deposit", nullable = false, precision = 10, scale = 2) + private BigDecimal minimumDeposit; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; +} diff --git a/src/main/java/com/lottery/lottery/model/CryptoDepositMethod.java b/src/main/java/com/lottery/lottery/model/CryptoDepositMethod.java new file mode 100644 index 0000000..faa956c --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/CryptoDepositMethod.java @@ -0,0 +1,39 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; +import java.time.Instant; + +@Entity +@Table(name = "crypto_deposit_methods") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CryptoDepositMethod { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "pid", nullable = false, unique = true) + private Integer pid; + + @Column(name = "name", nullable = false, length = 100) + private String name; + + @Column(name = "network", nullable = false, length = 50) + private String network; + + @Column(name = "example", length = 255) + private String example; + + @Column(name = "min_deposit_sum", nullable = false, precision = 10, scale = 2) + private BigDecimal minDepositSum; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; +} diff --git a/src/main/java/com/lottery/lottery/model/CryptoWithdrawalMethod.java b/src/main/java/com/lottery/lottery/model/CryptoWithdrawalMethod.java new file mode 100644 index 0000000..0232578 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/CryptoWithdrawalMethod.java @@ -0,0 +1,39 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; +import java.time.Instant; + +@Entity +@Table(name = "crypto_withdrawal_methods") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CryptoWithdrawalMethod { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "pid", nullable = false, unique = true) + private Integer pid; + + @Column(name = "name", nullable = false, length = 50) + private String name; + + @Column(name = "network", nullable = false, length = 100) + private String network; + + @Column(name = "icon_id", nullable = false, length = 20) + private String iconId; + + @Column(name = "min_withdrawal", nullable = false, precision = 10, scale = 2) + private BigDecimal minWithdrawal; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; +} diff --git a/src/main/java/com/lottery/lottery/model/FeatureSwitch.java b/src/main/java/com/lottery/lottery/model/FeatureSwitch.java new file mode 100644 index 0000000..ffc7b0e --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/FeatureSwitch.java @@ -0,0 +1,32 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Entity +@Table(name = "feature_switches") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FeatureSwitch { + + @Id + @Column(name = "`key`", length = 64, nullable = false) + private String key; + + @Column(name = "enabled", nullable = false) + private boolean enabled; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PreUpdate + @PrePersist + protected void onUpdate() { + updatedAt = Instant.now(); + } +} diff --git a/src/main/java/com/lottery/lottery/model/FlexibleBotConfig.java b/src/main/java/com/lottery/lottery/model/FlexibleBotConfig.java new file mode 100644 index 0000000..cecab2c --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/FlexibleBotConfig.java @@ -0,0 +1,34 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; +import java.time.Instant; + +@Entity +@Table(name = "flexible_bot_configs") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FlexibleBotConfig { + + @Id + @Column(name = "user_id", nullable = false) + private Integer userId; + + /** Win rate 0.0 to 1.0 (e.g. 0.25 = 25%). */ + @Column(name = "win_rate", nullable = false, precision = 5, scale = 4) + private BigDecimal winRate; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PreUpdate + @PrePersist + protected void onUpdate() { + updatedAt = Instant.now(); + } +} diff --git a/src/main/java/com/lottery/lottery/model/GamePhase.java b/src/main/java/com/lottery/lottery/model/GamePhase.java new file mode 100644 index 0000000..5f81c6d --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/GamePhase.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.model; + +public enum GamePhase { + WAITING, // Zero or one player joined + COUNTDOWN, // 30 seconds countdown started + SPINNING, // Spin animation in progress + RESOLUTION // Winner resolved, payout applied +} + + + + + diff --git a/src/main/java/com/lottery/lottery/model/GameRoom.java b/src/main/java/com/lottery/lottery/model/GameRoom.java new file mode 100644 index 0000000..d9baa05 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/GameRoom.java @@ -0,0 +1,58 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Entity +@Table(name = "game_rooms") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GameRoom { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "room_number", unique = true, nullable = false) + private Integer roomNumber; + + @Enumerated(EnumType.STRING) + @Column(name = "current_phase", nullable = false, length = 20, columnDefinition = "VARCHAR(20)") + @Builder.Default + private GamePhase currentPhase = GamePhase.WAITING; + + @Column(name = "countdown_end_at") + private Instant countdownEndAt; + + @Column(name = "total_bet", nullable = false) + @Builder.Default + private Long totalBet = 0L; + + @Column(name = "registered_players", nullable = false) + @Builder.Default + private Integer registeredPlayers = 0; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PrePersist + protected void onCreate() { + createdAt = Instant.now(); + updatedAt = Instant.now(); + } + + @PreUpdate + protected void onUpdate() { + updatedAt = Instant.now(); + } +} + diff --git a/src/main/java/com/lottery/lottery/model/GameRound.java b/src/main/java/com/lottery/lottery/model/GameRound.java new file mode 100644 index 0000000..020a671 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/GameRound.java @@ -0,0 +1,68 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Entity +@Table(name = "game_rounds") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GameRound { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "room_id", nullable = false) + private GameRoom room; + + @Enumerated(EnumType.STRING) + @Column(name = "phase", nullable = false, length = 20, columnDefinition = "VARCHAR(20)") + private GamePhase phase; + + @Column(name = "total_bet", nullable = false) + private Long totalBet; + + @Column(name = "winner_user_id") + private Integer winnerUserId; + + @Column(name = "winner_bet", nullable = false) + @Builder.Default + private Long winnerBet = 0L; + + @Column(name = "commission", nullable = false) + @Builder.Default + private Long commission = 0L; + + @Column(name = "payout", nullable = false) + @Builder.Default + private Long payout = 0L; + + @Column(name = "started_at", nullable = false) + private Instant startedAt; + + @Column(name = "countdown_started_at") + private Instant countdownStartedAt; + + @Column(name = "countdown_ended_at") + private Instant countdownEndedAt; + + @Column(name = "resolved_at") + private Instant resolvedAt; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @PrePersist + protected void onCreate() { + createdAt = Instant.now(); + } +} + diff --git a/src/main/java/com/lottery/lottery/model/GameRoundParticipant.java b/src/main/java/com/lottery/lottery/model/GameRoundParticipant.java new file mode 100644 index 0000000..f701ec2 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/GameRoundParticipant.java @@ -0,0 +1,40 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +@Entity +@Table(name = "game_round_participants") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GameRoundParticipant { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "round_id", nullable = false) + private GameRound round; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "bet", nullable = false) + private Long bet; + + @Column(name = "joined_at", nullable = false, updatable = false) + private Instant joinedAt; + + @PrePersist + protected void onCreate() { + joinedAt = Instant.now(); + } +} + diff --git a/src/main/java/com/lottery/lottery/model/LotteryBotConfig.java b/src/main/java/com/lottery/lottery/model/LotteryBotConfig.java new file mode 100644 index 0000000..164946c --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/LotteryBotConfig.java @@ -0,0 +1,75 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalTime; +import java.time.Instant; + +@Entity +@Table(name = "lottery_bot_configs") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LotteryBotConfig { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Integer id; + + @Column(name = "user_id", nullable = false, unique = true) + private Integer userId; + + @Column(name = "room_1", nullable = false) + @Builder.Default + private Boolean room1 = false; + + @Column(name = "room_2", nullable = false) + @Builder.Default + private Boolean room2 = false; + + @Column(name = "room_3", nullable = false) + @Builder.Default + private Boolean room3 = false; + + @Column(name = "time_utc_start", nullable = false) + private LocalTime timeUtcStart; + + @Column(name = "time_utc_end", nullable = false) + private LocalTime timeUtcEnd; + + @Column(name = "bet_min", nullable = false) + private Long betMin; + + @Column(name = "bet_max", nullable = false) + private Long betMax; + + @Column(name = "persona", nullable = false, length = 20) + @Builder.Default + private String persona = "balanced"; + + @Column(name = "active", nullable = false) + @Builder.Default + private Boolean active = true; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PrePersist + protected void onCreate() { + Instant now = Instant.now(); + if (createdAt == null) createdAt = now; + if (updatedAt == null) updatedAt = now; + } + + @PreUpdate + protected void onUpdate() { + updatedAt = Instant.now(); + } +} diff --git a/src/main/java/com/lottery/lottery/model/NotificationAudit.java b/src/main/java/com/lottery/lottery/model/NotificationAudit.java new file mode 100644 index 0000000..469715b --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/NotificationAudit.java @@ -0,0 +1,35 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Entity +@Table(name = "notifications_audit") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class NotificationAudit { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "status", nullable = false, length = 20) + private String status; // SUCCESS or FAILED + + @Column(name = "telegram_status_code") + private Integer telegramStatusCode; + + @Column(name = "created_at", nullable = false) + private Instant createdAt; + + public static final String STATUS_SUCCESS = "SUCCESS"; + public static final String STATUS_FAILED = "FAILED"; +} diff --git a/src/main/java/com/lottery/lottery/model/Payment.java b/src/main/java/com/lottery/lottery/model/Payment.java new file mode 100644 index 0000000..ae47eea --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/Payment.java @@ -0,0 +1,67 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; +import java.time.Instant; + +@Entity +@Table(name = "payments") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Payment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "order_id", nullable = false, unique = true, length = 255) + private String orderId; + + @Column(name = "stars_amount", nullable = false) + private Integer starsAmount; + + @Column(name = "usd_amount", precision = 20, scale = 2) + private BigDecimal usdAmount; // stored as decimal, e.g. 1.25 USD = 1.25 + + @Column(name = "tickets_amount", nullable = false) + private Long ticketsAmount; // Tickets amount in bigint format + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false, length = 20, columnDefinition = "VARCHAR(20)") + @Builder.Default + private PaymentStatus status = PaymentStatus.PENDING; + + @Column(name = "telegram_payment_charge_id", length = 255) + private String telegramPaymentChargeId; + + @Column(name = "telegram_provider_payment_charge_id", length = 255) + private String telegramProviderPaymentChargeId; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "completed_at") + private Instant completedAt; + + @PrePersist + protected void onCreate() { + createdAt = Instant.now(); + } + + public enum PaymentStatus { + PENDING, COMPLETED, FAILED, CANCELLED + } +} + + + + diff --git a/src/main/java/com/lottery/lottery/model/Payout.java b/src/main/java/com/lottery/lottery/model/Payout.java new file mode 100644 index 0000000..2089036 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/Payout.java @@ -0,0 +1,109 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; +import java.time.Instant; + +@Entity +@Table(name = "payouts") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Payout { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "username", nullable = false, length = 255) + private String username; + + @Column(name = "wallet", length = 120) + private String wallet; + + @Enumerated(EnumType.STRING) + @Column(name = "type", nullable = false, length = 20, columnDefinition = "VARCHAR(20)") + private PayoutType type; + + @Enumerated(EnumType.STRING) + @Column(name = "gift_name", length = 50, columnDefinition = "VARCHAR(50)") + private GiftType giftName; + + @Column(name = "crypto_name", length = 20) + private String cryptoName; + + @Column(name = "total", nullable = false) + private Long total; // Tickets amount in bigint format + + @Column(name = "stars_amount", nullable = false) + private Integer starsAmount; + + @Column(name = "usd_amount", precision = 20, scale = 2) + private BigDecimal usdAmount; // stored as decimal, e.g. 1.25 USD = 1.25 + + @Column(name = "amount_coins", length = 50) + private String amountCoins; + + @Column(name = "commission_coins", length = 50) + private String commissionCoins; + + @Column(name = "amount_to_send", length = 50) + private String amountToSend; + + @Column(name = "payment_id") + private Integer paymentId; // Crypto API payment id from withdrawal response + + @Column(name = "txhash", length = 255) + private String txhash; // Transaction hash from crypto API (WithdrawalInfoApiResponse.PaymentItem) + + @Column(name = "quantity", nullable = false) + @Builder.Default + private Integer quantity = 1; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false, length = 20, columnDefinition = "VARCHAR(20)") + @Builder.Default + private PayoutStatus status = PayoutStatus.PROCESSING; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "updated_at") + private Instant updatedAt; + + @Column(name = "resolved_at") + private Instant resolvedAt; + + @PrePersist + protected void onCreate() { + Instant now = Instant.now(); + createdAt = now; + updatedAt = now; + } + + @PreUpdate + protected void onUpdate() { + updatedAt = Instant.now(); + } + + public enum PayoutType { + STARS, GIFT, CRYPTO + } + + public enum PayoutStatus { + PROCESSING, COMPLETED, CANCELLED, WAITING + } + + public enum GiftType { + HEART, BEAR, GIFTBOX, FLOWER, CAKE, BOUQUET, ROCKET, CUP, RING, DIAMOND, CHAMPAGNE + } +} + diff --git a/src/main/java/com/lottery/lottery/model/Promotion.java b/src/main/java/com/lottery/lottery/model/Promotion.java new file mode 100644 index 0000000..50a6021 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/Promotion.java @@ -0,0 +1,72 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Entity +@Table(name = "promotions") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Promotion { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Enumerated(EnumType.STRING) + @Column(name = "type", nullable = false, length = 32, columnDefinition = "VARCHAR(32)") + private PromotionType type; + + @Column(name = "start_time", nullable = false) + private Instant startTime; + + @Column(name = "end_time", nullable = false) + private Instant endTime; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false, length = 20, columnDefinition = "VARCHAR(20)") + @Builder.Default + private PromotionStatus status = PromotionStatus.PLANNED; + + /** Total prize fund in bigint (1 ticket = 1_000_000). */ + @Column(name = "total_reward") + private Long totalReward; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PrePersist + protected void onCreate() { + Instant now = Instant.now(); + if (createdAt == null) createdAt = now; + if (updatedAt == null) updatedAt = now; + } + + @PreUpdate + protected void onUpdate() { + updatedAt = Instant.now(); + } + + public enum PromotionType { + NET_WIN, + /** Same as NET_WIN but points only when winner made max bet in the room. */ + NET_WIN_MAX_BET, + /** 1 point per referral (level 1) who played at least one round (awarded when referral completes first round). */ + REF_COUNT + } + + public enum PromotionStatus { + ACTIVE, + INACTIVE, + FINISHED, + PLANNED + } +} diff --git a/src/main/java/com/lottery/lottery/model/PromotionReward.java b/src/main/java/com/lottery/lottery/model/PromotionReward.java new file mode 100644 index 0000000..3ce99b4 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/PromotionReward.java @@ -0,0 +1,48 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Entity +@Table(name = "promotions_rewards") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PromotionReward { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "promo_id", nullable = false) + private Promotion promotion; + + @Column(name = "place", nullable = false) + private Integer place; + + @Column(name = "reward", nullable = false) + private Long reward; // tickets in bigint (1 ticket = 1_000_000) + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PrePersist + protected void onCreate() { + Instant now = Instant.now(); + if (createdAt == null) createdAt = now; + if (updatedAt == null) updatedAt = now; + } + + @PreUpdate + protected void onUpdate() { + updatedAt = Instant.now(); + } +} diff --git a/src/main/java/com/lottery/lottery/model/PromotionUser.java b/src/main/java/com/lottery/lottery/model/PromotionUser.java new file mode 100644 index 0000000..e0a20f3 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/PromotionUser.java @@ -0,0 +1,67 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Objects; + +@Entity +@Table(name = "promotions_users") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@IdClass(PromotionUser.PromotionUserId.class) +public class PromotionUser { + + @Id + @Column(name = "promo_id", nullable = false) + private Integer promoId; + + @Id + @Column(name = "user_id", nullable = false) + private Integer userId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "promo_id", nullable = false, insertable = false, updatable = false) + private Promotion promotion; + + @Column(name = "points", nullable = false, precision = 20, scale = 2) + @Builder.Default + private BigDecimal points = BigDecimal.ZERO; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PrePersist + @PreUpdate + protected void onUpdate() { + updatedAt = Instant.now(); + } + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class PromotionUserId implements Serializable { + private Integer promoId; + private Integer userId; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PromotionUserId that = (PromotionUserId) o; + return Objects.equals(promoId, that.promoId) && Objects.equals(userId, that.userId); + } + + @Override + public int hashCode() { + return Objects.hash(promoId, userId); + } + } +} diff --git a/src/main/java/com/lottery/lottery/model/QuickAnswer.java b/src/main/java/com/lottery/lottery/model/QuickAnswer.java new file mode 100644 index 0000000..9a8f495 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/QuickAnswer.java @@ -0,0 +1,42 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "quick_answers") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuickAnswer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "admin_id", nullable = false) + private Admin admin; + + @Column(name = "text", nullable = false, columnDefinition = "TEXT") + private String text; + + @Column(name = "created_at", nullable = false, updatable = false) + @Builder.Default + private LocalDateTime createdAt = LocalDateTime.now(); + + @Column(name = "updated_at", nullable = false) + @Builder.Default + private LocalDateTime updatedAt = LocalDateTime.now(); + + @PreUpdate + protected void onUpdate() { + updatedAt = LocalDateTime.now(); + } +} + diff --git a/src/main/java/com/lottery/lottery/model/SafeBotUser.java b/src/main/java/com/lottery/lottery/model/SafeBotUser.java new file mode 100644 index 0000000..9e6f637 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/SafeBotUser.java @@ -0,0 +1,18 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "safe_bot_users") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SafeBotUser { + + @Id + @Column(name = "user_id", nullable = false) + private Integer userId; +} diff --git a/src/main/java/com/honey/honey/model/Session.java b/src/main/java/com/lottery/lottery/model/Session.java similarity index 95% rename from src/main/java/com/honey/honey/model/Session.java rename to src/main/java/com/lottery/lottery/model/Session.java index bc58daf..b0d1b6f 100644 --- a/src/main/java/com/honey/honey/model/Session.java +++ b/src/main/java/com/lottery/lottery/model/Session.java @@ -1,4 +1,4 @@ -package com.honey.honey.model; +package com.lottery.lottery.model; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/lottery/lottery/model/SupportMessage.java b/src/main/java/com/lottery/lottery/model/SupportMessage.java new file mode 100644 index 0000000..d80e6e7 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/SupportMessage.java @@ -0,0 +1,40 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Entity +@Table(name = "support_messages") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SupportMessage { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "ticket_id", nullable = false) + private SupportTicket ticket; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private UserA user; + + @Column(name = "message", nullable = false, length = 2000) + private String message; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @PrePersist + protected void onCreate() { + createdAt = Instant.now(); + } +} + diff --git a/src/main/java/com/lottery/lottery/model/SupportTicket.java b/src/main/java/com/lottery/lottery/model/SupportTicket.java new file mode 100644 index 0000000..f7bf6e7 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/SupportTicket.java @@ -0,0 +1,57 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Entity +@Table(name = "support_tickets") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SupportTicket { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private UserA user; + + @Column(name = "subject", nullable = false, length = 100) + private String subject; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false, length = 10) + @Builder.Default + private TicketStatus status = TicketStatus.OPENED; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @PrePersist + protected void onCreate() { + createdAt = Instant.now(); + updatedAt = Instant.now(); + } + + @PreUpdate + protected void onUpdate() { + updatedAt = Instant.now(); + } + + public enum TicketStatus { + OPENED, + CLOSED + } +} + + + diff --git a/src/main/java/com/lottery/lottery/model/Task.java b/src/main/java/com/lottery/lottery/model/Task.java new file mode 100644 index 0000000..a9226ca --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/Task.java @@ -0,0 +1,43 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "tasks") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Task { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Integer id; + + @Column(name = "type", nullable = false, length = 20) + private String type; // referral, follow, other + + @Column(name = "requirement", nullable = false) + private Long requirement; + + @Column(name = "reward_amount", nullable = false) + private Long rewardAmount; + + @Column(name = "reward_type", nullable = false, length = 20) + @Builder.Default + private String rewardType = "Tickets"; + + @Column(name = "display_order", nullable = false) + @Builder.Default + private Integer displayOrder = 0; + + @Column(name = "title", nullable = false, length = 255) + private String title; + + @Column(name = "description", columnDefinition = "TEXT") + private String description; +} + diff --git a/src/main/java/com/lottery/lottery/model/Transaction.java b/src/main/java/com/lottery/lottery/model/Transaction.java new file mode 100644 index 0000000..c405cc5 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/Transaction.java @@ -0,0 +1,61 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Entity +@Table(name = "transactions") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Transaction { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "amount", nullable = false) + private Long amount; // Amount in bigint format (positive for credits, negative for debits) + + @Enumerated(EnumType.STRING) + @Column(name = "type", nullable = false, length = 50, columnDefinition = "VARCHAR(50)") + private TransactionType type; + + @Column(name = "task_id") + private Integer taskId; // Task ID for TASK_BONUS type + + @Column(name = "round_id") + private Long roundId; // Round ID for WIN/BET type + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @PrePersist + protected void onCreate() { + // Only set createdAt if it's not already set (allows custom timestamps) + if (createdAt == null) { + createdAt = Instant.now(); + } + } + + public enum TransactionType { + DEPOSIT, // Payment/deposit + WITHDRAWAL, // Payout/withdrawal + WIN, // Game round win (total payout) + BET, // Game round bet (for all participants, winners and losers) + @Deprecated + LOSS, // Legacy: Old bet type, replaced by BET (kept for backward compatibility with old database records) + TASK_BONUS, // Task reward + DAILY_BONUS, // Daily bonus reward (no taskId) + CANCELLATION_OF_WITHDRAWAL // Cancellation of withdrawal (payout cancelled by admin) + } +} + diff --git a/src/main/java/com/honey/honey/model/UserA.java b/src/main/java/com/lottery/lottery/model/UserA.java similarity index 87% rename from src/main/java/com/honey/honey/model/UserA.java rename to src/main/java/com/lottery/lottery/model/UserA.java index 24de1ba..7dba0bd 100644 --- a/src/main/java/com/honey/honey/model/UserA.java +++ b/src/main/java/com/lottery/lottery/model/UserA.java @@ -1,4 +1,4 @@ -package com.honey.honey.model; +package com.lottery.lottery.model; import jakarta.persistence.*; import lombok.*; @@ -58,5 +58,12 @@ public class UserA { @Column(name = "banned", nullable = false) @Builder.Default private Integer banned = 0; + + @Column(name = "avatar_url", length = 500) + private String avatarUrl; + + @Column(name = "last_telegram_file_id", length = 255) + private String lastTelegramFileId; } + diff --git a/src/main/java/com/honey/honey/model/UserB.java b/src/main/java/com/lottery/lottery/model/UserB.java similarity index 57% rename from src/main/java/com/honey/honey/model/UserB.java rename to src/main/java/com/lottery/lottery/model/UserB.java index cfea42f..fed6662 100644 --- a/src/main/java/com/honey/honey/model/UserB.java +++ b/src/main/java/com/lottery/lottery/model/UserB.java @@ -1,4 +1,4 @@ -package com.honey.honey.model; +package com.lottery.lottery.model; import jakarta.persistence.*; import lombok.*; @@ -39,5 +39,20 @@ public class UserB { @Column(name = "withdraw_count", nullable = false) @Builder.Default private Integer withdrawCount = 0; + + @Column(name = "rounds_played", nullable = false) + @Builder.Default + private Integer roundsPlayed = 0; + + /** Total winnings since last deposit (bigint: 1 ticket = 1_000_000). Reset to 0 on deposit; incremented on round win; reduced when payout is created. */ + @Column(name = "total_win_after_deposit", nullable = false) + @Builder.Default + private Long totalWinAfterDeposit = 0L; + + /** When true, the user cannot create any payout request (blocked on backend). */ + @Column(name = "withdrawals_disabled", nullable = false) + @Builder.Default + private Boolean withdrawalsDisabled = false; } + diff --git a/src/main/java/com/honey/honey/model/UserD.java b/src/main/java/com/lottery/lottery/model/UserD.java similarity index 94% rename from src/main/java/com/honey/honey/model/UserD.java rename to src/main/java/com/lottery/lottery/model/UserD.java index ace3413..97b82bd 100644 --- a/src/main/java/com/honey/honey/model/UserD.java +++ b/src/main/java/com/lottery/lottery/model/UserD.java @@ -1,4 +1,4 @@ -package com.honey.honey.model; +package com.lottery.lottery.model; import jakarta.persistence.*; import lombok.*; @@ -16,6 +16,10 @@ public class UserD { @Column(name = "id") private Integer id; + @Column(name = "screen_name", nullable = false, length = 75) + @Builder.Default + private String screenName = "-"; + @Column(name = "referer_id_1", nullable = false) @Builder.Default private Integer refererId1 = 0; @@ -101,3 +105,4 @@ public class UserD { private Long toReferer5 = 0L; } + diff --git a/src/main/java/com/lottery/lottery/model/UserDailyBonusClaim.java b/src/main/java/com/lottery/lottery/model/UserDailyBonusClaim.java new file mode 100644 index 0000000..3f1863f --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/UserDailyBonusClaim.java @@ -0,0 +1,42 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "user_daily_bonus_claims") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserDailyBonusClaim { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "avatar_url", length = 255) + private String avatarUrl; + + @Column(name = "screen_name", nullable = false, length = 75) + @Builder.Default + private String screenName = "-"; + + @Column(name = "claimed_at", nullable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + private LocalDateTime claimedAt; + + @PrePersist + protected void onCreate() { + if (claimedAt == null) { + claimedAt = LocalDateTime.now(); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/model/UserTaskClaim.java b/src/main/java/com/lottery/lottery/model/UserTaskClaim.java new file mode 100644 index 0000000..21c90b2 --- /dev/null +++ b/src/main/java/com/lottery/lottery/model/UserTaskClaim.java @@ -0,0 +1,42 @@ +package com.lottery.lottery.model; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "user_task_claims") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserTaskClaim { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "task_id", nullable = false) + private Integer taskId; + + @Column(name = "claimed_at", nullable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + private LocalDateTime claimedAt; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "task_id", insertable = false, updatable = false) + private Task task; + + @PrePersist + protected void onCreate() { + if (claimedAt == null) { + claimedAt = LocalDateTime.now(); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/repository/AdminRepository.java b/src/main/java/com/lottery/lottery/repository/AdminRepository.java new file mode 100644 index 0000000..65f18a7 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/AdminRepository.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.Admin; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface AdminRepository extends JpaRepository { + Optional findByUsername(String username); +} + diff --git a/src/main/java/com/lottery/lottery/repository/ConfigurationRepository.java b/src/main/java/com/lottery/lottery/repository/ConfigurationRepository.java new file mode 100644 index 0000000..2d85d5d --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/ConfigurationRepository.java @@ -0,0 +1,9 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.Configuration; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ConfigurationRepository extends JpaRepository { +} diff --git a/src/main/java/com/lottery/lottery/repository/CryptoDepositConfigRepository.java b/src/main/java/com/lottery/lottery/repository/CryptoDepositConfigRepository.java new file mode 100644 index 0000000..0f70098 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/CryptoDepositConfigRepository.java @@ -0,0 +1,9 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.CryptoDepositConfig; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CryptoDepositConfigRepository extends JpaRepository { +} diff --git a/src/main/java/com/lottery/lottery/repository/CryptoDepositMethodRepository.java b/src/main/java/com/lottery/lottery/repository/CryptoDepositMethodRepository.java new file mode 100644 index 0000000..125bf28 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/CryptoDepositMethodRepository.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.CryptoDepositMethod; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface CryptoDepositMethodRepository extends JpaRepository { + + Optional findByPid(Integer pid); +} diff --git a/src/main/java/com/lottery/lottery/repository/CryptoWithdrawalMethodRepository.java b/src/main/java/com/lottery/lottery/repository/CryptoWithdrawalMethodRepository.java new file mode 100644 index 0000000..78bcc2a --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/CryptoWithdrawalMethodRepository.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.CryptoWithdrawalMethod; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CryptoWithdrawalMethodRepository extends JpaRepository { + + List findAllByOrderByPidAsc(); +} diff --git a/src/main/java/com/lottery/lottery/repository/FeatureSwitchRepository.java b/src/main/java/com/lottery/lottery/repository/FeatureSwitchRepository.java new file mode 100644 index 0000000..b428fed --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/FeatureSwitchRepository.java @@ -0,0 +1,9 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.FeatureSwitch; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FeatureSwitchRepository extends JpaRepository { +} diff --git a/src/main/java/com/lottery/lottery/repository/FlexibleBotConfigRepository.java b/src/main/java/com/lottery/lottery/repository/FlexibleBotConfigRepository.java new file mode 100644 index 0000000..1c67603 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/FlexibleBotConfigRepository.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.FlexibleBotConfig; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface FlexibleBotConfigRepository extends JpaRepository { + + List findAllByOrderByUserIdAsc(); +} diff --git a/src/main/java/com/lottery/lottery/repository/GameRoomRepository.java b/src/main/java/com/lottery/lottery/repository/GameRoomRepository.java new file mode 100644 index 0000000..e7ca749 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/GameRoomRepository.java @@ -0,0 +1,30 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.GamePhase; +import com.lottery.lottery.model.GameRoom; +import jakarta.persistence.LockModeType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface GameRoomRepository extends JpaRepository { + Optional findByRoomNumber(Integer roomNumber); + + // Efficient query for rooms in specific phase (uses index on current_phase) + List findByCurrentPhase(GamePhase phase); + + /** + * Finds room by room number with pessimistic write lock to prevent race conditions. + * This ensures only one transaction can update the room at a time. + */ + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT r FROM GameRoom r WHERE r.roomNumber = :roomNumber") + Optional findByRoomNumberWithLock(@Param("roomNumber") Integer roomNumber); +} + diff --git a/src/main/java/com/lottery/lottery/repository/GameRoundParticipantRepository.java b/src/main/java/com/lottery/lottery/repository/GameRoundParticipantRepository.java new file mode 100644 index 0000000..8a5efbf --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/GameRoundParticipantRepository.java @@ -0,0 +1,53 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.GameRoundParticipant; +import jakarta.persistence.LockModeType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +@Repository +public interface GameRoundParticipantRepository extends JpaRepository { + List findByRoundId(Long roundId); + + @Query("SELECT p FROM GameRoundParticipant p WHERE p.round.id = :roundId AND p.userId = :userId") + List findByRoundIdAndUserId(@Param("roundId") Long roundId, @Param("userId") Integer userId); + + /** + * Finds participant by ID with pessimistic write lock to prevent race conditions. + * This ensures only one transaction can update the participant at a time. + */ + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT p FROM GameRoundParticipant p WHERE p.id = :id") + Optional findByIdWithLock(@Param("id") Long id); + + /** + * Finds all rounds where the user participated, ordered by resolution time (newest first). + * Only returns completed rounds (phase = RESOLUTION, resolvedAt IS NOT NULL). + */ + @Query("SELECT p FROM GameRoundParticipant p " + + "WHERE p.userId = :userId " + + "AND p.round.phase = 'RESOLUTION' " + + "AND p.round.resolvedAt IS NOT NULL " + + "ORDER BY p.round.resolvedAt DESC") + List findUserCompletedRounds(@Param("userId") Integer userId, + org.springframework.data.domain.Pageable pageable); + + /** + * Batch deletes participants older than the specified date (up to batchSize). + * Returns the number of deleted rows. + * Note: MySQL requires LIMIT to be used directly in DELETE statements. + */ + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Query(value = "DELETE FROM game_round_participants WHERE joined_at < :cutoffDate LIMIT :batchSize", nativeQuery = true) + int deleteOldParticipantsBatch(@Param("cutoffDate") Instant cutoffDate, @Param("batchSize") int batchSize); +} + + diff --git a/src/main/java/com/lottery/lottery/repository/GameRoundRepository.java b/src/main/java/com/lottery/lottery/repository/GameRoundRepository.java new file mode 100644 index 0000000..b147a0e --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/GameRoundRepository.java @@ -0,0 +1,61 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.GameRound; +import com.lottery.lottery.model.GameRoom; +import com.lottery.lottery.model.GamePhase; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +@Repository +public interface GameRoundRepository extends JpaRepository { + + /** Fetch rounds by ids with room loaded (for admin game history). */ + @Query("SELECT r FROM GameRound r LEFT JOIN FETCH r.room WHERE r.id IN :ids") + List findAllByIdWithRoom(@Param("ids") Set ids); + + /** + * Finds the most recent active round(s) for a room, ordered by startedAt DESC. + * Use Pageable with size 1 to get only the single most recent round (resilient to corrupted data with multiple rounds in same phase). + */ + @Query("SELECT r FROM GameRound r WHERE r.room.id = :roomId AND r.phase IN :phases ORDER BY r.startedAt DESC") + List findMostRecentActiveRoundsByRoomId( + @Param("roomId") Integer roomId, + @Param("phases") List phases, + Pageable pageable + ); + + /** + * Finds the last N completed rounds for a room, ordered by resolution time (newest first). + * Only returns rounds that have a winner (winner_user_id IS NOT NULL). + */ + @Query("SELECT r FROM GameRound r WHERE r.room.roomNumber = :roomNumber AND r.phase = 'RESOLUTION' AND r.resolvedAt IS NOT NULL AND r.winnerUserId IS NOT NULL ORDER BY r.resolvedAt DESC") + List findLastCompletedRoundsByRoomNumber( + @Param("roomNumber") Integer roomNumber, + org.springframework.data.domain.Pageable pageable + ); + + /** + * Counts rounds resolved after the specified date. + */ + long countByResolvedAtAfter(Instant date); + + /** + * Calculates average total_bet for rounds resolved after the specified date. + */ + @Query("SELECT AVG(r.totalBet) FROM GameRound r WHERE r.resolvedAt >= :after AND r.resolvedAt IS NOT NULL") + Optional avgTotalBetByResolvedAtAfter(@Param("after") Instant after); + + /** + * Counts rounds resolved between two dates. + */ + long countByResolvedAtBetween(Instant start, Instant end); +} + diff --git a/src/main/java/com/lottery/lottery/repository/LotteryBotConfigRepository.java b/src/main/java/com/lottery/lottery/repository/LotteryBotConfigRepository.java new file mode 100644 index 0000000..a943f97 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/LotteryBotConfigRepository.java @@ -0,0 +1,24 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.LotteryBotConfig; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface LotteryBotConfigRepository extends JpaRepository { + + List findAllByOrderByIdAsc(); + + List findAllByActiveTrue(); + + List findAllByRoom2True(); + + List findAllByRoom3True(); + + Optional findByUserId(Integer userId); + + boolean existsByUserId(Integer userId); +} diff --git a/src/main/java/com/lottery/lottery/repository/NotificationAuditRepository.java b/src/main/java/com/lottery/lottery/repository/NotificationAuditRepository.java new file mode 100644 index 0000000..f9db65b --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/NotificationAuditRepository.java @@ -0,0 +1,14 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.NotificationAudit; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface NotificationAuditRepository extends JpaRepository { + + /** Latest audit row for the user (by created_at). Uses index (user_id, created_at DESC). */ + Optional findTopByUserIdOrderByCreatedAtDesc(Integer userId); +} diff --git a/src/main/java/com/lottery/lottery/repository/PaymentRepository.java b/src/main/java/com/lottery/lottery/repository/PaymentRepository.java new file mode 100644 index 0000000..2fb3e0f --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/PaymentRepository.java @@ -0,0 +1,91 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.Payment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Optional; + +@Repository +public interface PaymentRepository extends JpaRepository, JpaSpecificationExecutor { + org.springframework.data.domain.Page findByUserId(Integer userId, org.springframework.data.domain.Pageable pageable); + Optional findByOrderId(String orderId); + Optional findByOrderIdAndStatus(String orderId, Payment.PaymentStatus status); + + /** + * Sums all completed payment stars_amount for a user. + * @param userId The user ID + * @return Sum of stars_amount for all COMPLETED payments, or 0 if none + */ + @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payment p WHERE p.userId = :userId AND p.status = 'COMPLETED'") + Integer sumCompletedStarsAmountByUserId(@Param("userId") Integer userId); + + /** + * Sums tickets_amount for all payments with given status. + */ + @Query("SELECT COALESCE(SUM(p.ticketsAmount), 0) FROM Payment p WHERE p.status = :status") + Optional sumTicketsAmountByStatus(@Param("status") Payment.PaymentStatus status); + + /** + * Sums tickets_amount for payments with given status created after the specified date. + */ + @Query("SELECT COALESCE(SUM(p.ticketsAmount), 0) FROM Payment p WHERE p.status = :status AND p.createdAt >= :after") + Optional sumTicketsAmountByStatusAndCreatedAtAfter( + @Param("status") Payment.PaymentStatus status, + @Param("after") Instant after + ); + + /** + * Sums stars_amount for all payments with given status. + */ + @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payment p WHERE p.status = :status") + Optional sumStarsAmountByStatus(@Param("status") Payment.PaymentStatus status); + + /** + * Sums stars_amount for payments with given status created after the specified date. + */ + @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payment p WHERE p.status = :status AND p.createdAt >= :after") + Optional sumStarsAmountByStatusAndCreatedAtAfter( + @Param("status") Payment.PaymentStatus status, + @Param("after") Instant after + ); + + /** + * Sums stars_amount for payments with given status created between two dates. + */ + @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payment p WHERE p.status = :status AND p.createdAt >= :start AND p.createdAt < :end") + Optional sumStarsAmountByStatusAndCreatedAtBetween( + @Param("status") Payment.PaymentStatus status, + @Param("start") Instant start, + @Param("end") Instant end + ); + + /** Sum usd_amount for completed payments where usd_amount is not null (CRYPTO deposits). */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payment p WHERE p.status = :status AND p.usdAmount IS NOT NULL") + Optional sumUsdAmountByStatusAndUsdAmountNotNull(@Param("status") Payment.PaymentStatus status); + + /** Sum usd_amount for completed payments (CRYPTO) created after the specified date. */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payment p WHERE p.status = :status AND p.usdAmount IS NOT NULL AND p.createdAt >= :after") + Optional sumUsdAmountByStatusAndUsdAmountNotNullAndCreatedAtAfter( + @Param("status") Payment.PaymentStatus status, + @Param("after") Instant after + ); + + /** Sum usd_amount for a user's completed CRYPTO deposits. */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payment p WHERE p.userId = :userId AND p.status = 'COMPLETED' AND p.usdAmount IS NOT NULL") + Optional sumUsdAmountByUserIdAndCompletedAndUsdAmountNotNull(@Param("userId") Integer userId); + + /** Sum usd_amount for completed CRYPTO payments created between two dates. */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payment p WHERE p.status = :status AND p.usdAmount IS NOT NULL AND p.createdAt >= :start AND p.createdAt < :end") + Optional sumUsdAmountByStatusAndUsdAmountNotNullAndCreatedAtBetween( + @Param("status") Payment.PaymentStatus status, + @Param("start") Instant start, + @Param("end") Instant end + ); +} + diff --git a/src/main/java/com/lottery/lottery/repository/PayoutRepository.java b/src/main/java/com/lottery/lottery/repository/PayoutRepository.java new file mode 100644 index 0000000..0aa257c --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/PayoutRepository.java @@ -0,0 +1,111 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.Payout; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +@Repository +public interface PayoutRepository extends JpaRepository, JpaSpecificationExecutor { + List findByUserIdOrderByCreatedAtDesc(Integer userId); + org.springframework.data.domain.Page findByUserIdAndType(Integer userId, Payout.PayoutType type, org.springframework.data.domain.Pageable pageable); + + /** + * Finds CRYPTO payouts by status in (PROCESSING, WAITING) with non-null payment_id, ordered by updated_at ASC (oldest first). + * Used by withdrawal status sync cron to process up to page size (e.g. 20) per run. STARS and GIFT are ignored. + */ + List findByTypeAndStatusInAndPaymentIdIsNotNullOrderByUpdatedAtAsc( + Payout.PayoutType type, + Set statuses, + Pageable pageable); + + /** + * Returns whether the user has at least one payout with the given status. + * Used to enforce at most one PROCESSING payout per user. + */ + boolean existsByUserIdAndStatus(Integer userId, Payout.PayoutStatus status); + + List findByStatusOrderByCreatedAtDesc(Payout.PayoutStatus status); + + Optional findByIdAndStatus(Long id, Payout.PayoutStatus status); + + /** + * Finds the last N payouts for a specific user, ordered by creation date descending. + * Uses the index idx_payouts_user_created for optimal performance. + */ + @Query("SELECT p FROM Payout p WHERE p.userId = :userId ORDER BY p.createdAt DESC") + List findLastPayoutsByUserId(@Param("userId") Integer userId, org.springframework.data.domain.Pageable pageable); + + /** + * Sums total for all payouts with given status. + */ + @Query("SELECT COALESCE(SUM(p.total), 0) FROM Payout p WHERE p.status = :status") + Optional sumTotalByStatus(@Param("status") Payout.PayoutStatus status); + + /** + * Sums total for payouts with given status created after the specified date. + */ + @Query("SELECT COALESCE(SUM(p.total), 0) FROM Payout p WHERE p.status = :status AND p.createdAt >= :after") + Optional sumTotalByStatusAndCreatedAtAfter( + @Param("status") Payout.PayoutStatus status, + @Param("after") Instant after + ); + + /** + * Sums stars_amount for all payouts with given status. + */ + @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payout p WHERE p.status = :status") + Optional sumStarsAmountByStatus(@Param("status") Payout.PayoutStatus status); + + /** + * Sums stars_amount for payouts with given status created after the specified date. + */ + @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payout p WHERE p.status = :status AND p.createdAt >= :after") + Optional sumStarsAmountByStatusAndCreatedAtAfter( + @Param("status") Payout.PayoutStatus status, + @Param("after") Instant after + ); + + /** + * Sums stars_amount for payouts with given status created between two dates. + */ + @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payout p WHERE p.status = :status AND p.createdAt >= :start AND p.createdAt < :end") + Optional sumStarsAmountByStatusAndCreatedAtBetween( + @Param("status") Payout.PayoutStatus status, + @Param("start") Instant start, + @Param("end") Instant end + ); + + /** Sum usd_amount for CRYPTO payouts with given status. */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.type = 'CRYPTO' AND p.status = :status") + Optional sumUsdAmountByTypeCryptoAndStatus(@Param("status") Payout.PayoutStatus status); + + /** Sum usd_amount for CRYPTO payouts with given status created after the specified date. */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.type = 'CRYPTO' AND p.status = :status AND p.createdAt >= :after") + Optional sumUsdAmountByTypeCryptoAndStatusAndCreatedAtAfter( + @Param("status") Payout.PayoutStatus status, + @Param("after") Instant after + ); + + /** Sum usd_amount for a user's completed CRYPTO payouts. */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.userId = :userId AND p.type = 'CRYPTO' AND p.status = 'COMPLETED'") + Optional sumUsdAmountByUserIdAndTypeCryptoAndCompleted(@Param("userId") Integer userId); + + /** Sum usd_amount for CRYPTO payouts with given status created between two dates. */ + @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.type = 'CRYPTO' AND p.status = :status AND p.createdAt >= :start AND p.createdAt < :end") + Optional sumUsdAmountByTypeCryptoAndStatusAndCreatedAtBetween( + @Param("status") Payout.PayoutStatus status, + @Param("start") Instant start, + @Param("end") Instant end + ); +} + diff --git a/src/main/java/com/lottery/lottery/repository/PromotionRepository.java b/src/main/java/com/lottery/lottery/repository/PromotionRepository.java new file mode 100644 index 0000000..3257c34 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/PromotionRepository.java @@ -0,0 +1,30 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.Promotion; +import com.lottery.lottery.model.Promotion.PromotionStatus; +import com.lottery.lottery.model.Promotion.PromotionType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.time.Instant; +import java.util.List; + +@Repository +public interface PromotionRepository extends JpaRepository { + + /** + * Find all promotions of given type that are ACTIVE and current time is within [start_time, end_time]. + */ + @Query("SELECT p FROM Promotion p WHERE p.type = :type AND p.status = 'ACTIVE' " + + "AND p.startTime < :now AND p.endTime > :now") + List findActiveByTypeAndTimeRange( + @Param("type") PromotionType type, + @Param("now") Instant now); + + List findAllByOrderByStartTimeDesc(); + + /** For app: list promotions with status in ACTIVE, PLANNED, FINISHED (exclude INACTIVE). */ + List findByStatusInOrderByStartTimeDesc(List statuses); +} diff --git a/src/main/java/com/lottery/lottery/repository/PromotionRewardRepository.java b/src/main/java/com/lottery/lottery/repository/PromotionRewardRepository.java new file mode 100644 index 0000000..2911916 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/PromotionRewardRepository.java @@ -0,0 +1,15 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.PromotionReward; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface PromotionRewardRepository extends JpaRepository { + + List findByPromotionIdOrderByPlaceAsc(Integer promotionId); + + void deleteByPromotionId(Integer promotionId); +} diff --git a/src/main/java/com/lottery/lottery/repository/PromotionUserRepository.java b/src/main/java/com/lottery/lottery/repository/PromotionUserRepository.java new file mode 100644 index 0000000..8f7cd32 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/PromotionUserRepository.java @@ -0,0 +1,32 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.PromotionUser; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.math.BigDecimal; +import java.util.Optional; + +@Repository +public interface PromotionUserRepository extends JpaRepository { + + Optional findByPromoIdAndUserId(Integer promoId, Integer userId); + + Page findByPromoId(Integer promoId, Pageable pageable); + + Page findByPromoIdAndUserId(Integer promoId, Integer userId, Pageable pageable); + + long countByPromoId(Integer promoId); + + @Query("SELECT COUNT(pu) FROM PromotionUser pu WHERE pu.promoId = :promoId AND pu.points > :points") + long countByPromoIdAndPointsGreaterThan(@Param("promoId") int promoId, @Param("points") java.math.BigDecimal points); + + @Modifying + @Query("UPDATE PromotionUser pu SET pu.points = :points WHERE pu.promoId = :promoId AND pu.userId = :userId") + int updatePoints(@Param("promoId") int promoId, @Param("userId") int userId, @Param("points") BigDecimal points); +} diff --git a/src/main/java/com/lottery/lottery/repository/QuickAnswerRepository.java b/src/main/java/com/lottery/lottery/repository/QuickAnswerRepository.java new file mode 100644 index 0000000..8b47acf --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/QuickAnswerRepository.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.QuickAnswer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface QuickAnswerRepository extends JpaRepository { + List findByAdminIdOrderByCreatedAtDesc(Integer adminId); +} + diff --git a/src/main/java/com/lottery/lottery/repository/SafeBotUserRepository.java b/src/main/java/com/lottery/lottery/repository/SafeBotUserRepository.java new file mode 100644 index 0000000..f21edbf --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/SafeBotUserRepository.java @@ -0,0 +1,13 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.SafeBotUser; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface SafeBotUserRepository extends JpaRepository { + + List findAllByOrderByUserIdAsc(); +} diff --git a/src/main/java/com/honey/honey/repository/SessionRepository.java b/src/main/java/com/lottery/lottery/repository/SessionRepository.java similarity index 77% rename from src/main/java/com/honey/honey/repository/SessionRepository.java rename to src/main/java/com/lottery/lottery/repository/SessionRepository.java index b77807c..08b132e 100644 --- a/src/main/java/com/honey/honey/repository/SessionRepository.java +++ b/src/main/java/com/lottery/lottery/repository/SessionRepository.java @@ -1,6 +1,6 @@ -package com.honey.honey.repository; +package com.lottery.lottery.repository; -import com.honey.honey.model.Session; +import com.lottery.lottery.model.Session; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -29,6 +29,18 @@ public interface SessionRepository extends JpaRepository { @Query("SELECT s FROM Session s WHERE s.userId = :userId AND s.expiresAt > :now ORDER BY s.createdAt ASC") List findOldestActiveSessionsByUserId(@Param("userId") Integer userId, @Param("now") LocalDateTime now, Pageable pageable); + /** + * Counts all sessions (active + expired) for a user. + */ + long countByUserId(Integer userId); + + /** + * Finds oldest sessions (active or expired) for a user, ordered by created_at ASC. + * Used to delete oldest sessions when max limit is exceeded. + */ + @Query("SELECT s FROM Session s WHERE s.userId = :userId ORDER BY s.createdAt ASC") + List findOldestSessionsByUserId(@Param("userId") Integer userId, Pageable pageable); + /** * Batch deletes expired sessions (up to batchSize). * Returns the number of deleted rows. diff --git a/src/main/java/com/lottery/lottery/repository/SupportMessageRepository.java b/src/main/java/com/lottery/lottery/repository/SupportMessageRepository.java new file mode 100644 index 0000000..d5dce91 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/SupportMessageRepository.java @@ -0,0 +1,32 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.SupportMessage; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface SupportMessageRepository extends JpaRepository { + + /** + * Find all messages for a ticket, ordered by created_at ASC. + */ + List findByTicketIdOrderByCreatedAtAsc(Long ticketId); + + /** + * Count messages for a ticket. + */ + long countByTicketId(Long ticketId); + + /** + * Find the last message for a user in a specific ticket (for rate limiting per ticket). + * Returns only the most recent message (limit 1). + */ + @Query("SELECT m FROM SupportMessage m WHERE m.ticket.id = :ticketId AND m.user.id = :userId ORDER BY m.createdAt DESC") + List findLastMessageByTicketIdAndUserId(@Param("ticketId") Long ticketId, @Param("userId") Integer userId, Pageable pageable); +} + diff --git a/src/main/java/com/lottery/lottery/repository/SupportTicketRepository.java b/src/main/java/com/lottery/lottery/repository/SupportTicketRepository.java new file mode 100644 index 0000000..eb758da --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/SupportTicketRepository.java @@ -0,0 +1,51 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.SupportTicket; +import com.lottery.lottery.model.SupportTicket.TicketStatus; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface SupportTicketRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * Count OPENED tickets for a user. + */ + long countByUserIdAndStatus(Integer userId, TicketStatus status); + + /** + * Count tickets by status (for admin dashboard). + */ + long countByStatus(TicketStatus status); + + /** + * Find tickets by user ID, ordered by created_at DESC. + */ + Page findByUserIdOrderByCreatedAtDesc(Integer userId, Pageable pageable); + + /** + * Find a ticket by ID and user ID (for security - users can only access their own tickets). + */ + Optional findByIdAndUserId(Long id, Integer userId); + + /** + * Find all tickets ordered by created_at DESC (for admin panel). + */ + Page findAllByOrderByCreatedAtDesc(Pageable pageable); + + /** + * Find tickets by status, ordered by created_at DESC (for admin panel). + */ + Page findByStatusOrderByCreatedAtDesc(TicketStatus status, Pageable pageable); +} + + + diff --git a/src/main/java/com/lottery/lottery/repository/TaskRepository.java b/src/main/java/com/lottery/lottery/repository/TaskRepository.java new file mode 100644 index 0000000..46ae6da --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/TaskRepository.java @@ -0,0 +1,14 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.Task; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface TaskRepository extends JpaRepository { + List findByTypeOrderByDisplayOrderAsc(String type); + List findByTypeAndRequirementIn(String type, List requirements); +} + diff --git a/src/main/java/com/lottery/lottery/repository/TransactionRepository.java b/src/main/java/com/lottery/lottery/repository/TransactionRepository.java new file mode 100644 index 0000000..0c1e101 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/TransactionRepository.java @@ -0,0 +1,62 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.Transaction; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Set; +import java.time.Instant; + +@Repository +public interface TransactionRepository extends JpaRepository { + + /** + * Finds all transactions for a user, ordered by creation time descending (newest first). + * Uses index idx_user_id_created_at for optimal performance. + */ + Page findByUserIdOrderByCreatedAtDesc(Integer userId, Pageable pageable); + + /** + * Finds WIN transactions for a user created after the specified date, ordered by creation time descending. + * Used for game history (win history). + */ + Page findByUserIdAndTypeAndCreatedAtAfterOrderByCreatedAtDesc( + Integer userId, Transaction.TransactionType type, Instant createdAfter, Pageable pageable); + + /** + * Batch deletes all transactions older than the specified date (up to batchSize). + * Returns the number of deleted rows. + * Note: MySQL requires LIMIT to be used directly in DELETE statements. + */ + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Query(value = "DELETE FROM transactions WHERE created_at < :cutoffDate LIMIT :batchSize", nativeQuery = true) + int deleteOldTransactionsBatch(@Param("cutoffDate") Instant cutoffDate, @Param("batchSize") int batchSize); + + /** + * Counts transactions of a specific type for a user. + * Used to check if this is the user's 3rd bet for referral bonus. + */ + long countByUserIdAndType(Integer userId, Transaction.TransactionType type); + + /** + * Returns sum of transaction amounts per user for the given user IDs (batch, for admin list). + */ + @Query("SELECT t.userId, COALESCE(SUM(t.amount), 0) FROM Transaction t WHERE t.userId IN :userIds GROUP BY t.userId") + List sumAmountByUserIdIn(@Param("userIds") List userIds); + + /** BET transactions for a user, ordered by createdAt desc (for game history). */ + Page findByUserIdAndTypeOrderByCreatedAtDesc(Integer userId, Transaction.TransactionType type, Pageable pageable); + + /** WIN transactions for a user and given round IDs (batch). */ + @Query("SELECT t FROM Transaction t WHERE t.userId = :userId AND t.type = 'WIN' AND t.roundId IN :roundIds") + List findByUserIdAndTypeWinAndRoundIdIn(@Param("userId") Integer userId, @Param("roundIds") Set roundIds); +} + diff --git a/src/main/java/com/lottery/lottery/repository/UserARepository.java b/src/main/java/com/lottery/lottery/repository/UserARepository.java new file mode 100644 index 0000000..48e1b12 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/UserARepository.java @@ -0,0 +1,54 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.UserA; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserARepository extends JpaRepository, JpaSpecificationExecutor { + /** + * Users with id in [fromId, toId] for notification broadcast (paged). + */ + @Query("SELECT u FROM UserA u WHERE u.id >= :fromId AND u.id <= :toId ORDER BY u.id") + Page findByIdBetween(@Param("fromId") int fromId, @Param("toId") int toId, Pageable pageable); + + /** + * Max user id for default broadcast range (1 to latest). + */ + @Query("SELECT COALESCE(MAX(u.id), 0) FROM UserA u") + int getMaxId(); + Optional findByTelegramId(Long telegramId); + + /** + * Counts users registered after the specified Unix timestamp (seconds). + */ + @Query("SELECT COUNT(u) FROM UserA u WHERE u.dateReg >= :timestamp") + long countByDateRegAfter(@Param("timestamp") Integer timestamp); + + /** + * Counts users who logged in after the specified Unix timestamp (seconds). + */ + @Query("SELECT COUNT(u) FROM UserA u WHERE u.dateLogin >= :timestamp") + long countByDateLoginAfter(@Param("timestamp") Integer timestamp); + + /** + * Counts users registered between two Unix timestamps (seconds). + */ + @Query("SELECT COUNT(u) FROM UserA u WHERE u.dateReg >= :start AND u.dateReg < :end") + long countByDateRegBetween(@Param("start") Integer start, @Param("end") Integer end); + + /** + * Counts users who logged in between two Unix timestamps (seconds). + */ + @Query("SELECT COUNT(u) FROM UserA u WHERE u.dateLogin >= :start AND u.dateLogin < :end") + long countByDateLoginBetween(@Param("start") Integer start, @Param("end") Integer end); +} + + diff --git a/src/main/java/com/lottery/lottery/repository/UserBRepository.java b/src/main/java/com/lottery/lottery/repository/UserBRepository.java new file mode 100644 index 0000000..780a2da --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/UserBRepository.java @@ -0,0 +1,25 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.UserB; +import jakarta.persistence.LockModeType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserBRepository extends JpaRepository { + + /** + * Loads UserB with pessimistic write lock (SELECT ... FOR UPDATE) so that concurrent + * withdrawals for the same user are serialized and cannot double-spend. + */ + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT b FROM UserB b WHERE b.id = :id") + Optional findByIdForUpdate(@Param("id") Integer id); +} + + diff --git a/src/main/java/com/lottery/lottery/repository/UserDRepository.java b/src/main/java/com/lottery/lottery/repository/UserDRepository.java new file mode 100644 index 0000000..8f3e5d8 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/UserDRepository.java @@ -0,0 +1,114 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.dto.ReferralDto; +import com.lottery.lottery.model.UserD; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface UserDRepository extends JpaRepository { + + /** + * Increments referals_1 for a user. + */ + @Modifying + @Query("UPDATE UserD u SET u.referals1 = u.referals1 + 1 WHERE u.id = :userId") + void incrementReferals1(@Param("userId") Integer userId); + + /** + * Increments referals_2 for a user. + */ + @Modifying + @Query("UPDATE UserD u SET u.referals2 = u.referals2 + 1 WHERE u.id = :userId") + void incrementReferals2(@Param("userId") Integer userId); + + /** + * Increments referals_3 for a user. + */ + @Modifying + @Query("UPDATE UserD u SET u.referals3 = u.referals3 + 1 WHERE u.id = :userId") + void incrementReferals3(@Param("userId") Integer userId); + + /** + * Increments referals_4 for a user. + */ + @Modifying + @Query("UPDATE UserD u SET u.referals4 = u.referals4 + 1 WHERE u.id = :userId") + void incrementReferals4(@Param("userId") Integer userId); + + /** + * Increments referals_5 for a user. + */ + @Modifying + @Query("UPDATE UserD u SET u.referals5 = u.referals5 + 1 WHERE u.id = :userId") + void incrementReferals5(@Param("userId") Integer userId); + + /** + * Finds referrals for level 1 (where referer_id_1 = userId). + * Returns referrals with their screen_name and to_referer_1 commission. + */ + @Query("SELECT new com.lottery.lottery.dto.ReferralDto(" + + "ud.screenName, ud.toReferer1) " + + "FROM UserD ud " + + "WHERE ud.refererId1 = :userId AND ud.refererId1 > 0 " + + "ORDER BY ud.toReferer1 DESC") + Page findReferralsLevel1(@Param("userId") Integer userId, Pageable pageable); + + /** + * Finds referrals for level 2 (where referer_id_2 = userId). + * Returns referrals with their screen_name and to_referer_2 commission. + */ + @Query("SELECT new com.lottery.lottery.dto.ReferralDto(" + + "ud.screenName, ud.toReferer2) " + + "FROM UserD ud " + + "WHERE ud.refererId2 = :userId AND ud.refererId2 > 0 " + + "ORDER BY ud.toReferer2 DESC") + Page findReferralsLevel2(@Param("userId") Integer userId, Pageable pageable); + + /** + * Finds referrals for level 3 (where referer_id_3 = userId). + * Returns referrals with their screen_name and to_referer_3 commission. + */ + @Query("SELECT new com.lottery.lottery.dto.ReferralDto(" + + "ud.screenName, ud.toReferer3) " + + "FROM UserD ud " + + "WHERE ud.refererId3 = :userId AND ud.refererId3 > 0 " + + "ORDER BY ud.toReferer3 DESC") + Page findReferralsLevel3(@Param("userId") Integer userId, Pageable pageable); + + /** + * Masters: users whose id equals their master_id (and master_id > 0). + * Ordered by id DESC (primary key) by default. + */ + @Query("SELECT d FROM UserD d WHERE d.id = d.masterId AND d.masterId > 0 ORDER BY d.id DESC") + List findAllMasters(); + + /** + * IDs of users who are Masters (id = master_id and master_id > 0). Used to exclude them from GAME_ADMIN views. + */ + @Query("SELECT d.id FROM UserD d WHERE d.id = d.masterId AND d.masterId > 0") + List findMasterUserIds(); + + /** + * For each master_id in the list, returns total referral count (users with that master_id). + * Returns Object[] { masterId (Integer), count (Long) }. + */ + @Query(value = "SELECT d.master_id, COUNT(*) FROM db_users_d d WHERE d.master_id IN :masterIds GROUP BY d.master_id", nativeQuery = true) + List countReferralsByMasterIds(@Param("masterIds") List masterIds); + + /** + * Sum of deposit_total and withdraw_total per master_id (over all users with that master_id). + * Returns Object[] { masterId (Integer), sumDeposit (Long), sumWithdraw (Long) }. + */ + @Query(value = "SELECT d.master_id, COALESCE(SUM(b.deposit_total),0), COALESCE(SUM(b.withdraw_total),0) FROM db_users_d d LEFT JOIN db_users_b b ON b.id = d.id WHERE d.master_id IN :masterIds GROUP BY d.master_id", nativeQuery = true) + List sumDepositWithdrawByMasterIds(@Param("masterIds") List masterIds); +} + + diff --git a/src/main/java/com/lottery/lottery/repository/UserDailyBonusClaimRepository.java b/src/main/java/com/lottery/lottery/repository/UserDailyBonusClaimRepository.java new file mode 100644 index 0000000..383cd25 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/UserDailyBonusClaimRepository.java @@ -0,0 +1,31 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.UserDailyBonusClaim; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface UserDailyBonusClaimRepository extends JpaRepository { + + /** + * Finds the most recent daily bonus claim for a user. + * Used to check if user can claim (24h cooldown). + */ + Optional findFirstByUserIdOrderByClaimedAtDesc(Integer userId); + + /** + * Finds the 50 most recent daily bonus claims ordered by claimed_at DESC. + * Simple query without JOINs - all data is in the same table. + */ + List findTop50ByOrderByClaimedAtDesc(); + + /** + * Finds all daily bonus claims for a user, ordered by claimed_at DESC. + */ + List findByUserIdOrderByClaimedAtDesc(Integer userId); +} + diff --git a/src/main/java/com/lottery/lottery/repository/UserTaskClaimRepository.java b/src/main/java/com/lottery/lottery/repository/UserTaskClaimRepository.java new file mode 100644 index 0000000..a124394 --- /dev/null +++ b/src/main/java/com/lottery/lottery/repository/UserTaskClaimRepository.java @@ -0,0 +1,20 @@ +package com.lottery.lottery.repository; + +import com.lottery.lottery.model.UserTaskClaim; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface UserTaskClaimRepository extends JpaRepository { + Optional findByUserIdAndTaskId(Integer userId, Integer taskId); + List findByUserId(Integer userId); + boolean existsByUserIdAndTaskId(Integer userId, Integer taskId); + boolean existsByUserIdAndTaskIdIn(Integer userId, List taskIds); +} + + + + diff --git a/src/main/java/com/honey/honey/security/AuthInterceptor.java b/src/main/java/com/lottery/lottery/security/AuthInterceptor.java similarity index 63% rename from src/main/java/com/honey/honey/security/AuthInterceptor.java rename to src/main/java/com/lottery/lottery/security/AuthInterceptor.java index c43ae02..6353f9c 100644 --- a/src/main/java/com/honey/honey/security/AuthInterceptor.java +++ b/src/main/java/com/lottery/lottery/security/AuthInterceptor.java @@ -1,7 +1,9 @@ -package com.honey.honey.security; +package com.lottery.lottery.security; -import com.honey.honey.model.UserA; -import com.honey.honey.service.SessionService; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.service.LocalizationService; +import com.lottery.lottery.service.SessionService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -9,6 +11,7 @@ import org.springframework.web.servlet.HandlerInterceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.util.Map; import java.util.Optional; @Slf4j @@ -17,6 +20,8 @@ import java.util.Optional; public class AuthInterceptor implements HandlerInterceptor { private final SessionService sessionService; + private final LocalizationService localizationService; + private final ObjectMapper objectMapper = new ObjectMapper(); @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) { @@ -32,7 +37,7 @@ public class AuthInterceptor implements HandlerInterceptor { // If no Bearer token, fail if (sessionId == null || sessionId.isBlank()) { - log.warn("❌ Missing Bearer token in Authorization header"); + log.debug("Missing Bearer token"); res.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } @@ -41,15 +46,29 @@ public class AuthInterceptor implements HandlerInterceptor { Optional userOpt = sessionService.getUserBySession(sessionId); if (userOpt.isEmpty()) { - log.warn("❌ Invalid or expired session: {}", maskSessionId(sessionId)); + log.debug("Invalid or expired session: {}", maskSessionId(sessionId)); res.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } - // Put user in context UserA user = userOpt.get(); + if (user.getBanned() != null && user.getBanned() == 1) { + log.debug("Banned user attempted access: userId={}", user.getId()); + res.setStatus(HttpServletResponse.SC_FORBIDDEN); + res.setContentType("application/json"); + res.setCharacterEncoding("UTF-8"); + try { + String message = localizationService.getMessageForUser(user.getId(), "auth.error.accessRestricted"); + String body = objectMapper.writeValueAsString(Map.of("code", "BANNED", "message", message)); + res.getWriter().write(body); + } catch (Exception e) { + log.warn("Failed to write banned response body", e); + } + return false; + } + + // Put user in context UserContext.set(user); - log.debug("🔑 Authenticated userId={} via session", user.getId()); return true; } diff --git a/src/main/java/com/lottery/lottery/security/RateLimitInterceptor.java b/src/main/java/com/lottery/lottery/security/RateLimitInterceptor.java new file mode 100644 index 0000000..f75c680 --- /dev/null +++ b/src/main/java/com/lottery/lottery/security/RateLimitInterceptor.java @@ -0,0 +1,98 @@ +package com.lottery.lottery.security; + +import com.lottery.lottery.util.IpUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Rate limiting interceptor (IP-based). Limits requests per IP to prevent abuse. + * Note: Not currently registered for any path in WebConfig; available if needed. + */ +@Slf4j +@Component +public class RateLimitInterceptor implements HandlerInterceptor { + + // Rate limit configuration + private static final int MAX_REQUESTS_PER_IP = 10; // per time window + private static final long TIME_WINDOW_MS = 60_000; // 1 minute + + // IP-based rate limiting + private final Map ipRateLimit = new ConcurrentHashMap<>(); + + @Override + public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) { + // Allow CORS preflight (OPTIONS) without rate limiting + if ("OPTIONS".equalsIgnoreCase(req.getMethod())) { + return true; + } + + String clientIp = IpUtils.getClientIp(req); + + if (clientIp == null) { + // If we can't determine IP, allow the request (shouldn't happen, but be safe) + return true; + } + + // Check IP-based rate limit + if (isRateLimited(clientIp, ipRateLimit, MAX_REQUESTS_PER_IP)) { + log.warn("Rate limit exceeded for bot registration - IP={}, maxRequests={}, window={}ms", + clientIp, MAX_REQUESTS_PER_IP, TIME_WINDOW_MS); + res.setStatus(429); // 429 Too Many Requests + res.setHeader("Retry-After", "60"); // Retry after 60 seconds + return false; + } + + return true; + } + + /** + * Checks if a key is rate limited. + */ + private boolean isRateLimited(String key, Map rateLimitMap, int maxRequests) { + long now = System.currentTimeMillis(); + + RateLimitEntry entry = rateLimitMap.computeIfAbsent(key, k -> new RateLimitEntry()); + + // Reset if time window has passed + if (now - entry.windowStart.get() > TIME_WINDOW_MS) { + entry.count.set(0); + entry.windowStart.set(now); + } + + // Increment and check + int currentCount = entry.count.incrementAndGet(); + + // Clean up old entries periodically + if (currentCount == 1 && rateLimitMap.size() > 1000) { + cleanupOldEntries(rateLimitMap, now); + } + + return currentCount > maxRequests; + } + + /** + * Cleans up old rate limit entries. + */ + private void cleanupOldEntries(Map rateLimitMap, long now) { + rateLimitMap.entrySet().removeIf(entry -> + now - entry.getValue().windowStart.get() > TIME_WINDOW_MS * 2 + ); + } + + /** + * Rate limit entry for tracking requests. + */ + private static class RateLimitEntry { + final AtomicInteger count = new AtomicInteger(0); + final AtomicLong windowStart = new AtomicLong(System.currentTimeMillis()); + } +} + diff --git a/src/main/java/com/honey/honey/security/UserContext.java b/src/main/java/com/lottery/lottery/security/UserContext.java similarity index 80% rename from src/main/java/com/honey/honey/security/UserContext.java rename to src/main/java/com/lottery/lottery/security/UserContext.java index 90b9f4f..ee6d97e 100644 --- a/src/main/java/com/honey/honey/security/UserContext.java +++ b/src/main/java/com/lottery/lottery/security/UserContext.java @@ -1,6 +1,6 @@ -package com.honey.honey.security; +package com.lottery.lottery.security; -import com.honey.honey.model.UserA; +import com.lottery.lottery.model.UserA; public class UserContext { diff --git a/src/main/java/com/lottery/lottery/security/UserRateLimitInterceptor.java b/src/main/java/com/lottery/lottery/security/UserRateLimitInterceptor.java new file mode 100644 index 0000000..5c1bd00 --- /dev/null +++ b/src/main/java/com/lottery/lottery/security/UserRateLimitInterceptor.java @@ -0,0 +1,113 @@ +package com.lottery.lottery.security; + +import com.lottery.lottery.model.UserA; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Rate limiting interceptor for authenticated user endpoints. + * Limits requests per user ID to prevent abuse. + * Requires UserContext to be set (must be applied after AuthInterceptor). + */ +@Slf4j +@Component +public class UserRateLimitInterceptor implements HandlerInterceptor { + + // Rate limit configuration for payment creation + private static final int MAX_REQUESTS_PER_USER = 5; // per time window + private static final long TIME_WINDOW_MS = 60_000; // 1 minute + + // User-based rate limiting + private final Map userRateLimit = new ConcurrentHashMap<>(); + + @Override + public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) { + // Allow CORS preflight (OPTIONS) without rate limiting + if ("OPTIONS".equalsIgnoreCase(req.getMethod())) { + return true; + } + + // Only rate limit POST requests (GET requests like /api/payouts/history should not be rate limited) + if (!"POST".equalsIgnoreCase(req.getMethod())) { + return true; + } + + // Get user from context (set by AuthInterceptor) + UserA user = UserContext.get(); + if (user == null) { + // If no user context, allow (shouldn't happen for authenticated endpoints, but be safe) + return true; + } + + Integer userId = user.getId(); + + // Check user-based rate limit + if (isRateLimited(userId, userRateLimit, MAX_REQUESTS_PER_USER)) { + log.warn("Rate limit exceeded: userId={}, endpoint={}", userId, req.getRequestURI()); + res.setStatus(429); // 429 Too Many Requests + res.setHeader("Retry-After", "60"); // Retry after 60 seconds + res.setContentType("application/json;charset=UTF-8"); + try { + java.io.PrintWriter writer = res.getWriter(); + writer.write("{\"message\":\"Too many requests. Please wait a moment before trying again.\"}"); + writer.flush(); + } catch (Exception e) { + log.error("Error writing rate limit response", e); + } + return false; + } + + return true; + } + + /** + * Checks if a user is rate limited. + */ + private boolean isRateLimited(Integer userId, Map rateLimitMap, int maxRequests) { + long now = System.currentTimeMillis(); + + RateLimitEntry entry = rateLimitMap.computeIfAbsent(userId, k -> new RateLimitEntry()); + + // Reset if time window has passed + if (now - entry.windowStart.get() > TIME_WINDOW_MS) { + entry.count.set(0); + entry.windowStart.set(now); + } + + // Increment and check + int currentCount = entry.count.incrementAndGet(); + + // Clean up old entries periodically + if (currentCount == 1 && rateLimitMap.size() > 1000) { + cleanupOldEntries(rateLimitMap, now); + } + + return currentCount > maxRequests; + } + + /** + * Cleans up old rate limit entries. + */ + private void cleanupOldEntries(Map rateLimitMap, long now) { + rateLimitMap.entrySet().removeIf(entry -> + now - entry.getValue().windowStart.get() > TIME_WINDOW_MS * 2 + ); + } + + /** + * Rate limit entry for tracking requests. + */ + private static class RateLimitEntry { + final AtomicInteger count = new AtomicInteger(0); + final AtomicLong windowStart = new AtomicLong(System.currentTimeMillis()); + } +} + diff --git a/src/main/java/com/lottery/lottery/security/admin/AdminDetailsService.java b/src/main/java/com/lottery/lottery/security/admin/AdminDetailsService.java new file mode 100644 index 0000000..37dfd16 --- /dev/null +++ b/src/main/java/com/lottery/lottery/security/admin/AdminDetailsService.java @@ -0,0 +1,39 @@ +package com.lottery.lottery.security.admin; + +import com.lottery.lottery.model.Admin; +import com.lottery.lottery.repository.AdminRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class AdminDetailsService implements UserDetailsService { + + private final AdminRepository adminRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Admin admin = adminRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("Admin not found: " + username)); + + List authorities = Collections.singletonList( + new SimpleGrantedAuthority(admin.getRole()) + ); + + return User.builder() + .username(admin.getUsername()) + .password(admin.getPasswordHash()) + .authorities(authorities) + .build(); + } +} + diff --git a/src/main/java/com/lottery/lottery/security/admin/JwtAuthenticationFilter.java b/src/main/java/com/lottery/lottery/security/admin/JwtAuthenticationFilter.java new file mode 100644 index 0000000..4132f95 --- /dev/null +++ b/src/main/java/com/lottery/lottery/security/admin/JwtAuthenticationFilter.java @@ -0,0 +1,64 @@ +package com.lottery.lottery.security.admin; + +import com.lottery.lottery.model.Admin; +import com.lottery.lottery.repository.AdminRepository; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Collections; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + private final AdminRepository adminRepository; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + final String authHeader = request.getHeader("Authorization"); + String username = null; + String jwt = null; + + if (authHeader != null && authHeader.startsWith("Bearer ")) { + jwt = authHeader.substring(7); + try { + username = jwtUtil.getUsernameFromToken(jwt); + } catch (Exception e) { + // Invalid token, continue without authentication + } + } + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + if (jwtUtil.validateToken(jwt, username)) { + // Get admin from database to retrieve actual role + String role = adminRepository.findByUsername(username) + .map(Admin::getRole) + .orElse("ROLE_ADMIN"); // Fallback to ROLE_ADMIN if not found + + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + username, + null, + Collections.singletonList(new SimpleGrantedAuthority(role)) + ); + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + + filterChain.doFilter(request, response); + } +} + diff --git a/src/main/java/com/lottery/lottery/security/admin/JwtUtil.java b/src/main/java/com/lottery/lottery/security/admin/JwtUtil.java new file mode 100644 index 0000000..20abb15 --- /dev/null +++ b/src/main/java/com/lottery/lottery/security/admin/JwtUtil.java @@ -0,0 +1,81 @@ +package com.lottery.lottery.security.admin; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +@Component +public class JwtUtil { + + @Value("${app.admin.jwt.secret:your-secret-key-change-this-in-production-min-256-bits}") + private String secret; + + @Value("${app.admin.jwt.expiration:86400000}") // 24 hours default + private Long expiration; + + private SecretKey getSigningKey() { + return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); + } + + public String generateToken(Integer userId, String username) { + Map claims = new HashMap<>(); + claims.put("userId", userId); + claims.put("username", username); + return createToken(claims, username); + } + + private String createToken(Map claims, String subject) { + return Jwts.builder() + .claims(claims) + .subject(subject) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + expiration)) + .signWith(getSigningKey()) + .compact(); + } + + public Integer getUserIdFromToken(String token) { + return getClaimFromToken(token, claims -> claims.get("userId", Integer.class)); + } + + public String getUsernameFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + public Date getExpirationDateFromToken(String token) { + return getClaimFromToken(token, Claims::getExpiration); + } + + public T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + private Claims getAllClaimsFromToken(String token) { + return Jwts.parser() + .verifyWith(getSigningKey()) + .build() + .parseSignedClaims(token) + .getPayload(); + } + + private Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + public Boolean validateToken(String token, String username) { + final String tokenUsername = getUsernameFromToken(token); + return (tokenUsername.equals(username) && !isTokenExpired(token)); + } +} + diff --git a/src/main/java/com/lottery/lottery/service/AdminBotConfigService.java b/src/main/java/com/lottery/lottery/service/AdminBotConfigService.java new file mode 100644 index 0000000..fc4b6b6 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/AdminBotConfigService.java @@ -0,0 +1,210 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.AdminBotConfigDto; +import com.lottery.lottery.dto.AdminBotConfigRequest; +import com.lottery.lottery.model.LotteryBotConfig; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.LotteryBotConfigRepository; +import com.lottery.lottery.repository.UserARepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class AdminBotConfigService { + + private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm"); + + private final LotteryBotConfigRepository lotteryBotConfigRepository; + private final UserARepository userARepository; + + public List listAll() { + List configs = lotteryBotConfigRepository.findAllByOrderByIdAsc(); + if (configs.isEmpty()) { + return List.of(); + } + List userIds = configs.stream().map(LotteryBotConfig::getUserId).distinct().toList(); + Map screenNameByUserId = userARepository.findAllById(userIds).stream() + .collect(Collectors.toMap(UserA::getId, u -> u.getScreenName() != null ? u.getScreenName() : "-")); + return configs.stream() + .map(c -> toDto(c, screenNameByUserId.getOrDefault(c.getUserId(), "-"))) + .toList(); + } + + public Optional getById(Integer id) { + return lotteryBotConfigRepository.findById(id) + .map(c -> { + String screenName = userARepository.findById(c.getUserId()) + .map(UserA::getScreenName) + .orElse("-"); + return toDto(c, screenName); + }); + } + + public Optional getByUserId(Integer userId) { + return lotteryBotConfigRepository.findByUserId(userId) + .map(c -> { + String screenName = userARepository.findById(c.getUserId()) + .map(UserA::getScreenName) + .orElse("-"); + return toDto(c, screenName); + }); + } + + @Transactional + public AdminBotConfigDto create(AdminBotConfigRequest request) { + if (!userARepository.existsById(request.getUserId())) { + throw new IllegalArgumentException("User with id " + request.getUserId() + " does not exist"); + } + if (lotteryBotConfigRepository.existsByUserId(request.getUserId())) { + throw new IllegalArgumentException("Bot config already exists for user id " + request.getUserId()); + } + LotteryBotConfig config = toEntity(request); + config.setId(null); + config.setCreatedAt(Instant.now()); + config.setUpdatedAt(Instant.now()); + config = lotteryBotConfigRepository.save(config); + String screenName = userARepository.findById(config.getUserId()).map(UserA::getScreenName).orElse("-"); + return toDto(config, screenName); + } + + @Transactional + public Optional update(Integer id, AdminBotConfigRequest request) { + Optional opt = lotteryBotConfigRepository.findById(id); + if (opt.isEmpty()) return Optional.empty(); + if (!userARepository.existsById(request.getUserId())) { + throw new IllegalArgumentException("User with id " + request.getUserId() + " does not exist"); + } + LotteryBotConfig existing = opt.get(); + if (!existing.getUserId().equals(request.getUserId()) && lotteryBotConfigRepository.existsByUserId(request.getUserId())) { + throw new IllegalArgumentException("Bot config already exists for user id " + request.getUserId()); + } + updateEntity(existing, request); + existing.setUpdatedAt(Instant.now()); + LotteryBotConfig saved = lotteryBotConfigRepository.save(existing); + String screenName = userARepository.findById(saved.getUserId()).map(UserA::getScreenName).orElse("-"); + return Optional.of(toDto(saved, screenName)); + } + + @Transactional + public boolean delete(Integer id) { + if (!lotteryBotConfigRepository.existsById(id)) return false; + lotteryBotConfigRepository.deleteById(id); + return true; + } + + /** + * Shuffles time windows for bots that have the given room enabled. + * Groups configs by their current time window, then randomly redistributes those same windows across all configs. + */ + @Transactional + public void shuffleTimeWindowsForRoom(int roomNumber) { + List configs = roomNumber == 2 + ? lotteryBotConfigRepository.findAllByRoom2True() + : lotteryBotConfigRepository.findAllByRoom3True(); + if (configs.isEmpty()) { + throw new IllegalArgumentException("No bot configs with room " + roomNumber + " enabled"); + } + shuffleWindows(configs); + Instant now = Instant.now(); + for (LotteryBotConfig c : configs) { + c.setUpdatedAt(now); + } + lotteryBotConfigRepository.saveAll(configs); + } + + /** Groups configs by (start, end) window, collects one slot per config, shuffles slots, assigns back. */ + private static void shuffleWindows(List configs) { + Map> byWindow = new LinkedHashMap<>(); + for (LotteryBotConfig c : configs) { + if (c.getTimeUtcStart() == null || c.getTimeUtcEnd() == null) continue; + TimeWindow w = new TimeWindow(c.getTimeUtcStart(), c.getTimeUtcEnd()); + byWindow.computeIfAbsent(w, k -> new ArrayList<>()).add(c); + } + List windowSlots = new ArrayList<>(); + for (Map.Entry> e : byWindow.entrySet()) { + for (int i = 0; i < e.getValue().size(); i++) { + windowSlots.add(e.getKey()); + } + } + Collections.shuffle(windowSlots); + int idx = 0; + for (LotteryBotConfig c : configs) { + if (c.getTimeUtcStart() != null && c.getTimeUtcEnd() != null && idx < windowSlots.size()) { + TimeWindow w = windowSlots.get(idx++); + c.setTimeUtcStart(w.start); + c.setTimeUtcEnd(w.end); + } + } + } + + private record TimeWindow(LocalTime start, LocalTime end) {} + + private static AdminBotConfigDto toDto(LotteryBotConfig c, String screenName) { + return AdminBotConfigDto.builder() + .id(c.getId()) + .userId(c.getUserId()) + .screenName(screenName) + .room1(c.getRoom1()) + .room2(c.getRoom2()) + .room3(c.getRoom3()) + .timeUtcStart(c.getTimeUtcStart() != null ? c.getTimeUtcStart().format(TIME_FORMAT) : null) + .timeUtcEnd(c.getTimeUtcEnd() != null ? c.getTimeUtcEnd().format(TIME_FORMAT) : null) + .betMin(c.getBetMin()) + .betMax(c.getBetMax()) + .persona(c.getPersona() != null ? c.getPersona() : "balanced") + .active(c.getActive()) + .createdAt(c.getCreatedAt()) + .updatedAt(c.getUpdatedAt()) + .build(); + } + + private static LotteryBotConfig toEntity(AdminBotConfigRequest r) { + return LotteryBotConfig.builder() + .userId(r.getUserId()) + .room1(r.getRoom1()) + .room2(r.getRoom2()) + .room3(r.getRoom3()) + .timeUtcStart(parseTime(r.getTimeUtcStart())) + .timeUtcEnd(parseTime(r.getTimeUtcEnd())) + .betMin(r.getBetMin()) + .betMax(r.getBetMax()) + .persona(r.getPersona() != null && !r.getPersona().isBlank() ? r.getPersona() : "balanced") + .active(r.getActive()) + .build(); + } + + private static void updateEntity(LotteryBotConfig existing, AdminBotConfigRequest r) { + existing.setUserId(r.getUserId()); + existing.setRoom1(r.getRoom1()); + existing.setRoom2(r.getRoom2()); + existing.setRoom3(r.getRoom3()); + existing.setTimeUtcStart(parseTime(r.getTimeUtcStart())); + existing.setTimeUtcEnd(parseTime(r.getTimeUtcEnd())); + existing.setBetMin(r.getBetMin()); + existing.setBetMax(r.getBetMax()); + existing.setPersona(r.getPersona() != null && !r.getPersona().isBlank() ? r.getPersona() : "balanced"); + existing.setActive(r.getActive()); + } + + private static LocalTime parseTime(String s) { + if (s == null || s.isBlank()) throw new IllegalArgumentException("Time is required (HH:mm)"); + try { + return LocalTime.parse(s.trim(), TIME_FORMAT); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid time format, use HH:mm (e.g. 14:00)"); + } + } +} diff --git a/src/main/java/com/lottery/lottery/service/AdminMasterService.java b/src/main/java/com/lottery/lottery/service/AdminMasterService.java new file mode 100644 index 0000000..2840a9d --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/AdminMasterService.java @@ -0,0 +1,79 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.AdminMasterDto; +import com.lottery.lottery.model.UserD; +import com.lottery.lottery.repository.UserDRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class AdminMasterService { + + private static final BigDecimal USD_DIVISOR = new BigDecimal("1000000000"); + + private final UserDRepository userDRepository; + + @Transactional(readOnly = true) + public List getMasters() { + List masters = userDRepository.findAllMasters(); + if (masters.isEmpty()) { + return List.of(); + } + List masterIds = masters.stream().map(UserD::getId).collect(Collectors.toList()); + + Map referralCountByMaster = mapFromCountQuery(userDRepository.countReferralsByMasterIds(masterIds)); + Map depositWithdrawByMaster = mapFromSumQuery(userDRepository.sumDepositWithdrawByMasterIds(masterIds)); + + return masters.stream() + .map(m -> { + int id = m.getId(); + long totalReferrals = Math.max(0L, referralCountByMaster.getOrDefault(id, 0L) - 1L); // exclude master themselves from count + long[] sums = depositWithdrawByMaster.getOrDefault(id, new long[]{0L, 0L}); + long sumDeposit = sums[0]; + long sumWithdraw = sums[1]; + BigDecimal depositUsd = BigDecimal.valueOf(sumDeposit).divide(USD_DIVISOR, 4, RoundingMode.HALF_UP); + BigDecimal withdrawUsd = BigDecimal.valueOf(sumWithdraw).divide(USD_DIVISOR, 4, RoundingMode.HALF_UP); + BigDecimal profitUsd = depositUsd.subtract(withdrawUsd); + + return AdminMasterDto.builder() + .id(id) + .screenName(m.getScreenName() != null ? m.getScreenName() : "-") + .referals1(m.getReferals1() != null ? m.getReferals1() : 0) + .referals2(m.getReferals2() != null ? m.getReferals2() : 0) + .referals3(m.getReferals3() != null ? m.getReferals3() : 0) + .totalReferrals(totalReferrals) + .depositTotalUsd(depositUsd) + .withdrawTotalUsd(withdrawUsd) + .profitUsd(profitUsd) + .build(); + }) + .collect(Collectors.toList()); + } + + private static Map mapFromCountQuery(List rows) { + return rows.stream() + .collect(Collectors.toMap( + row -> (Integer) row[0], + row -> row[1] instanceof Long ? (Long) row[1] : ((Number) row[1]).longValue() + )); + } + + private static Map mapFromSumQuery(List rows) { + return rows.stream() + .collect(Collectors.toMap( + row -> (Integer) row[0], + row -> new long[]{ + row[1] instanceof Long ? (Long) row[1] : ((Number) row[1]).longValue(), + row[2] instanceof Long ? (Long) row[2] : ((Number) row[2]).longValue() + } + )); + } +} diff --git a/src/main/java/com/lottery/lottery/service/AdminPromotionService.java b/src/main/java/com/lottery/lottery/service/AdminPromotionService.java new file mode 100644 index 0000000..26c3b6d --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/AdminPromotionService.java @@ -0,0 +1,180 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.*; +import com.lottery.lottery.model.Promotion; +import com.lottery.lottery.model.Promotion.PromotionStatus; +import com.lottery.lottery.model.Promotion.PromotionType; +import com.lottery.lottery.model.PromotionReward; +import com.lottery.lottery.model.PromotionUser; +import com.lottery.lottery.repository.PromotionRepository; +import com.lottery.lottery.repository.PromotionRewardRepository; +import com.lottery.lottery.repository.PromotionUserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class AdminPromotionService { + + private final PromotionRepository promotionRepository; + private final PromotionRewardRepository promotionRewardRepository; + private final PromotionUserRepository promotionUserRepository; + + public List listPromotions() { + return promotionRepository.findAllByOrderByStartTimeDesc().stream() + .map(this::toDto) + .collect(Collectors.toList()); + } + + public Optional getPromotion(int id) { + return promotionRepository.findById(id).map(this::toDto); + } + + @Transactional + public AdminPromotionDto createPromotion(AdminPromotionRequest req) { + Promotion p = Promotion.builder() + .type(PromotionType.valueOf(req.getType())) + .startTime(req.getStartTime()) + .endTime(req.getEndTime()) + .status(PromotionStatus.valueOf(req.getStatus())) + .totalReward(req.getTotalReward()) + .createdAt(java.time.Instant.now()) + .updatedAt(java.time.Instant.now()) + .build(); + p = promotionRepository.save(p); + return toDto(p); + } + + @Transactional + public Optional updatePromotion(int id, AdminPromotionRequest req) { + return promotionRepository.findById(id).map(p -> { + p.setType(PromotionType.valueOf(req.getType())); + p.setStartTime(req.getStartTime()); + p.setEndTime(req.getEndTime()); + p.setStatus(PromotionStatus.valueOf(req.getStatus())); + p.setTotalReward(req.getTotalReward()); + return toDto(promotionRepository.save(p)); + }); + } + + @Transactional + public boolean deletePromotion(int id) { + if (!promotionRepository.existsById(id)) return false; + promotionRepository.deleteById(id); + return true; + } + + public List listRewards(int promoId) { + return promotionRewardRepository.findByPromotionIdOrderByPlaceAsc(promoId).stream() + .map(this::toRewardDto) + .collect(Collectors.toList()); + } + + @Transactional + public AdminPromotionRewardDto createReward(int promoId, AdminPromotionRewardRequest req) { + Promotion promo = promotionRepository.findById(promoId) + .orElseThrow(() -> new IllegalArgumentException("Promotion not found: " + promoId)); + PromotionReward r = PromotionReward.builder() + .promotion(promo) + .place(req.getPlace()) + .reward(req.getReward()) + .build(); + r = promotionRewardRepository.save(r); + return toRewardDto(r); + } + + @Transactional + public Optional updateReward(int rewardId, AdminPromotionRewardRequest req) { + return promotionRewardRepository.findById(rewardId).map(r -> { + r.setPlace(req.getPlace()); + r.setReward(req.getReward()); + return toRewardDto(promotionRewardRepository.save(r)); + }); + } + + @Transactional + public boolean deleteReward(int rewardId) { + if (!promotionRewardRepository.existsById(rewardId)) return false; + promotionRewardRepository.deleteById(rewardId); + return true; + } + + public Page getPromotionUsers(int promoId, int page, int size, + String sortBy, String sortDir, + Integer searchUserId) { + Sort.Direction direction = "asc".equalsIgnoreCase(sortDir) ? Sort.Direction.ASC : Sort.Direction.DESC; + String property = "points".equalsIgnoreCase(sortBy) ? "points" : "userId"; + Sort sort = Sort.by(direction, property); + Pageable pageable = PageRequest.of(page, size, sort); + + Page pageResult; + if (searchUserId != null) { + pageResult = promotionUserRepository.findByPromoIdAndUserId(promoId, searchUserId, pageable); + } else { + pageResult = promotionUserRepository.findByPromoId(promoId, pageable); + } + + return pageResult.map(this::toUserDto); + } + + @Transactional + public Optional updatePromotionUserPoints(int promoId, int userId, BigDecimal points) { + Optional opt = promotionUserRepository.findByPromoIdAndUserId(promoId, userId); + if (opt.isEmpty()) { + PromotionUser pu = PromotionUser.builder() + .promoId(promoId) + .userId(userId) + .points(points != null ? points : BigDecimal.ZERO) + .updatedAt(java.time.Instant.now()) + .build(); + pu = promotionUserRepository.save(pu); + return Optional.of(toUserDto(pu)); + } + PromotionUser pu = opt.get(); + pu.setPoints(points != null ? points : BigDecimal.ZERO); + return Optional.of(toUserDto(promotionUserRepository.save(pu))); + } + + private AdminPromotionDto toDto(Promotion p) { + return AdminPromotionDto.builder() + .id(p.getId()) + .type(p.getType().name()) + .startTime(p.getStartTime()) + .endTime(p.getEndTime()) + .status(p.getStatus().name()) + .totalReward(p.getTotalReward()) + .createdAt(p.getCreatedAt()) + .updatedAt(p.getUpdatedAt()) + .build(); + } + + private AdminPromotionRewardDto toRewardDto(PromotionReward r) { + return AdminPromotionRewardDto.builder() + .id(r.getId()) + .promoId(r.getPromotion().getId()) + .place(r.getPlace()) + .reward(r.getReward()) + .createdAt(r.getCreatedAt()) + .updatedAt(r.getUpdatedAt()) + .build(); + } + + private AdminPromotionUserDto toUserDto(PromotionUser pu) { + return AdminPromotionUserDto.builder() + .promoId(pu.getPromoId()) + .userId(pu.getUserId()) + .points(pu.getPoints()) + .updatedAt(pu.getUpdatedAt()) + .build(); + } +} diff --git a/src/main/java/com/lottery/lottery/service/AdminService.java b/src/main/java/com/lottery/lottery/service/AdminService.java new file mode 100644 index 0000000..6618301 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/AdminService.java @@ -0,0 +1,43 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.Admin; +import com.lottery.lottery.repository.AdminRepository; +import com.lottery.lottery.security.admin.JwtUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class AdminService { + + private final AdminRepository adminRepository; + private final PasswordEncoder passwordEncoder; + private final JwtUtil jwtUtil; + + public Optional authenticate(String username, String password) { + Optional adminOpt = adminRepository.findByUsername(username); + + if (adminOpt.isEmpty()) { + return Optional.empty(); + } + + Admin admin = adminOpt.get(); + + // Check password + if (!passwordEncoder.matches(password, admin.getPasswordHash())) { + return Optional.empty(); + } + + // Generate JWT token + String token = jwtUtil.generateToken(admin.getId(), username); + return Optional.of(token); + } + + public Optional getAdminByUsername(String username) { + return adminRepository.findByUsername(username); + } +} + diff --git a/src/main/java/com/lottery/lottery/service/AdminUserService.java b/src/main/java/com/lottery/lottery/service/AdminUserService.java new file mode 100644 index 0000000..58001c1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/AdminUserService.java @@ -0,0 +1,864 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.*; +import com.lottery.lottery.model.*; +import com.lottery.lottery.repository.*; +import com.lottery.lottery.util.IpUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Subquery; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Instant; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class AdminUserService { + + private static final long TICKETS_MULTIPLIER = 1_000_000L; + + private static final BigDecimal TICKETS_TO_USD = new BigDecimal("0.001"); // 1000 tickets = 1 USD + + private final UserARepository userARepository; + private final UserBRepository userBRepository; + private final UserDRepository userDRepository; + private final TransactionRepository transactionRepository; + private final GameRoundParticipantRepository gameRoundParticipantRepository; + private final PaymentRepository paymentRepository; + private final PayoutRepository payoutRepository; + private final UserTaskClaimRepository userTaskClaimRepository; + private final TaskRepository taskRepository; + private final UserDailyBonusClaimRepository userDailyBonusClaimRepository; + private final EntityManager entityManager; + private final GameRoundRepository gameRoundRepository; + + public Page getUsers( + Pageable pageable, + String search, + Integer banned, + String countryCode, + String languageCode, + Integer dateRegFrom, + Integer dateRegTo, + Long balanceMin, + Long balanceMax, + Integer roundsPlayedMin, + Integer roundsPlayedMax, + Integer referralCountMin, + Integer referralCountMax, + Integer referrerId, + Integer referralLevel, + String ipFilter, + String sortBy, + String sortDir, + boolean excludeMasters) { + + List masterIds = excludeMasters ? userDRepository.findMasterUserIds() : List.of(); + + // Build specification for filtering UserA fields + Specification spec = (root, query, cb) -> { + List predicates = new ArrayList<>(); + + if (!masterIds.isEmpty()) { + predicates.add(cb.not(root.get("id").in(masterIds))); + } + + // IP filter (exact match; stored as varbinary(16)) + if (ipFilter != null && !ipFilter.trim().isEmpty()) { + byte[] ipBytes = IpUtils.ipToBytes(ipFilter.trim()); + if (ipBytes != null) { + predicates.add(cb.equal(root.get("ip"), ipBytes)); + } + } + + // Search filter (ID, Telegram ID, or name) + if (search != null && !search.trim().isEmpty()) { + String searchPattern = "%" + search.trim() + "%"; + List searchPredicates = new ArrayList<>(); + + // Try to parse as integer (for ID search) + Integer searchId = parseInteger(search.trim()); + if (searchId != null) { + searchPredicates.add(cb.equal(root.get("id"), searchId)); + } + + // Try to parse as long (for Telegram ID search) + Long searchTelegramId = parseLong(search.trim()); + if (searchTelegramId != null) { + searchPredicates.add(cb.equal(root.get("telegramId"), searchTelegramId)); + } + + // Name search (telegramName or screenName) + searchPredicates.add(cb.or( + cb.like(cb.lower(root.get("telegramName")), searchPattern.toLowerCase()), + cb.like(cb.lower(root.get("screenName")), searchPattern.toLowerCase()) + )); + + predicates.add(cb.or(searchPredicates.toArray(new Predicate[0]))); + } + + // Ban status filter + if (banned != null) { + predicates.add(cb.equal(root.get("banned"), banned)); + } + + // Country code filter + if (countryCode != null && !countryCode.trim().isEmpty()) { + predicates.add(cb.equal(root.get("countryCode"), countryCode.trim())); + } + + // Language code filter + if (languageCode != null && !languageCode.trim().isEmpty()) { + predicates.add(cb.equal(root.get("languageCode"), languageCode.trim())); + } + + // Registration date range filter + if (dateRegFrom != null) { + predicates.add(cb.greaterThanOrEqualTo(root.get("dateReg"), dateRegFrom)); + } + if (dateRegTo != null) { + // Include the end date (<= instead of <) + // Convert to end of day timestamp (23:59:59) + int endOfDayTimestamp = dateRegTo + 86399; // Add 86399 seconds (23:59:59) + predicates.add(cb.lessThanOrEqualTo(root.get("dateReg"), endOfDayTimestamp)); + } + + // Balance / rounds / referral filters via subqueries so DB handles pagination + if (balanceMin != null || balanceMax != null || roundsPlayedMin != null || roundsPlayedMax != null) { + Subquery subB = query.subquery(Integer.class); + Root br = subB.from(UserB.class); + subB.select(br.get("id")); + List subPreds = new ArrayList<>(); + if (balanceMin != null) { + subPreds.add(cb.greaterThanOrEqualTo( + cb.sum(br.get("balanceA"), br.get("balanceB")), balanceMin)); + } + if (balanceMax != null) { + subPreds.add(cb.lessThanOrEqualTo( + cb.sum(br.get("balanceA"), br.get("balanceB")), balanceMax)); + } + if (roundsPlayedMin != null) subPreds.add(cb.greaterThanOrEqualTo(br.get("roundsPlayed"), roundsPlayedMin)); + if (roundsPlayedMax != null) subPreds.add(cb.lessThanOrEqualTo(br.get("roundsPlayed"), roundsPlayedMax)); + subB.where(cb.and(subPreds.toArray(new Predicate[0]))); + predicates.add(cb.in(root.get("id")).value(subB)); + } + if (referralCountMin != null || referralCountMax != null) { + Subquery subD = query.subquery(Integer.class); + Root dr = subD.from(UserD.class); + subD.select(dr.get("id")); + var refSum = cb.sum(cb.sum(cb.sum(cb.sum(dr.get("referals1"), dr.get("referals2")), + dr.get("referals3")), dr.get("referals4")), dr.get("referals5")); + List subPreds = new ArrayList<>(); + if (referralCountMin != null) subPreds.add(cb.greaterThanOrEqualTo(refSum.as(Long.class), cb.literal(referralCountMin.longValue()))); + if (referralCountMax != null) subPreds.add(cb.lessThanOrEqualTo(refSum.as(Long.class), cb.literal(referralCountMax.longValue()))); + subD.where(cb.and(subPreds.toArray(new Predicate[0]))); + predicates.add(cb.in(root.get("id")).value(subD)); + } + + if (referrerId != null && referrerId > 0 && referralLevel != null && referralLevel >= 1 && referralLevel <= 5) { + Subquery subRef = query.subquery(Integer.class); + Root refDr = subRef.from(UserD.class); + subRef.select(refDr.get("id")); + Predicate refererPred = switch (referralLevel) { + case 1 -> cb.equal(refDr.get("refererId1"), referrerId); + case 2 -> cb.equal(refDr.get("refererId2"), referrerId); + case 3 -> cb.equal(refDr.get("refererId3"), referrerId); + case 4 -> cb.equal(refDr.get("refererId4"), referrerId); + case 5 -> cb.equal(refDr.get("refererId5"), referrerId); + default -> cb.disjunction(); + }; + subRef.where(refererPred); + predicates.add(cb.in(root.get("id")).value(subRef)); + } + + return cb.and(predicates.toArray(new Predicate[0])); + }; + + Set sortRequiresJoin = Set.of("balanceA", "depositTotal", "withdrawTotal", "roundsPlayed", "referralCount", "profit"); + boolean useJoinSort = sortBy != null && sortRequiresJoin.contains(sortBy); + List userList; + long totalElements; + + if (useJoinSort) { + List orderedIds = getOrderedUserIdsForAdminList( + search, banned, countryCode, languageCode, + dateRegFrom, dateRegTo, balanceMin, balanceMax, + roundsPlayedMin, roundsPlayedMax, referralCountMin, referralCountMax, + referrerId, referralLevel, ipFilter, + sortBy, sortDir != null ? sortDir : "desc", + pageable.getPageSize(), (int) pageable.getOffset(), + masterIds); + totalElements = userARepository.count(spec); + if (orderedIds.isEmpty()) { + userList = List.of(); + } else { + List unsorted = userARepository.findAllById(orderedIds); + userList = unsorted.stream() + .sorted(Comparator.comparingInt(u -> orderedIds.indexOf(u.getId()))) + .collect(Collectors.toList()); + } + } else { + Page userPage = userARepository.findAll(spec, pageable); + userList = userPage.getContent(); + totalElements = userPage.getTotalElements(); + } + + List userIds = userList.stream().map(UserA::getId).collect(Collectors.toList()); + + Map userBMap = userBRepository.findAllById(userIds).stream() + .collect(Collectors.toMap(UserB::getId, ub -> ub)); + + Map userDMap = userDRepository.findAllById(userIds).stream() + .collect(Collectors.toMap(UserD::getId, ud -> ud)); + + // Map to DTOs (filtering is done in DB via specification subqueries) + List pageContent = userList.stream() + .map(userA -> { + UserB userB = userBMap.getOrDefault(userA.getId(), + UserB.builder() + .id(userA.getId()) + .balanceA(0L) + .balanceB(0L) + .depositTotal(0L) + .depositCount(0) + .withdrawTotal(0L) + .withdrawCount(0) + .roundsPlayed(0) + .build()); + + UserD userD = userDMap.getOrDefault(userA.getId(), + UserD.builder() + .id(userA.getId()) + .referals1(0) + .referals2(0) + .referals3(0) + .referals4(0) + .referals5(0) + .fromReferals1(0L) + .fromReferals2(0L) + .fromReferals3(0L) + .fromReferals4(0L) + .fromReferals5(0L) + .build()); + + int totalReferrals = userD.getReferals1() + userD.getReferals2() + + userD.getReferals3() + userD.getReferals4() + userD.getReferals5(); + long totalCommissions = userD.getFromReferals1() + userD.getFromReferals2() + + userD.getFromReferals3() + userD.getFromReferals4() + userD.getFromReferals5(); + long profit = userB.getDepositTotal() - userB.getWithdrawTotal(); + BigDecimal depositTotalUsd = ticketsToUsd(userB.getDepositTotal()); + BigDecimal withdrawTotalUsd = ticketsToUsd(userB.getWithdrawTotal()); + BigDecimal profitUsd = ticketsToUsd(profit); + + return AdminUserDto.builder() + .id(userA.getId()) + .screenName(userA.getScreenName()) + .telegramId(userA.getTelegramId()) + .telegramName(userA.getTelegramName()) + .balanceA(userB.getBalanceA()) + .balanceB(userB.getBalanceB()) + .depositTotal(userB.getDepositTotal()) + .depositCount(userB.getDepositCount()) + .withdrawTotal(userB.getWithdrawTotal()) + .withdrawCount(userB.getWithdrawCount()) + .roundsPlayed(userB.getRoundsPlayed()) + .dateReg(userA.getDateReg()) + .dateLogin(userA.getDateLogin()) + .banned(userA.getBanned()) + .countryCode(userA.getCountryCode()) + .languageCode(userA.getLanguageCode()) + .referralCount(totalReferrals) + .totalCommissionsEarned(totalCommissions) + .profit(profit) + .depositTotalUsd(depositTotalUsd) + .withdrawTotalUsd(withdrawTotalUsd) + .profitUsd(profitUsd) + .build(); + }) + .collect(Collectors.toList()); + + return new PageImpl<>(pageContent, pageable, totalElements); + } + + /** + * Returns ordered user ids for admin list when sorting by UserB/UserD columns (balanceA, depositTotal, etc.). + */ + @SuppressWarnings("unchecked") + private List getOrderedUserIdsForAdminList( + String search, + Integer banned, + String countryCode, + String languageCode, + Integer dateRegFrom, + Integer dateRegTo, + Long balanceMin, + Long balanceMax, + Integer roundsPlayedMin, + Integer roundsPlayedMax, + Integer referralCountMin, + Integer referralCountMax, + Integer referrerId, + Integer referralLevel, + String ipFilter, + String sortBy, + String sortDir, + int limit, + int offset, + List excludeUserIds) { + StringBuilder sql = new StringBuilder( + "SELECT a.id FROM db_users_a a " + + "INNER JOIN db_users_b b ON a.id = b.id " + + "INNER JOIN db_users_d d ON a.id = d.id WHERE 1=1"); + List params = new ArrayList<>(); + int paramIndex = 1; + + if (excludeUserIds != null && !excludeUserIds.isEmpty()) { + sql.append(" AND a.id NOT IN ("); + for (int i = 0; i < excludeUserIds.size(); i++) { + sql.append(i == 0 ? "?" : ",?"); + } + sql.append(")"); + params.addAll(excludeUserIds); + paramIndex += excludeUserIds.size(); + } + + if (search != null && !search.trim().isEmpty()) { + String searchPattern = "%" + search.trim() + "%"; + Integer searchId = parseInteger(search.trim()); + Long searchTgId = parseLong(search.trim()); + if (searchId != null) { + sql.append(" AND a.id = ?"); + params.add(searchId); + paramIndex++; + } else if (searchTgId != null) { + sql.append(" AND a.telegram_id = ?"); + params.add(searchTgId); + paramIndex++; + } else { + sql.append(" AND (a.screen_name LIKE ? OR a.telegram_name LIKE ?)"); + params.add(searchPattern); + params.add(searchPattern); + paramIndex += 2; + } + } + if (banned != null) { + sql.append(" AND a.banned = ?"); + params.add(banned); + paramIndex++; + } + if (countryCode != null && !countryCode.trim().isEmpty()) { + sql.append(" AND a.country_code = ?"); + params.add(countryCode.trim()); + paramIndex++; + } + if (languageCode != null && !languageCode.trim().isEmpty()) { + sql.append(" AND a.language_code = ?"); + params.add(languageCode.trim()); + paramIndex++; + } + if (dateRegFrom != null) { + sql.append(" AND a.date_reg >= ?"); + params.add(dateRegFrom); + paramIndex++; + } + if (dateRegTo != null) { + sql.append(" AND a.date_reg <= ?"); + params.add(dateRegTo); + paramIndex++; + } + if (balanceMin != null) { + sql.append(" AND (b.balance_a + b.balance_b) >= ?"); + params.add(balanceMin); + paramIndex++; + } + if (balanceMax != null) { + sql.append(" AND (b.balance_a + b.balance_b) <= ?"); + params.add(balanceMax); + paramIndex++; + } + if (roundsPlayedMin != null) { + sql.append(" AND b.rounds_played >= ?"); + params.add(roundsPlayedMin); + paramIndex++; + } + if (roundsPlayedMax != null) { + sql.append(" AND b.rounds_played <= ?"); + params.add(roundsPlayedMax); + paramIndex++; + } + if (referralCountMin != null || referralCountMax != null) { + sql.append(" AND (d.referals_1 + d.referals_2 + d.referals_3 + d.referals_4 + d.referals_5)"); + if (referralCountMin != null && referralCountMax != null) { + sql.append(" BETWEEN ? AND ?"); + params.add(referralCountMin.longValue()); + params.add(referralCountMax.longValue()); + paramIndex += 2; + } else if (referralCountMin != null) { + sql.append(" >= ?"); + params.add(referralCountMin.longValue()); + paramIndex++; + } else { + sql.append(" <= ?"); + params.add(referralCountMax.longValue()); + paramIndex++; + } + } + if (referrerId != null && referrerId > 0 && referralLevel != null && referralLevel >= 1 && referralLevel <= 5) { + sql.append(" AND d.referer_id_").append(referralLevel).append(" = ?"); + params.add(referrerId); + paramIndex++; + } + if (ipFilter != null && !ipFilter.trim().isEmpty()) { + byte[] ipBytes = IpUtils.ipToBytes(ipFilter.trim()); + if (ipBytes != null) { + sql.append(" AND a.ip = ?"); + params.add(ipBytes); + paramIndex++; + } + } + + String orderColumn = switch (sortBy) { + case "balanceA" -> "b.balance_a"; + case "depositTotal" -> "b.deposit_total"; + case "withdrawTotal" -> "b.withdraw_total"; + case "roundsPlayed" -> "b.rounds_played"; + case "referralCount" -> "(d.referals_1 + d.referals_2 + d.referals_3 + d.referals_4 + d.referals_5)"; + case "profit" -> "(b.deposit_total - b.withdraw_total)"; + default -> "a.id"; + }; + String direction = "asc".equalsIgnoreCase(sortDir) ? " ASC" : " DESC"; + sql.append(" ORDER BY ").append(orderColumn).append(direction); + sql.append(" LIMIT ? OFFSET ?"); + params.add(limit); + params.add(offset); + + Query query = entityManager.createNativeQuery(sql.toString()); + for (int i = 0; i < params.size(); i++) { + query.setParameter(i + 1, params.get(i)); + } + List rows = query.getResultList(); + List ids = new ArrayList<>(); + for (Object row : rows) { + if (row instanceof Number n) { + ids.add(n.intValue()); + } + } + return ids; + } + + private Integer parseInteger(String str) { + try { + return Integer.parseInt(str); + } catch (NumberFormatException e) { + return null; + } + } + + private Long parseLong(String str) { + try { + return Long.parseLong(str); + } catch (NumberFormatException e) { + return null; + } + } + + public AdminUserDetailDto getUserDetail(Integer userId, boolean excludeMasters) { + if (excludeMasters) { + Optional userDOpt = userDRepository.findById(userId); + if (userDOpt.isPresent()) { + UserD d = userDOpt.get(); + if (d.getMasterId() > 0 && d.getId().equals(d.getMasterId())) { + return null; + } + } + } + Optional userAOpt = userARepository.findById(userId); + if (userAOpt.isEmpty()) { + return null; + } + + UserA userA = userAOpt.get(); + UserB userB = userBRepository.findById(userId).orElse( + UserB.builder() + .id(userId) + .balanceA(0L) + .balanceB(0L) + .depositTotal(0L) + .depositCount(0) + .withdrawTotal(0L) + .withdrawCount(0) + .roundsPlayed(0) + .totalWinAfterDeposit(0L) + .withdrawalsDisabled(false) + .build()); + + UserD userD = userDRepository.findById(userId).orElse( + UserD.builder() + .id(userId) + .referals1(0) + .referals2(0) + .referals3(0) + .referals4(0) + .referals5(0) + .fromReferals1(0L) + .fromReferals2(0L) + .fromReferals3(0L) + .fromReferals4(0L) + .fromReferals5(0L) + .toReferer1(0L) + .toReferer2(0L) + .toReferer3(0L) + .toReferer4(0L) + .toReferer5(0L) + .masterId(0) + .build()); + + int totalReferrals = userD.getReferals1() + userD.getReferals2() + + userD.getReferals3() + userD.getReferals4() + userD.getReferals5(); + long totalCommissions = userD.getFromReferals1() + userD.getFromReferals2() + + userD.getFromReferals3() + userD.getFromReferals4() + userD.getFromReferals5(); + + // Build referral levels + List referralLevels = new ArrayList<>(); + for (int level = 1; level <= 5; level++) { + int refererId = switch (level) { + case 1 -> userD.getRefererId1(); + case 2 -> userD.getRefererId2(); + case 3 -> userD.getRefererId3(); + case 4 -> userD.getRefererId4(); + case 5 -> userD.getRefererId5(); + default -> 0; + }; + int referralCount = switch (level) { + case 1 -> userD.getReferals1(); + case 2 -> userD.getReferals2(); + case 3 -> userD.getReferals3(); + case 4 -> userD.getReferals4(); + case 5 -> userD.getReferals5(); + default -> 0; + }; + long commissionsEarned = switch (level) { + case 1 -> userD.getFromReferals1(); + case 2 -> userD.getFromReferals2(); + case 3 -> userD.getFromReferals3(); + case 4 -> userD.getFromReferals4(); + case 5 -> userD.getFromReferals5(); + default -> 0L; + }; + long commissionsPaid = switch (level) { + case 1 -> userD.getToReferer1(); + case 2 -> userD.getToReferer2(); + case 3 -> userD.getToReferer3(); + case 4 -> userD.getToReferer4(); + case 5 -> userD.getToReferer5(); + default -> 0L; + }; + + referralLevels.add(ReferralLevelDto.builder() + .level(level) + .refererId(refererId > 0 ? refererId : null) + .referralCount(referralCount) + .commissionsEarned(commissionsEarned) + .commissionsPaid(commissionsPaid) + .commissionsEarnedUsd(ticketsToUsd(commissionsEarned)) + .commissionsPaidUsd(ticketsToUsd(commissionsPaid)) + .build()); + } + + BigDecimal depositTotalUsd = ticketsToUsd(userB.getDepositTotal()); + BigDecimal withdrawTotalUsd = ticketsToUsd(userB.getWithdrawTotal()); + BigDecimal totalCommissionsEarnedUsd = ticketsToUsd(totalCommissions); + + return AdminUserDetailDto.builder() + .id(userA.getId()) + .screenName(userA.getScreenName()) + .telegramId(userA.getTelegramId()) + .telegramName(userA.getTelegramName()) + .isPremium(userA.getIsPremium()) + .languageCode(userA.getLanguageCode()) + .countryCode(userA.getCountryCode()) + .deviceCode(userA.getDeviceCode()) + .avatarUrl(userA.getAvatarUrl()) + .dateReg(userA.getDateReg()) + .dateLogin(userA.getDateLogin()) + .banned(userA.getBanned()) + .ipAddress(IpUtils.bytesToIp(userA.getIp())) + .balanceA(userB.getBalanceA()) + .depositTotal(userB.getDepositTotal()) + .depositCount(userB.getDepositCount()) + .withdrawTotal(userB.getWithdrawTotal()) + .withdrawCount(userB.getWithdrawCount()) + .depositTotalUsd(depositTotalUsd) + .withdrawTotalUsd(withdrawTotalUsd) + .withdrawalsDisabled(Boolean.TRUE.equals(userB.getWithdrawalsDisabled())) + .roundsPlayed(userB.getRoundsPlayed()) + .referralCount(totalReferrals) + .totalCommissionsEarned(totalCommissions) + .totalCommissionsEarnedUsd(totalCommissionsEarnedUsd) + .masterId(userD.getMasterId() > 0 ? userD.getMasterId() : null) + .referralLevels(referralLevels) + .build(); + } + + private static BigDecimal ticketsToUsd(long ticketsBigint) { + if (ticketsBigint == 0) return BigDecimal.ZERO; + return BigDecimal.valueOf(ticketsBigint).divide(BigDecimal.valueOf(1_000_000L), 6, RoundingMode.HALF_UP).multiply(TICKETS_TO_USD).setScale(2, RoundingMode.HALF_UP); + } + + public Page getUserTransactions(Integer userId, Pageable pageable) { + Page transactions = transactionRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable); + return transactions.map(t -> AdminTransactionDto.builder() + .id(t.getId()) + .amount(t.getAmount()) + .type(t.getType().name()) + .taskId(t.getTaskId()) + .roundId(t.getRoundId()) + .createdAt(t.getCreatedAt()) + .build()); + } + + /** Deposits (payments) for user detail. */ + public Page getUserPayments(Integer userId, Pageable pageable) { + Page page = paymentRepository.findByUserId(userId, pageable); + return page.map(p -> UserDepositDto.builder() + .id(p.getId()) + .usdAmount(p.getUsdAmount()) + .status(p.getStatus().name()) + .orderId(p.getOrderId()) + .createdAt(p.getCreatedAt()) + .completedAt(p.getCompletedAt()) + .build()); + } + + /** Withdrawals (CRYPTO payouts only) for user detail. */ + public Page getUserPayouts(Integer userId, Pageable pageable) { + Page page = payoutRepository.findByUserIdAndType(userId, Payout.PayoutType.CRYPTO, pageable); + return page.map(p -> UserWithdrawalDto.builder() + .id(p.getId()) + .usdAmount(p.getUsdAmount()) + .cryptoName(p.getCryptoName()) + .amountToSend(p.getAmountToSend()) + .txhash(p.getTxhash()) + .status(p.getStatus().name()) + .paymentId(p.getPaymentId()) + .createdAt(p.getCreatedAt()) + .resolvedAt(p.getResolvedAt()) + .build()); + } + + /** + * Game history from transactions (BET/WIN). Participants table is cleaned after each round. + */ + public Page getUserGameRounds(Integer userId, Pageable pageable) { + Page betPage = transactionRepository.findByUserIdAndTypeOrderByCreatedAtDesc(userId, Transaction.TransactionType.BET, pageable); + List bets = betPage.getContent(); + if (bets.isEmpty()) { + return new PageImpl<>(List.of(), pageable, 0); + } + Set roundIds = bets.stream().map(Transaction::getRoundId).filter(java.util.Objects::nonNull).collect(Collectors.toSet()); + List wins = roundIds.isEmpty() ? List.of() : transactionRepository.findByUserIdAndTypeWinAndRoundIdIn(userId, roundIds); + Map payoutByRound = wins.stream().collect(Collectors.toMap(Transaction::getRoundId, t -> t.getAmount() != null ? t.getAmount() : 0L, (a, b) -> a)); + Map resolvedAtByRound = wins.stream().collect(Collectors.toMap(Transaction::getRoundId, t -> t.getCreatedAt() != null ? t.getCreatedAt() : Instant.EPOCH, (a, b) -> a)); + + Map roundById = roundIds.isEmpty() ? Map.of() : gameRoundRepository.findAllByIdWithRoom(roundIds).stream() + .collect(Collectors.toMap(GameRound::getId, r -> r, (a, b) -> a)); + + List rounds = bets.stream() + .map(bet -> { + Long roundId = bet.getRoundId(); + GameRound gr = roundById.get(roundId); + Integer roomNumber = gr != null && gr.getRoom() != null ? gr.getRoom().getRoomNumber() : null; + Long totalBet = gr != null ? gr.getTotalBet() : null; + long userBet = bet.getAmount() != null ? Math.abs(bet.getAmount()) : 0L; + Long payout = payoutByRound.getOrDefault(roundId, 0L); + boolean isWinner = payout > 0; + Instant resolvedAt = resolvedAtByRound.getOrDefault(roundId, bet.getCreatedAt()); + return AdminGameRoundDto.builder() + .roundId(roundId) + .roomNumber(roomNumber) + .phase(gr != null && gr.getPhase() != null ? gr.getPhase().name() : null) + .totalBet(totalBet) + .userBet(userBet) + .winnerUserId(isWinner ? userId : null) + .winnerBet(isWinner ? userBet : null) + .payout(isWinner ? payout : 0L) + .commission(null) + .startedAt(null) + .resolvedAt(resolvedAt) + .isWinner(isWinner) + .build(); + }) + .collect(Collectors.toList()); + + return new PageImpl<>(rounds, pageable, betPage.getTotalElements()); + } + + public Map getUserTasks(Integer userId) { + List claims = userTaskClaimRepository.findByUserId(userId); + List allTasks = taskRepository.findAll(); + + Map taskMap = allTasks.stream() + .collect(Collectors.toMap(Task::getId, t -> t)); + + List completedTasks = claims.stream() + .map(claim -> { + Task task = taskMap.get(claim.getTaskId()); + // Convert LocalDateTime to Instant (assuming database stores in UTC) + Instant claimedAtInstant = claim.getClaimedAt() != null + ? claim.getClaimedAt().atZone(ZoneId.of("UTC")).toInstant() + : null; + return AdminTaskClaimDto.builder() + .id(claim.getId()) + .taskId(claim.getTaskId()) + .taskTitle(task != null ? task.getTitle() : "Unknown Task") + .taskType(task != null ? task.getType() : "unknown") + .claimedAt(claimedAtInstant) + .build(); + }) + .collect(Collectors.toList()); + + List> availableTasks = allTasks.stream() + .filter(task -> !claims.stream().anyMatch(c -> c.getTaskId().equals(task.getId()))) + .map(task -> Map.of( + "id", task.getId(), + "title", task.getTitle(), + "type", task.getType(), + "requirement", task.getRequirement(), + "rewardAmount", task.getRewardAmount(), + "rewardType", task.getRewardType(), + "description", task.getDescription() != null ? task.getDescription() : "" + )) + .collect(Collectors.toList()); + + // Get daily bonus claims + List dailyBonusClaims = userDailyBonusClaimRepository.findByUserIdOrderByClaimedAtDesc(userId); + List> dailyBonuses = dailyBonusClaims.stream() + .map(claim -> { + Instant claimedAtInstant = claim.getClaimedAt() != null + ? claim.getClaimedAt().atZone(ZoneId.of("UTC")).toInstant() + : null; + return Map.of( + "id", claim.getId(), + "claimedAt", claimedAtInstant != null ? claimedAtInstant.toEpochMilli() : null, + "screenName", claim.getScreenName() != null ? claim.getScreenName() : "-" + ); + }) + .collect(Collectors.toList()); + + return Map.of( + "completed", completedTasks, + "available", availableTasks, + "dailyBonuses", dailyBonuses + ); + } + + @Transactional + public void setBanned(Integer userId, boolean banned) { + UserA user = userARepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("User not found with ID: " + userId)); + user.setBanned(banned ? 1 : 0); + userARepository.save(user); + } + + @Transactional + public void setWithdrawalsEnabled(Integer userId, boolean enabled) { + UserB userB = userBRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("User not found with ID: " + userId)); + userB.setWithdrawalsDisabled(!enabled); + userBRepository.save(userB); + } + + @Transactional + public BalanceAdjustmentResponse adjustBalance(Integer userId, BalanceAdjustmentRequest request) { + // Verify user exists + Optional userAOpt = userARepository.findById(userId); + if (userAOpt.isEmpty()) { + throw new IllegalArgumentException("User not found with ID: " + userId); + } + + // Get or create UserB + UserB userB = userBRepository.findById(userId).orElse( + UserB.builder() + .id(userId) + .balanceA(0L) + .balanceB(0L) + .depositTotal(0L) + .depositCount(0) + .withdrawTotal(0L) + .withdrawCount(0) + .roundsPlayed(0) + .build()); + + // Store previous balances + Long previousBalanceA = userB.getBalanceA(); + Long previousBalanceB = userB.getBalanceB(); + + // Calculate new balance + Long adjustmentAmount = request.getAmount(); + if (request.getOperation() == BalanceAdjustmentRequest.OperationType.SUBTRACT) { + adjustmentAmount = -adjustmentAmount; + } + + Long newBalanceA = userB.getBalanceA(); + Long newBalanceB = userB.getBalanceB(); + + if (request.getBalanceType() == BalanceAdjustmentRequest.BalanceType.A) { + newBalanceA = userB.getBalanceA() + adjustmentAmount; + if (newBalanceA < 0) { + throw new IllegalArgumentException("Balance A cannot be negative. Current: " + + (userB.getBalanceA() / 1_000_000.0) + ", Attempted adjustment: " + + (adjustmentAmount / 1_000_000.0)); + } + userB.setBalanceA(newBalanceA); + } else { + newBalanceB = userB.getBalanceB() + adjustmentAmount; + if (newBalanceB < 0) { + throw new IllegalArgumentException("Balance B cannot be negative. Current: " + + (userB.getBalanceB() / 1_000_000.0) + ", Attempted adjustment: " + + (adjustmentAmount / 1_000_000.0)); + } + userB.setBalanceB(newBalanceB); + } + + // Save updated balance + userBRepository.save(userB); + + // Create transaction record for audit + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(adjustmentAmount) + .type(adjustmentAmount > 0 ? Transaction.TransactionType.DEPOSIT : Transaction.TransactionType.WITHDRAWAL) + .createdAt(java.time.Instant.now()) + .build(); + transactionRepository.save(transaction); + + return BalanceAdjustmentResponse.builder() + .newBalanceA(userB.getBalanceA()) + .newBalanceB(userB.getBalanceB()) + .previousBalanceA(previousBalanceA) + .previousBalanceB(previousBalanceB) + .adjustmentAmount(adjustmentAmount) + .message("Balance adjusted successfully") + .build(); + } +} + diff --git a/src/main/java/com/lottery/lottery/service/AvatarService.java b/src/main/java/com/lottery/lottery/service/AvatarService.java new file mode 100644 index 0000000..d02cbbd --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/AvatarService.java @@ -0,0 +1,693 @@ +package com.lottery.lottery.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.zip.Adler32; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.lottery.lottery.config.TelegramProperties; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.UserARepository; + +/** + * Service for handling user avatar download, processing, and storage. + * Stores avatar URL and last_telegram_file_id in database to avoid unnecessary downloads. + * URLs include ?v=timestamp parameter for cache busting (forces refresh within 1h session lifetime). + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AvatarService { + + private final TelegramProperties telegramProperties; + private final ObjectMapper objectMapper = new ObjectMapper(); + private final UserARepository userARepository; + + @Value("${app.avatar.storage-path:./data/avatars}") + private String storagePath; + + // Normalized absolute storage path (computed once on initialization) + private Path normalizedStoragePath; + + @Value("${app.avatar.public-base-url:}") + private String publicBaseUrl; + + @Value("${app.avatar.max-size-bytes:2097152}") // 2MB default + private long maxSizeBytes; + + @Value("${app.avatar.max-dimension:110}") // 110x110 default + private int maxDimension; + + @Value("${app.avatar.cache-ttl-minutes:5}") // 5 minutes default + private int cacheTtlMinutes; + + private final HttpClient httpClient = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); + + // In-memory cache for avatar URLs (userId -> CacheEntry with URL and timestamp) + private final Map avatarUrlCache = new ConcurrentHashMap<>(); + + // Store last downloaded content type for avatar type detection + private volatile String lastDownloadedContentType = null; + + /** + * Cache entry with timestamp for TTL-based expiration. + */ + private static class CacheEntry { + final String avatarUrl; + final long timestamp; + + CacheEntry(String avatarUrl) { + this.avatarUrl = avatarUrl; + this.timestamp = System.currentTimeMillis(); + } + + boolean isExpired(int ttlMinutes) { + return (System.currentTimeMillis() - timestamp) > (ttlMinutes * 60 * 1000L); + } + } + + /** + * Initializes the normalized storage path after dependency injection. + */ + @PostConstruct + public void init() { + normalizedStoragePath = Paths.get(storagePath).toAbsolutePath().normalize(); + log.debug("Avatar storage path initialized: {}", normalizedStoragePath); + } + + /** + * Updates user avatar if the Telegram file_id has changed. + * Only downloads and processes avatar if last_telegram_file_id differs from current file_id. + * Stores avatar URL with ?v=timestamp parameter in database for cache busting. + * + * @param user UserA entity (will be updated with new avatar_url and last_telegram_file_id) + * @param telegramUserId Telegram user ID (from initData.user.id) + * @param tgUser Telegram user data map (from initData) - used for fallback to photo_url + * @return true if avatar was updated, false if no update needed or failed + */ + public boolean updateAvatarIfNeeded(UserA user, Long telegramUserId, Map tgUser) { + try { + String currentFileId = null; + byte[] imageData = null; + boolean isSvg = false; + + // Step 1: Try to get user profile photos from Telegram Bot API + String fileId = getProfilePhotoFileId(telegramUserId); + if (fileId != null && !fileId.isEmpty()) { + currentFileId = fileId; + + // Check if file_id changed (skip download if unchanged) + if (fileId.equals(user.getLastTelegramFileId())) { + return false; + } + + // Step 2: Get file path from Telegram + String telegramFilePath = getTelegramFilePath(fileId); + if (telegramFilePath != null && !telegramFilePath.isEmpty()) { + // Step 3: Download the actual image from Telegram + imageData = downloadTelegramFile(telegramFilePath); + } + } + + // Fallback: If Bot API failed (user restricted access or no photos), try photo_url from initData + if (imageData == null || imageData.length == 0) { + String photoUrl = extractAvatarUrl(tgUser); + if (photoUrl != null && !photoUrl.isEmpty()) { + imageData = downloadAvatar(photoUrl); + String contentTypeFromDownload = getLastDownloadedContentType(); + if (imageData != null && imageData.length > 0) { + // Check if it's an SVG - use Content-Type header first, then URL extension, then content check + String lowerUrl = photoUrl.toLowerCase(); + boolean urlSuggestsSvg = lowerUrl.endsWith(".svg"); + boolean contentTypeSuggestsSvg = contentTypeFromDownload != null && + (contentTypeFromDownload.toLowerCase().contains("svg") || contentTypeFromDownload.toLowerCase().contains("xml")); + boolean contentSuggestsSvg = imageData.length > 4 && + new String(imageData, 0, Math.min(100, imageData.length), java.nio.charset.StandardCharsets.UTF_8).trim().startsWith(" maxSizeBytes) { + log.warn("Avatar too large for userId={}: {} bytes (max: {})", user.getId(), imageData.length, maxSizeBytes); + return false; + } + + // Determine file extension: .svg for fallback avatars, .png for Bot API avatars + String fileExtension = isSvg ? "svg" : "png"; + + // Calculate path components once (includes hash calculation) - reused for both file path and URL + PathComponents pathComponents = calculatePathComponents(user.getId(), fileExtension); + + // Calculate file path using sharding with appropriate extension + Path filePath = calculateFilePath(pathComponents); + + // Create directories if they don't exist + Path parentDir = filePath.getParent(); + try { + Files.createDirectories(parentDir); + } catch (Exception e) { + log.error("Failed to create parent directory for userId={}, parentDir={}: {}", + user.getId(), parentDir, e.getMessage(), e); + return false; + } + + // Save file: SVG directly, PNG after processing + if (isSvg) { + // Save SVG directly + try { + Files.write(filePath, imageData); + } catch (Exception e) { + log.error("Exception while writing SVG file for userId={}, filePath={}: {}", + user.getId(), filePath, e.getMessage(), e); + return false; + } + } else { + // Process and save PNG (resize if needed) + BufferedImage image = processImage(imageData); + if (image == null) { + log.warn("Failed to process image for userId={}, imageData size={} bytes", user.getId(), imageData.length); + return false; + } + + File outputFile = filePath.toFile(); + boolean written = ImageIO.write(image, "png", outputFile); + if (!written) { + log.warn("ImageIO.write() returned false for userId={}, filePath={}", user.getId(), filePath); + } + } + + // Build public URL with ?v=timestamp parameter for cache busting (reuses pathComponents) + String avatarUrl = buildPublicUrlWithTimestamp(pathComponents); + + // Update user entity with new avatar URL and file_id + user.setAvatarUrl(avatarUrl); + user.setLastTelegramFileId(currentFileId); + userARepository.save(user); + + // Invalidate cache when avatar is updated + invalidateCache(user.getId()); + + return true; + + } catch (Exception e) { + log.error("Error updating avatar for userId={}: {}", user.getId(), e.getMessage(), e); + return false; + } + } + + /** + * Gets the public URL for a user's avatar from database. + * Returns null if user has no avatar URL stored. + * + * @param userId User ID + * @return Avatar URL from database, or null if not found + */ + public String getAvatarUrl(Integer userId) { + if (userId == null) { + return null; + } + + return userARepository.findById(userId) + .map(UserA::getAvatarUrl) + .orElse(null); + } + + /** + * Gets avatar URLs for multiple users in a single database query (optimized for bulk operations). + * Returns a map of userId -> avatarUrl. Users without avatars will have null values in the map. + * Uses cache to avoid database queries for recently fetched avatars. + * + * @param userIds List of user IDs + * @return Map of userId to avatarUrl + */ + public Map getAvatarUrls(List userIds) { + if (userIds == null || userIds.isEmpty()) { + return Collections.emptyMap(); + } + + Map avatarUrlMap = new HashMap<>(); + List uncachedUserIds = new ArrayList<>(); + + // Check cache first + for (Integer userId : userIds) { + CacheEntry cached = avatarUrlCache.get(userId); + if (cached != null && !cached.isExpired(cacheTtlMinutes)) { + avatarUrlMap.put(userId, cached.avatarUrl); + // Cache hit + } else { + uncachedUserIds.add(userId); + } + } + + // Fetch uncached users from database in one query + if (!uncachedUserIds.isEmpty()) { + List users = userARepository.findAllById(uncachedUserIds); + + for (UserA user : users) { + String avatarUrl = user.getAvatarUrl(); + avatarUrlMap.put(user.getId(), avatarUrl); + + // Update cache + if (avatarUrl != null) { + avatarUrlCache.put(user.getId(), new CacheEntry(avatarUrl)); + } else { + // Cache null for a shorter time (1 minute) + avatarUrlCache.put(user.getId(), new CacheEntry(null)); + } + } + + // Ensure all requested userIds are in the map (even if null) + for (Integer userId : uncachedUserIds) { + avatarUrlMap.putIfAbsent(userId, null); + } + + // Bulk fetch completed + } + + return avatarUrlMap; + } + + /** + * Invalidates cache entry for a user (called when avatar is updated). + * + * @param userId User ID + */ + public void invalidateCache(Integer userId) { + if (userId != null) { + avatarUrlCache.remove(userId); + // Cache invalidated + } + } + + /** + * Gets user profile photos from Telegram Bot API and returns the file_id of the largest photo. + * + * @param telegramUserId Telegram user ID + * @return file_id of the largest profile photo, or null if no photos found + */ + private String getProfilePhotoFileId(Long telegramUserId) { + try { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured for avatar fetching"); + return null; + } + + String apiUrl = String.format("https://api.telegram.org/bot%s/getUserProfilePhotos?user_id=%d&limit=1", + botToken, telegramUserId); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(apiUrl)) + .GET() + .timeout(java.time.Duration.ofSeconds(10)) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != 200) { + log.warn("Failed to get profile photos from Telegram: HTTP {}", response.statusCode()); + return null; + } + + // Parse JSON response + JsonNode jsonResponse = objectMapper.readTree(response.body()); + + if (!jsonResponse.get("ok").asBoolean()) { + String description = jsonResponse.has("description") ? jsonResponse.get("description").asText() : "Unknown error"; + log.warn("Telegram API returned error when getting profile photos: {}, telegramUserId={}", + description, telegramUserId); + return null; + } + + JsonNode result = jsonResponse.get("result"); + int totalCount = result.get("total_count").asInt(); + + if (totalCount == 0) { + return null; + } + + // Get the first photo (photos[0]) + JsonNode photos = result.get("photos"); + if (!photos.isArray() || photos.size() == 0) { + log.warn("Photos array is empty or invalid for telegramUserId={}", telegramUserId); + return null; + } + + JsonNode firstPhoto = photos.get(0); + if (!firstPhoto.isArray() || firstPhoto.size() == 0) { + log.warn("First photo array is empty or invalid for telegramUserId={}", telegramUserId); + return null; + } + + // Get the last element (largest resolution) + JsonNode largestPhoto = firstPhoto.get(firstPhoto.size() - 1); + String fileId = largestPhoto.get("file_id").asText(); + + return fileId; + + } catch (Exception e) { + log.error("Error fetching profile photos for telegramUserId={}: {}", telegramUserId, e.getMessage(), e); + return null; + } + } + + /** + * Gets file path from Telegram using file_id. + * + * @param fileId Telegram file_id + * @return file_path from Telegram, or null if failed + */ + private String getTelegramFilePath(String fileId) { + try { + String botToken = telegramProperties.getBotToken(); + String apiUrl = String.format("https://api.telegram.org/bot%s/getFile?file_id=%s", + botToken, fileId); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(apiUrl)) + .GET() + .timeout(java.time.Duration.ofSeconds(10)) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != 200) { + log.warn("Failed to get file path from Telegram: HTTP {}", response.statusCode()); + return null; + } + + // Parse JSON response + JsonNode jsonResponse = objectMapper.readTree(response.body()); + + if (!jsonResponse.get("ok").asBoolean()) { + String description = jsonResponse.has("description") ? jsonResponse.get("description").asText() : "Unknown error"; + log.warn("Telegram API returned error when getting file path: {}", description); + return null; + } + + String filePath = jsonResponse.get("result").get("file_path").asText(); + + return filePath; + + } catch (Exception e) { + log.error("Error getting file path for fileId={}: {}", fileId, e.getMessage(), e); + return null; + } + } + + /** + * Downloads file from Telegram file server. + * + * @param filePath Telegram file path (e.g., "photos/file_123.jpg") + * @return File content as bytes, or null if failed + */ + private byte[] downloadTelegramFile(String filePath) { + try { + String botToken = telegramProperties.getBotToken(); + String downloadUrl = String.format("https://api.telegram.org/file/bot%s/%s", + botToken, filePath); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(downloadUrl)) + .GET() + .timeout(java.time.Duration.ofSeconds(30)) // Longer timeout for file downloads + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); + + if (response.statusCode() == 200) { + return response.body(); + } else { + log.warn("Failed to download file from Telegram: HTTP {}", response.statusCode()); + return null; + } + } catch (Exception e) { + log.error("Error downloading file from Telegram: {}", e.getMessage(), e); + return null; + } + } + + /** + * Extracts avatar URL from Telegram user data (fallback method). + * Telegram provides photo_url in the user object (usually SVG with initials). + */ + private String extractAvatarUrl(Map tgUser) { + if (tgUser == null) { + return null; + } + Object photoUrl = tgUser.get("photo_url"); + if (photoUrl != null) { + return photoUrl.toString(); + } + return null; + } + + /** + * Downloads avatar image from URL (fallback method for photo_url). + */ + private byte[] downloadAvatar(String url) { + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .GET() + .timeout(java.time.Duration.ofSeconds(10)) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); + + // Store Content-Type for later use + String contentType = response.headers().firstValue("Content-Type").orElse(""); + lastDownloadedContentType = contentType; + + if (response.statusCode() == 200) { + return response.body(); + } else { + log.warn("Failed to download avatar from URL: HTTP {}, url={}", response.statusCode(), url); + lastDownloadedContentType = null; + return null; + } + } catch (Exception e) { + log.error("Error downloading avatar from {}: {}", url, e.getMessage(), e); + lastDownloadedContentType = null; + return null; + } + } + + /** + * Gets the Content-Type of the last downloaded avatar (used for type detection). + * @return Content-Type header value, or null if not available + */ + private String getLastDownloadedContentType() { + return lastDownloadedContentType; + } + + /** + * Processes image: validates format and resizes if needed. + * Only used for PNG images from Telegram Bot API. + */ + private BufferedImage processImage(byte[] imageData) { + try { + // Read image from bytes + BufferedImage image = ImageIO.read(new java.io.ByteArrayInputStream(imageData)); + if (image == null) { + log.warn("Failed to read image data - unsupported format (might be SVG or corrupted file)"); + return null; + } + + // Resize if image is too large + int width = image.getWidth(); + int height = image.getHeight(); + + if (width > maxDimension || height > maxDimension) { + // Calculate new dimensions maintaining aspect ratio + double scale = Math.min((double) maxDimension / width, (double) maxDimension / height); + int newWidth = (int) (width * scale); + int newHeight = (int) (height * scale); + + log.debug("Resizing image from {}x{} to {}x{}", width, height, newWidth, newHeight); + + // Use high-quality scaling + BufferedImage resized = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = resized.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.drawImage(image, 0, 0, newWidth, newHeight, null); + g.dispose(); + + return resized; + } + + // Convert to ARGB if needed (for PNG support) + if (image.getType() != BufferedImage.TYPE_INT_ARGB) { + BufferedImage converted = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D g = converted.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return converted; + } + + return image; + + } catch (IOException e) { + log.error("Error processing image: {}", e.getMessage(), e); + return null; + } + } + + /** + * Path components for avatar file storage. + * Contains all calculated values needed for both file path and URL generation. + */ + private static class PathComponents { + final String digit2; + final String digit1; + final String digit0; + final String hash; + final String filename; + + PathComponents(String digit2, String digit1, String digit0, String hash, String filename) { + this.digit2 = digit2; + this.digit1 = digit1; + this.digit0 = digit0; + this.hash = hash; + this.filename = filename; + } + } + + /** + * Calculates path components (folder digits, hash, filename) for a user ID. + * This is done once and reused for both file path and URL generation. + * + * @param userId User ID + * @param extension File extension ("png" or "svg") + * @return PathComponents with all calculated values + */ + private PathComponents calculatePathComponents(Integer userId, String extension) { + // Zero-pad user ID to 8 digits + String paddedId = String.format("%08d", userId); + + // Extract last 3 digits for folder hierarchy + String digit2 = String.valueOf(paddedId.charAt(5)); // 6th digit (0-indexed) + String digit1 = String.valueOf(paddedId.charAt(6)); // 7th digit + String digit0 = String.valueOf(paddedId.charAt(7)); // 8th digit (last) + + // Hash the entire 8-digit padded ID for filename + String hash = hashString(paddedId); + String filename = hash + "." + extension; + + return new PathComponents(digit2, digit1, digit0, hash, filename); + } + + /** + * Calculates file path using sharding logic from pre-calculated path components. + * + * @param components Pre-calculated path components + * @return File path for storing the avatar + */ + private Path calculateFilePath(PathComponents components) { + // Build path using normalized storage path + return normalizedStoragePath + .resolve(components.digit2) + .resolve(components.digit1) + .resolve(components.digit0) + .resolve(components.filename); + } + + /** + * Hashes a string to an 8-character hex string using Adler32. + * Adler32 is a fast checksum algorithm that produces a 32-bit hash value. + * + * @param input Input string to hash + * @return 8-character hexadecimal hash string + */ + private String hashString(String input) { + if (input == null || input.isEmpty()) { + return "00000000"; + } + + Adler32 adler32 = new Adler32(); + byte[] bytes = input.getBytes(java.nio.charset.StandardCharsets.UTF_8); + adler32.update(bytes); + + long hashValue = adler32.getValue(); + return String.format("%08x", hashValue); + } + + /** + * Builds public URL for the avatar with ?v=timestamp parameter for cache busting. + * Format: {publicBaseUrl}/avatars/{digit2}/{digit1}/{digit0}/{hash}.{ext}?v={timestamp} + * If publicBaseUrl is empty, returns relative URL starting with /avatars + * + * Uses pre-calculated path components to avoid duplicate hash calculation. + * + * @param components Pre-calculated path components + * @return Public URL with timestamp parameter + */ + private String buildPublicUrlWithTimestamp(PathComponents components) { + // Build relative path + String relativePath = String.format("/avatars/%s/%s/%s/%s", + components.digit2, components.digit1, components.digit0, components.filename); + + // Add timestamp parameter for cache busting (forces refresh within 1h session lifetime) + long timestamp = System.currentTimeMillis() / 1000; // Unix timestamp in seconds + String pathWithTimestamp = relativePath + "?v=" + timestamp; + + // If publicBaseUrl is set, prepend it; otherwise return relative URL + if (publicBaseUrl != null && !publicBaseUrl.isEmpty()) { + String baseUrl = publicBaseUrl.endsWith("/") + ? publicBaseUrl.substring(0, publicBaseUrl.length() - 1) + : publicBaseUrl; + return baseUrl + pathWithTimestamp; + } + + // Return relative URL (browser will resolve it relative to current origin) + return pathWithTimestamp; + } +} diff --git a/src/main/java/com/lottery/lottery/service/BetDecisionService.java b/src/main/java/com/lottery/lottery/service/BetDecisionService.java new file mode 100644 index 0000000..29797d8 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/BetDecisionService.java @@ -0,0 +1,17 @@ +package com.lottery.lottery.service; + +/** + * Decides bet amount (in tickets) for a lottery bot. + * Implemented in-process by persona + loss-streak and zone logic (no external API). + */ +public interface BetDecisionService { + + /** + * Returns bet amount in tickets (1 ticket = 1_000_000 in DB bigint). + * Must be within room min/max; caller will clamp if needed. + * + * @param context room, round, config, etc. (for future AI) + * @return bet amount in tickets (e.g. 1000) + */ + long decideBetAmountTickets(BotBetContext context); +} diff --git a/src/main/java/com/lottery/lottery/service/BotBetContext.java b/src/main/java/com/lottery/lottery/service/BotBetContext.java new file mode 100644 index 0000000..1728ede --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/BotBetContext.java @@ -0,0 +1,33 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.LotteryBotConfig; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +/** + * Context passed to bet decision (ChatGPT). Bot range and history are required for the prompt. + */ +@Data +@Builder +public class BotBetContext { + + private int roomNumber; + private Long roundId; + private int participantCount; + private LotteryBotConfig config; + + /** Room min/max bet in tickets. */ + private long roomMinTickets; + private long roomMaxTickets; + /** Bot config min/max bet in tickets (hard bounds for output). */ + private long botMinTickets; + private long botMaxTickets; + /** Current round total bet in tickets (pot so far). */ + private long currentRoundTotalBetTickets; + /** Last 10 bet amounts in tickets (oldest → newest). Padded with 0 if fewer than 10. */ + private List lastBets10; + /** Last 10 results: W=win, L=loss, N=no data (oldest → newest). Padded with N if fewer than 10. */ + private List lastResults10; +} diff --git a/src/main/java/com/lottery/lottery/service/BotBetHistoryService.java b/src/main/java/com/lottery/lottery/service/BotBetHistoryService.java new file mode 100644 index 0000000..f734a75 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/BotBetHistoryService.java @@ -0,0 +1,83 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.Transaction; +import com.lottery.lottery.repository.TransactionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Loads last N bet amounts and win/loss results for a user from transactions (BET + WIN). + * Used to build ChatGPT prompt context. + */ +@Service +@RequiredArgsConstructor +public class BotBetHistoryService { + + private static final long TICKETS_TO_BIGINT = 1_000_000L; + private static final String RESULT_WIN = "W"; + private static final String RESULT_LOSS = "L"; + private static final String RESULT_NONE = "N"; + + private final TransactionRepository transactionRepository; + + /** + * Returns last {@code count} bet amounts (in tickets) and results (W/L/N), oldest first. + * If fewer than count bets exist, pads with 0 and "N". + */ + @Transactional(readOnly = true) + public BetHistoryResult getLastBetsAndResults(int userId, int count) { + if (count <= 0) { + return new BetHistoryResult(Collections.nCopies(count, 0), Collections.nCopies(count, RESULT_NONE)); + } + List betTxs = transactionRepository + .findByUserIdAndTypeOrderByCreatedAtDesc(userId, Transaction.TransactionType.BET, PageRequest.of(0, count)) + .getContent(); + if (betTxs.isEmpty()) { + return new BetHistoryResult( + Collections.nCopies(count, 0), + Collections.nCopies(count, RESULT_NONE)); + } + // Newest first → reverse to oldest first + List oldestFirst = new ArrayList<>(betTxs); + Collections.reverse(oldestFirst); + + List bets = new ArrayList<>(); + List roundIds = new ArrayList<>(); + for (Transaction t : oldestFirst) { + // BET amounts are stored as negative (debit); use abs for ticket count in prompt + long amount = t.getAmount() != null ? Math.abs(t.getAmount()) : 0L; + long tickets = amount / TICKETS_TO_BIGINT; + bets.add((int) Math.max(0, Math.min(Integer.MAX_VALUE, tickets))); + roundIds.add(t.getRoundId()); + } + + Set roundIdsToCheck = roundIds.stream().filter(id -> id != null).collect(Collectors.toSet()); + Set roundIdsWithWin = Set.of(); + if (!roundIdsToCheck.isEmpty()) { + List winTxs = transactionRepository.findByUserIdAndTypeWinAndRoundIdIn(userId, roundIdsToCheck); + roundIdsWithWin = winTxs.stream().map(Transaction::getRoundId).filter(id -> id != null).collect(Collectors.toSet()); + } + + List results = new ArrayList<>(); + for (Long roundId : roundIds) { + results.add(roundId != null && roundIdsWithWin.contains(roundId) ? RESULT_WIN : RESULT_LOSS); + } + + // Left-pad to count so format is [oldest …, newest] + while (bets.size() < count) { + bets.add(0, 0); + results.add(0, RESULT_NONE); + } + return new BetHistoryResult(bets, results); + } + + public record BetHistoryResult(List lastBets, List lastResults) {} +} diff --git a/src/main/java/com/lottery/lottery/service/BotConfigService.java b/src/main/java/com/lottery/lottery/service/BotConfigService.java new file mode 100644 index 0000000..194054c --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/BotConfigService.java @@ -0,0 +1,145 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.FlexibleBotConfig; +import com.lottery.lottery.model.GameRoundParticipant; +import com.lottery.lottery.model.SafeBotUser; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.repository.FlexibleBotConfigRepository; +import com.lottery.lottery.repository.SafeBotUserRepository; +import com.lottery.lottery.repository.UserBRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Bot configuration and winner-override logic for safe/flexible bots. + * Does not affect displayed chances or tape; only who is selected as winner at resolution. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BotConfigService { + + /** Balance below this (bigint: 1 ticket = 1_000_000) → safe bot gets 100% win. 20_000 tickets = 20$ */ + private static final long SAFE_BOT_BALANCE_THRESHOLD = 20_000L * 1_000_000L; // 20_000_000_000 + + private final SafeBotUserRepository safeBotUserRepository; + private final FlexibleBotConfigRepository flexibleBotConfigRepository; + private final UserBRepository userBRepository; + + /** + * If a bot override applies, returns the participant to use as winner; otherwise empty (use normal weighted random). + * Order: 1) Safe bot with balance < threshold wins. 2) Flexible bot with configured win rate. 3) Normal. + */ + @Transactional(readOnly = true) + public Optional resolveWinnerOverride( + List participants, + long totalBet + ) { + if (participants == null || participants.isEmpty()) return Optional.empty(); + + Set safeBotUserIds = getSafeBotUserIds(); + Map flexibleWinRates = getFlexibleBotWinRates(); + + // 1) Safe bot: any safe bot in round with balance < threshold wins (pick one randomly if multiple) + List safeBotsInRound = participants.stream() + .filter(p -> safeBotUserIds.contains(p.getUserId())) + .toList(); + if (!safeBotsInRound.isEmpty()) { + List lowBalanceSafeBots = new ArrayList<>(); + for (GameRoundParticipant p : safeBotsInRound) { + UserB userB = userBRepository.findById(p.getUserId()).orElse(null); + if (userB != null && userB.getBalanceA() != null && userB.getBalanceA() < SAFE_BOT_BALANCE_THRESHOLD) { + lowBalanceSafeBots.add(p); + } + } + if (!lowBalanceSafeBots.isEmpty()) { + GameRoundParticipant chosen = lowBalanceSafeBots.get(new Random().nextInt(lowBalanceSafeBots.size())); + log.debug("Safe bot winner override: userId={}, balance below threshold", chosen.getUserId()); + return Optional.of(chosen); + } + } + + // 2) Flexible bot: with probability win_rate that bot wins; remaining probability = normal weighted random + List flexBotsInRound = participants.stream() + .filter(p -> flexibleWinRates.containsKey(p.getUserId())) + .toList(); + if (flexBotsInRound.isEmpty()) return Optional.empty(); + + double roll = new Random().nextDouble(); + double cumulative = 0; + for (GameRoundParticipant p : flexBotsInRound) { + double rate = flexibleWinRates.get(p.getUserId()); + cumulative += rate; + if (roll < cumulative) { + log.debug("Flexible bot winner override: userId={}, winRate={}", p.getUserId(), rate); + return Optional.of(p); + } + } + // roll >= cumulative: fall through to normal (don't return empty here - we already have normal logic in caller) + return Optional.empty(); + } + + public Set getSafeBotUserIds() { + return safeBotUserRepository.findAllByOrderByUserIdAsc().stream() + .map(SafeBotUser::getUserId) + .collect(Collectors.toSet()); + } + + public Map getFlexibleBotWinRates() { + Map map = new HashMap<>(); + for (FlexibleBotConfig c : flexibleBotConfigRepository.findAllByOrderByUserIdAsc()) { + if (c.getWinRate() != null) { + map.put(c.getUserId(), c.getWinRate().doubleValue()); + } + } + return map; + } + + @Transactional(readOnly = true) + public BotConfigDto getConfig() { + List safeBotUserIds = safeBotUserRepository.findAllByOrderByUserIdAsc().stream() + .map(SafeBotUser::getUserId) + .toList(); + List flexibleBots = flexibleBotConfigRepository.findAllByOrderByUserIdAsc().stream() + .map(c -> new FlexibleBotEntryDto(c.getUserId(), c.getWinRate() != null ? c.getWinRate().doubleValue() : 0)) + .toList(); + return new BotConfigDto(safeBotUserIds, flexibleBots); + } + + @Transactional + public void setSafeBotUserIds(List userIds) { + safeBotUserRepository.deleteAll(); + if (userIds != null) { + for (Integer id : userIds) { + if (id != null) safeBotUserRepository.save(SafeBotUser.builder().userId(id).build()); + } + } + } + + @Transactional + public void setFlexibleBots(List entries) { + flexibleBotConfigRepository.deleteAll(); + if (entries != null) { + for (FlexibleBotEntryDto e : entries) { + if (e != null && e.userId() != null && e.winRate() != null) { + double r = Math.max(0, Math.min(1, e.winRate())); + flexibleBotConfigRepository.save(FlexibleBotConfig.builder() + .userId(e.userId()) + .winRate(BigDecimal.valueOf(r)) + .updatedAt(Instant.now()) + .build()); + } + } + } + } + + public record BotConfigDto(List safeBotUserIds, List flexibleBots) {} + public record FlexibleBotEntryDto(Integer userId, Double winRate) {} +} diff --git a/src/main/java/com/lottery/lottery/service/ConfigurationService.java b/src/main/java/com/lottery/lottery/service/ConfigurationService.java new file mode 100644 index 0000000..005ef8a --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/ConfigurationService.java @@ -0,0 +1,53 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.Configuration; +import com.lottery.lottery.repository.ConfigurationRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * Configurations: key-value store for app-wide settings (e.g. lottery_bot_max_participants_before_join). + */ +@Service +@RequiredArgsConstructor +public class ConfigurationService { + + public static final String KEY_BOT_MAX_PARTICIPANTS_BEFORE_JOIN = "lottery_bot_max_participants_before_join"; + private static final int DEFAULT_MAX_PARTICIPANTS_BEFORE_JOIN = 1; + private static final int MIN_MAX_PARTICIPANTS = 0; + private static final int MAX_MAX_PARTICIPANTS = 10; + + private final ConfigurationRepository configurationRepository; + + @Transactional(readOnly = true) + public int getMaxParticipantsBeforeBotJoin() { + return configurationRepository.findById(KEY_BOT_MAX_PARTICIPANTS_BEFORE_JOIN) + .map(c -> parsePositiveInt(c.getValue(), DEFAULT_MAX_PARTICIPANTS_BEFORE_JOIN)) + .orElse(DEFAULT_MAX_PARTICIPANTS_BEFORE_JOIN); + } + + @Transactional + public int setMaxParticipantsBeforeBotJoin(int value) { + int clamped = Math.max(MIN_MAX_PARTICIPANTS, Math.min(MAX_MAX_PARTICIPANTS, value)); + Configuration c = configurationRepository.findById(KEY_BOT_MAX_PARTICIPANTS_BEFORE_JOIN) + .orElseGet(() -> { + Configuration newConfig = new Configuration(); + newConfig.setKey(KEY_BOT_MAX_PARTICIPANTS_BEFORE_JOIN); + return newConfig; + }); + c.setValue(String.valueOf(clamped)); + configurationRepository.save(c); + return clamped; + } + + private static int parsePositiveInt(String value, int defaultValue) { + if (value == null || value.isBlank()) return defaultValue; + try { + int v = Integer.parseInt(value.trim()); + return Math.max(MIN_MAX_PARTICIPANTS, Math.min(MAX_MAX_PARTICIPANTS, v)); + } catch (NumberFormatException e) { + return defaultValue; + } + } +} diff --git a/src/main/java/com/honey/honey/service/CountryCodeService.java b/src/main/java/com/lottery/lottery/service/CountryCodeService.java similarity index 99% rename from src/main/java/com/honey/honey/service/CountryCodeService.java rename to src/main/java/com/lottery/lottery/service/CountryCodeService.java index f694aa9..2bfa62c 100644 --- a/src/main/java/com/honey/honey/service/CountryCodeService.java +++ b/src/main/java/com/lottery/lottery/service/CountryCodeService.java @@ -1,4 +1,4 @@ -package com.honey.honey.service; +package com.lottery.lottery.service; import com.maxmind.db.CHMCache; import com.maxmind.geoip2.DatabaseReader; diff --git a/src/main/java/com/lottery/lottery/service/CryptoDepositService.java b/src/main/java/com/lottery/lottery/service/CryptoDepositService.java new file mode 100644 index 0000000..fd98580 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/CryptoDepositService.java @@ -0,0 +1,216 @@ +package com.lottery.lottery.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.lottery.lottery.dto.CryptoDepositMethodsResponse; +import com.lottery.lottery.dto.DepositAddressApiRequest; +import com.lottery.lottery.dto.DepositAddressResponse; +import com.lottery.lottery.dto.DepositMethodsDto; +import com.lottery.lottery.model.CryptoDepositConfig; +import com.lottery.lottery.model.CryptoDepositMethod; +import com.lottery.lottery.repository.CryptoDepositConfigRepository; +import com.lottery.lottery.repository.CryptoDepositMethodRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CryptoDepositService { + + private static final String DEPOSIT_METHODS_PATH = "api/v1/deposit-methods"; + private static final String DEPOSIT_ADDRESS_PATH = "api/v1/deposit-address"; + + @Value("${app.crypto-api.base-url:https://spin-passim.tech/}") + private String baseUrl; + + @Value("${app.crypto-api.api-key:}") + private String apiKey; + + private final RestTemplate restTemplate = new RestTemplate(); + private final ObjectMapper objectMapper; + private final CryptoDepositConfigRepository configRepository; + private final CryptoDepositMethodRepository methodRepository; + + /** + * Returns minimum deposit from DB only (no external API call). + * Used by Store screen for validation. + */ + @Transactional(readOnly = true) + public BigDecimal getMinimumDeposit() { + return configRepository.findById(1) + .map(CryptoDepositConfig::getMinimumDeposit) + .orElse(BigDecimal.valueOf(2.5)); + } + + /** + * Returns deposit methods and minimum deposit from DB only (no external API call). + * Used by Payment Options screen. + */ + @Transactional(readOnly = true) + public DepositMethodsDto getDepositMethodsFromDb() { + Optional configOpt = configRepository.findById(1); + BigDecimal minDeposit = configOpt.map(CryptoDepositConfig::getMinimumDeposit).orElse(BigDecimal.valueOf(2.5)); + List methods = methodRepository.findAll(); + return toDto(minDeposit, methods); + } + + /** + * Runs every 10 minutes. Fetches from external API; if response hash differs from stored hash, updates methods and config. + */ + @Scheduled(cron = "0 */10 * * * ?") + @Transactional + public void syncFromExternalIfHashChanged() { + String url = baseUrl.endsWith("/") ? baseUrl + DEPOSIT_METHODS_PATH : baseUrl + "/" + DEPOSIT_METHODS_PATH; + HttpHeaders headers = new HttpHeaders(); + if (apiKey != null && !apiKey.isEmpty()) { + headers.set("API-KEY", apiKey); + } + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response; + try { + if (log.isDebugEnabled()) { + log.debug("Crypto API request: GET {} (sync deposit methods)", url); + } + response = restTemplate.exchange(url, HttpMethod.GET, entity, CryptoDepositMethodsResponse.class); + if (log.isDebugEnabled()) { + log.debug("Crypto API response: GET {} status={} body={}", url, response.getStatusCode(), toJson(response.getBody())); + } + } catch (HttpStatusCodeException e) { + if (log.isDebugEnabled()) { + log.debug("Crypto API error response: GET {} status={} body={}", url, e.getStatusCode(), e.getResponseBodyAsString()); + } + log.warn("Crypto deposit sync (10 min): failed to fetch from external API: {}", e.getMessage()); + return; + } catch (Exception e) { + log.warn("Crypto deposit sync (10 min): failed to fetch from external API: {}", e.getMessage()); + return; + } + + CryptoDepositMethodsResponse body = response.getBody(); + if (body == null || body.getResult() == null || body.getResult().getActiveMethods() == null) { + return; + } + if (body.getRequestInfo() != null && body.getRequestInfo().getErrorCode() != null && body.getRequestInfo().getErrorCode() != 0) { + log.warn("Crypto deposit sync (10 min): API error: {}", body.getRequestInfo().getErrorMessage()); + return; + } + + String responseHash = body.getResult().getHash(); + String storedHash = configRepository.findById(1).map(CryptoDepositConfig::getMethodsHash).orElse(null); + if (responseHash != null && !responseHash.isEmpty() && responseHash.equals(storedHash)) { + log.debug("Crypto deposit sync (10 min): hash unchanged, skip update"); + return; + } + + List active = body.getResult().getActiveMethods(); + double minSum = active.stream() + .mapToDouble(m -> m.getMinDepositSum() != null ? m.getMinDepositSum() : 0) + .filter(v -> v > 0) + .min() + .orElse(2.5); + + methodRepository.deleteAllInBatch(); + Instant now = Instant.now(); + for (CryptoDepositMethodsResponse.ActiveMethod m : active) { + if (m.getPid() == null) continue; + CryptoDepositMethod method = CryptoDepositMethod.builder() + .pid(m.getPid()) + .name(m.getName() != null ? m.getName() : "") + .network(m.getNetwork() != null ? m.getNetwork() : "") + .example(m.getExample()) + .minDepositSum(BigDecimal.valueOf(m.getMinDepositSum() != null ? m.getMinDepositSum() : 2.5).setScale(2, RoundingMode.HALF_UP)) + .updatedAt(now) + .build(); + methodRepository.save(method); + } + + CryptoDepositConfig config = configRepository.findById(1).orElseGet(() -> { + CryptoDepositConfig c = new CryptoDepositConfig(); + c.setId(1); + return c; + }); + config.setMinimumDeposit(BigDecimal.valueOf(minSum).setScale(2, RoundingMode.HALF_UP)); + config.setMethodsHash(responseHash); + config.setUpdatedAt(now); + configRepository.save(config); + + log.info("Crypto deposit sync (10 min): updated {} methods, minimum_deposit={}, hash={}", active.size(), minSum, responseHash); + } + + /** + * Calls external API POST deposit-address to get wallet address and amount in coins. + * Does not create any payment record; caller is responsible for that. + */ + public DepositAddressResponse postDepositAddress(DepositAddressApiRequest request) { + Integer userId = request.getUserData() != null ? request.getUserData().getInternalId() : null; + String url = baseUrl.endsWith("/") ? baseUrl + DEPOSIT_ADDRESS_PATH : baseUrl + "/" + DEPOSIT_ADDRESS_PATH; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + if (apiKey != null && !apiKey.isEmpty()) { + headers.set("API-KEY", apiKey); + } + HttpEntity entity = new HttpEntity<>(request, headers); + if (log.isDebugEnabled()) { + log.debug("Crypto API request: userId={} POST {} body={}", userId, url, toJson(request)); + } + try { + ResponseEntity response = restTemplate.exchange( + url, HttpMethod.POST, entity, DepositAddressResponse.class); + DepositAddressResponse responseBody = response.getBody(); + if (log.isDebugEnabled()) { + log.debug("Crypto API response: userId={} POST {} status={} body={}", userId, url, response.getStatusCode(), toJson(responseBody)); + } + return responseBody; + } catch (HttpStatusCodeException e) { + if (log.isDebugEnabled()) { + log.debug("Crypto API error response: userId={} POST {} status={} body={}", userId, url, e.getStatusCode(), e.getResponseBodyAsString()); + } + throw e; + } + } + + private String toJson(Object o) { + if (o == null) return "null"; + try { + return objectMapper.writeValueAsString(o); + } catch (JsonProcessingException e) { + return "<" + e.getMessage() + ">"; + } + } + + private static DepositMethodsDto toDto(BigDecimal minimumDeposit, List methods) { + List list = methods.stream() + .map(m -> DepositMethodsDto.DepositMethodItemDto.builder() + .pid(m.getPid()) + .name(m.getName()) + .network(m.getNetwork()) + .example(m.getExample()) + .minDepositSum(m.getMinDepositSum()) + .build()) + .collect(Collectors.toList()); + return DepositMethodsDto.builder() + .minimumDeposit(minimumDeposit) + .activeMethods(list) + .build(); + } +} diff --git a/src/main/java/com/lottery/lottery/service/CryptoWithdrawalService.java b/src/main/java/com/lottery/lottery/service/CryptoWithdrawalService.java new file mode 100644 index 0000000..b52bc36 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/CryptoWithdrawalService.java @@ -0,0 +1,332 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.WithdrawalApiRequest; +import com.lottery.lottery.dto.WithdrawalApiResponse; +import com.lottery.lottery.dto.WithdrawalInfoApiResponse; +import com.lottery.lottery.dto.WithdrawalMethodDetailsDto; +import com.lottery.lottery.dto.WithdrawalMethodsApiResponse; +import com.lottery.lottery.dto.WithdrawalMethodsDto; +import com.lottery.lottery.model.CryptoWithdrawalMethod; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.CryptoWithdrawalMethodRepository; +import com.lottery.lottery.repository.UserARepository; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.lottery.lottery.util.IpUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CryptoWithdrawalService { + + private static final String WITHDRAWAL_METHODS_PATH = "api/v1/withdrawal-methods"; + private static final String WITHDRAWAL_PATH = "api/v1/withdrawal"; + private static final String WITHDRAWALS_INFO_PATH = "api/v1/withdrawals-info"; + private static final BigDecimal DEFAULT_MIN_WITHDRAWAL = new BigDecimal("0.10"); + + @Value("${app.crypto-api.base-url:https://spin-passim.tech/}") + private String baseUrl; + + @Value("${app.crypto-api.api-key:}") + private String apiKey; + + private final RestTemplate restTemplate = new RestTemplate(); + private final ObjectMapper objectMapper; + private final CryptoWithdrawalMethodRepository methodRepository; + private final UserARepository userARepository; + + /** In-memory lock: one withdrawal per user at a time to prevent double-submit / spam. */ + private final ConcurrentHashMap withdrawalInProgress = new ConcurrentHashMap<>(); + + /** + * Tries to acquire the withdrawal lock for the user. Returns false if user already has a withdrawal in progress. + */ + public boolean tryAcquireWithdrawal(Integer userId) { + return withdrawalInProgress.putIfAbsent(userId, Boolean.TRUE) == null; + } + + /** + * Releases the withdrawal lock for the user. Must be called in finally after API call. + */ + public void releaseWithdrawal(Integer userId) { + withdrawalInProgress.remove(userId); + } + + /** + * Calls external API POST api/v1/withdrawal. Does not create payout or deduct balance. + * Caller must hold the withdrawal lock and release it in finally. + * + * @param manualPay when true, adds manual_pay=1 to the request (for users who completed referral 50 or 100) + * @return response body on HTTP 200 + * @throws org.springframework.web.client.HttpStatusCodeException on non-2xx + */ + public WithdrawalApiResponse postWithdrawal(Integer userId, Integer pid, String wallet, Double amountUsd, boolean manualPay) { + UserA user = userARepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("User not found: " + userId)); + + String userIp = formatIpForCryptoApi(user.getIp()); + WithdrawalApiRequest.UserData userData = WithdrawalApiRequest.UserData.builder() + .internalId(user.getId()) + .screenName(user.getScreenName() != null ? user.getScreenName() : "") + .tgUsername(user.getTelegramName() != null ? user.getTelegramName() : "") + .tgId(user.getTelegramId() != null ? String.valueOf(user.getTelegramId()) : "") + .countryCode(user.getCountryCode() != null ? user.getCountryCode() : "XX") + .deviceCode(user.getDeviceCode() != null ? user.getDeviceCode() : "XX") + .languageCode(user.getLanguageCode() != null ? user.getLanguageCode() : "XX") + .userIp(userIp != null ? userIp : "0.0.0.0") + .build(); + + WithdrawalApiRequest.WithdrawalApiRequestBuilder requestBuilder = WithdrawalApiRequest.builder() + .pid(pid) + .userId(userId) + .wallet(wallet) + .amountUsd(amountUsd) + .userData(userData); + if (manualPay) { + requestBuilder.manualPay(1); + } + WithdrawalApiRequest request = requestBuilder.build(); + + String url = baseUrl.endsWith("/") ? baseUrl + WITHDRAWAL_PATH : baseUrl + "/" + WITHDRAWAL_PATH; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + if (apiKey != null && !apiKey.isEmpty()) { + headers.set("API-KEY", apiKey); + } + HttpEntity entity = new HttpEntity<>(request, headers); + if (log.isDebugEnabled()) { + log.debug("Crypto API request: POST {} body={}", url, toJson(request)); + } + try { + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, entity, WithdrawalApiResponse.class); + WithdrawalApiResponse body = response.getBody(); + if (log.isDebugEnabled()) { + log.debug("Crypto API response: POST {} status={} body={}", url, response.getStatusCode(), toJson(body)); + } + return body; + } catch (HttpStatusCodeException e) { + if (log.isDebugEnabled()) { + log.debug("Crypto API error response: POST {} status={} body={}", url, e.getStatusCode(), e.getResponseBodyAsString()); + } + throw e; + } + } + + /** + * Calls external API GET api/v1/withdrawals-info/{payment_id}. + * Returns empty if request fails or result has no payment_list / empty list. + */ + public Optional getWithdrawalInfo(int paymentId) { + String path = WITHDRAWALS_INFO_PATH + "/" + paymentId; + String url = baseUrl.endsWith("/") ? baseUrl + path : baseUrl + "/" + path; + HttpHeaders headers = new HttpHeaders(); + if (apiKey != null && !apiKey.isEmpty()) { + headers.set("API-KEY", apiKey); + } + try { + log.debug("Crypto API request: GET {}", url); + ResponseEntity response = restTemplate.exchange( + url, HttpMethod.GET, new HttpEntity<>(headers), WithdrawalInfoApiResponse.class); + WithdrawalInfoApiResponse responseBody = response.getBody(); + log.debug("Crypto API response: GET {} status={} body={}", url, response.getStatusCode(), toJson(responseBody)); + return Optional.ofNullable(responseBody); + } catch (HttpStatusCodeException e) { + log.debug("Crypto API error response: GET {} status={} body={}", url, e.getStatusCode(), e.getResponseBodyAsString()); + log.warn("Withdrawal info API error: paymentId={}, status={}", paymentId, e.getStatusCode()); + return Optional.empty(); + } + } + + private String toJson(Object o) { + if (o == null) return "null"; + try { + return objectMapper.writeValueAsString(o); + } catch (JsonProcessingException e) { + return "<" + e.getMessage() + ">"; + } + } + + private static String formatIpForCryptoApi(byte[] ipBytes) { + if (ipBytes == null || ipBytes.length == 0) { + return "0.0.0.0"; + } + if (ipBytes.length == 16) { + return "0.0.0.0"; + } + String s = IpUtils.bytesToIp(ipBytes); + return s != null ? s : "0.0.0.0"; + } + + /** + * Fetches from external API and returns details for one method by pid (rate_usd, misha_fee_usd). + * Called when user opens Payout Confirmation screen. + */ + public Optional getWithdrawalMethodDetails(int pid) { + String url = baseUrl.endsWith("/") ? baseUrl + WITHDRAWAL_METHODS_PATH : baseUrl + "/" + WITHDRAWAL_METHODS_PATH; + HttpHeaders headers = new HttpHeaders(); + if (apiKey != null && !apiKey.isEmpty()) { + headers.set("API-KEY", apiKey); + } + HttpEntity entity = new HttpEntity<>(headers); + + try { + if (log.isDebugEnabled()) { + log.debug("Crypto API request: GET {} (pid={})", url, pid); + } + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, WithdrawalMethodsApiResponse.class); + WithdrawalMethodsApiResponse body = response.getBody(); + if (log.isDebugEnabled()) { + log.debug("Crypto API response: GET {} status={} body={}", url, response.getStatusCode(), toJson(body)); + } + if (body == null || body.getResult() == null || body.getResult().getActiveMethods() == null) { + return Optional.empty(); + } + if (body.getRequestInfo() != null && body.getRequestInfo().getErrorCode() != null && body.getRequestInfo().getErrorCode() != 0) { + log.warn("Withdrawal method details: API error pid={}: {}", pid, body.getRequestInfo().getErrorMessage()); + return Optional.empty(); + } + return body.getResult().getActiveMethods().stream() + .filter(m -> pid == (m.getPid() != null ? m.getPid() : 0)) + .findFirst() + .map(m -> { + BigDecimal rateUsd = parseBigDecimal(m.getRateUsd()); + BigDecimal mishaFeeUsd = m.getMishaFeeUsd() != null ? BigDecimal.valueOf(m.getMishaFeeUsd()) : BigDecimal.ZERO; + return WithdrawalMethodDetailsDto.builder() + .pid(m.getPid()) + .name(m.getName()) + .ticker(m.getTicker()) + .rateUsd(rateUsd != null ? rateUsd : BigDecimal.ZERO) + .mishaFeeUsd(mishaFeeUsd) + .build(); + }); + } catch (HttpStatusCodeException e) { + if (log.isDebugEnabled()) { + log.debug("Crypto API error response: GET {} status={} body={}", url, e.getStatusCode(), e.getResponseBodyAsString()); + } + log.warn("Withdrawal method details pid={}: failed to fetch: {}", pid, e.getMessage()); + return Optional.empty(); + } catch (Exception e) { + log.warn("Withdrawal method details pid={}: failed to fetch: {}", pid, e.getMessage()); + return Optional.empty(); + } + } + + private static BigDecimal parseBigDecimal(String s) { + if (s == null || s.isEmpty()) return null; + try { + return new BigDecimal(s.trim()); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Returns withdrawal methods from DB only (no external API call). + * Called when user opens Payout screen. + */ + @Transactional(readOnly = true) + public WithdrawalMethodsDto getWithdrawalMethodsFromDb() { + List methods = methodRepository.findAllByOrderByPidAsc(); + List list = methods.stream() + .map(m -> WithdrawalMethodsDto.WithdrawalMethodItemDto.builder() + .pid(m.getPid()) + .name(m.getName()) + .network(m.getNetwork()) + .iconId(m.getIconId()) + .minWithdrawal(m.getMinWithdrawal()) + .build()) + .collect(Collectors.toList()); + return WithdrawalMethodsDto.builder() + .methods(list) + .build(); + } + + /** + * Runs every 30 minutes. Fetches from external API and overwrites crypto_withdrawal_methods (no hash check). + */ + @Scheduled(cron = "0 */30 * * * ?") // Every 30 minutes at second 0 + @Transactional + public void syncFromExternal() { + String url = baseUrl.endsWith("/") ? baseUrl + WITHDRAWAL_METHODS_PATH : baseUrl + "/" + WITHDRAWAL_METHODS_PATH; + HttpHeaders headers = new HttpHeaders(); + if (apiKey != null && !apiKey.isEmpty()) { + headers.set("API-KEY", apiKey); + } + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response; + try { + if (log.isDebugEnabled()) { + log.debug("Crypto API request: GET {} (sync withdrawal methods)", url); + } + response = restTemplate.exchange(url, HttpMethod.GET, entity, WithdrawalMethodsApiResponse.class); + if (log.isDebugEnabled()) { + log.debug("Crypto API response: GET {} status={} body={}", url, response.getStatusCode(), toJson(response.getBody())); + } + } catch (HttpStatusCodeException e) { + if (log.isDebugEnabled()) { + log.debug("Crypto API error response: GET {} status={} body={}", url, e.getStatusCode(), e.getResponseBodyAsString()); + } + log.warn("Crypto withdrawal sync (30 min): failed to fetch from external API: {}", e.getMessage()); + return; + } catch (Exception e) { + log.warn("Crypto withdrawal sync (30 min): failed to fetch from external API: {}", e.getMessage()); + return; + } + + WithdrawalMethodsApiResponse body = response.getBody(); + if (body == null || body.getResult() == null || body.getResult().getActiveMethods() == null) { + return; + } + if (body.getRequestInfo() != null && body.getRequestInfo().getErrorCode() != null && body.getRequestInfo().getErrorCode() != 0) { + log.warn("Crypto withdrawal sync (30 min): API error: {}", body.getRequestInfo().getErrorMessage()); + return; + } + + List active = body.getResult().getActiveMethods(); + + methodRepository.deleteAllInBatch(); + Instant now = Instant.now(); + Set pidsSeen = new HashSet<>(); + for (WithdrawalMethodsApiResponse.ActiveMethod m : active) { + if (m.getPid() == null) continue; + if (!pidsSeen.add(m.getPid())) continue; + String name = m.getTicker() != null ? m.getTicker() : ""; + String network = m.getName() != null ? m.getName() : ""; + String iconId = m.getIconId() != null ? m.getIconId().trim() : ""; + CryptoWithdrawalMethod method = CryptoWithdrawalMethod.builder() + .pid(m.getPid()) + .name(name) + .network(network) + .iconId(iconId) + .minWithdrawal(DEFAULT_MIN_WITHDRAWAL) + .updatedAt(now) + .build(); + methodRepository.save(method); + } + + log.debug("Crypto withdrawal sync (30 min): updated {} methods", active.size()); + } +} diff --git a/src/main/java/com/lottery/lottery/service/DataCleanupService.java b/src/main/java/com/lottery/lottery/service/DataCleanupService.java new file mode 100644 index 0000000..7b365ec --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/DataCleanupService.java @@ -0,0 +1,88 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.repository.GameRoundParticipantRepository; +import com.lottery.lottery.repository.TransactionRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +/** + * Scheduled service for batch cleanup of old transactions. + * Runs once per day at 3 AM (not at the same time as session cleanup which runs hourly). + * Deletes transactions older than 30 days in batches to avoid long transactions. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DataCleanupService { + + private final TransactionRepository transactionRepository; + + @Value("${app.data-cleanup.batch-size:5000}") + private int batchSize; + + @Value("${app.data-cleanup.max-batches-per-run:20}") + private int maxBatchesPerRun; + + @Value("${app.data-cleanup.batch-sleep-ms:500}") + private long batchSleepMs; + + /** + * Batch deletes old transactions. + * Runs once per day at 3:00 AM (not at the same time as session cleanup at minute 0). + * Processes up to MAX_BATCHES_PER_RUN batches per run. + * Sleeps between batches to let the database "breathe". + */ + @Scheduled(cron = "0 0 3 * * ?") // Every day at 3:00 AM + @Transactional + public void cleanupOldData() { + Instant thirtyDaysAgo = Instant.now().minus(30, ChronoUnit.DAYS); + + int totalTransactionsDeleted = 0; + int batchesProcessed = 0; + + log.info("Starting transaction cleanup (cutoffDate={}, batchSize={}, maxBatches={}, sleepMs={})", + thirtyDaysAgo, batchSize, maxBatchesPerRun, batchSleepMs); + + // Cleanup all transactions older than 30 days + while (batchesProcessed < maxBatchesPerRun) { + int deleted = transactionRepository.deleteOldTransactionsBatch(thirtyDaysAgo, batchSize); + totalTransactionsDeleted += deleted; + batchesProcessed++; + + if (deleted > 0) { + log.debug("Deleted {} old transactions in batch {}", deleted, batchesProcessed); + } + + // Sleep between batches to let database process WebSocket inserts + if (deleted > 0 && batchesProcessed < maxBatchesPerRun) { + try { + Thread.sleep(batchSleepMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.warn("Cleanup sleep interrupted", e); + break; + } + } + + // If we deleted less than batch size, we've caught up + if (deleted < batchSize) { + break; + } + } + + if (totalTransactionsDeleted > 0) { + log.info("Transaction cleanup completed: deleted {} transaction(s) in {} batch(es)", + totalTransactionsDeleted, batchesProcessed); + } else { + log.debug("Transaction cleanup completed: no old data to delete"); + } + } +} + diff --git a/src/main/java/com/lottery/lottery/service/FeatureSwitchService.java b/src/main/java/com/lottery/lottery/service/FeatureSwitchService.java new file mode 100644 index 0000000..bf0fe87 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/FeatureSwitchService.java @@ -0,0 +1,139 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.FeatureSwitch; +import com.lottery.lottery.repository.FeatureSwitchRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class FeatureSwitchService { + + public static final String REMOTE_BET_ENABLED = "remote_bet_enabled"; + public static final String PAYMENT_ENABLED = "payment_enabled"; + public static final String PAYOUT_ENABLED = "payout_enabled"; + public static final String TASK_REFERRAL_50_ENABLED = "task_referral_50_enabled"; + public static final String TASK_REFERRAL_100_ENABLED = "task_referral_100_enabled"; + /** When enabled, the scheduler registers lottery bots into joinable rounds (Part 2). */ + public static final String LOTTERY_BOT_SCHEDULER_ENABLED = "lottery_bot_scheduler_enabled"; + /** When enabled, app shows Promotions button and /api/promotions endpoints are available. Default false. */ + public static final String PROMOTIONS_ENABLED = "promotions_enabled"; + /** When enabled, send manual_pay=1 for all crypto payouts. When disabled, only for users who completed 50 or 100 referrals (first withdrawal). Default true. */ + public static final String MANUAL_PAY_FOR_ALL_PAYOUTS = "manual_pay_for_all_payouts"; + + private final FeatureSwitchRepository featureSwitchRepository; + + /** + * Returns whether the remote bet API endpoint is enabled (runtime toggle from admin). + */ + @Transactional(readOnly = true) + public boolean isRemoteBetEnabled() { + return featureSwitchRepository.findById(REMOTE_BET_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(false); + } + + /** + * Returns whether payment (deposits) is enabled. Default true when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isPaymentEnabled() { + return featureSwitchRepository.findById(PAYMENT_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(true); + } + + /** + * Returns whether payout (withdrawals) is enabled. Default true when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isPayoutEnabled() { + return featureSwitchRepository.findById(PAYOUT_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(true); + } + + /** + * Returns whether the "Invite 50 friends" referral task is enabled. Default true when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isTaskReferral50Enabled() { + return featureSwitchRepository.findById(TASK_REFERRAL_50_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(true); + } + + /** + * Returns whether the "Invite 100 friends" referral task is enabled. Default true when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isTaskReferral100Enabled() { + return featureSwitchRepository.findById(TASK_REFERRAL_100_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(true); + } + + /** + * Returns whether the lottery bot scheduler (auto-join bots) is enabled. Default true when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isLotteryBotSchedulerEnabled() { + return featureSwitchRepository.findById(LOTTERY_BOT_SCHEDULER_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(true); + } + + /** + * Returns whether promotions are enabled (app button and /api/promotions). Default false when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isPromotionsEnabled() { + return featureSwitchRepository.findById(PROMOTIONS_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(false); + } + + /** + * Returns whether to send manual_pay=1 for all crypto payouts. When true, all payouts use manual_pay=1; when false, only users who completed 50 or 100 referrals (and have no withdrawals yet) do. Default true when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isManualPayForAllPayoutsEnabled() { + return featureSwitchRepository.findById(MANUAL_PAY_FOR_ALL_PAYOUTS) + .map(FeatureSwitch::isEnabled) + .orElse(true); + } + + /** + * Returns all feature switches for admin (key and enabled). + */ + @Transactional(readOnly = true) + public List getAll() { + return featureSwitchRepository.findAll().stream() + .map(f -> new FeatureSwitchDto(f.getKey(), f.isEnabled())) + .collect(Collectors.toList()); + } + + /** + * Updates a feature switch by key. Creates the row if it does not exist. + */ + @Transactional + public FeatureSwitchDto setEnabled(String key, boolean enabled) { + FeatureSwitch f = featureSwitchRepository.findById(key) + .orElseGet(() -> { + FeatureSwitch newSwitch = new FeatureSwitch(); + newSwitch.setKey(key); + newSwitch.setUpdatedAt(Instant.now()); + return newSwitch; + }); + f.setEnabled(enabled); + featureSwitchRepository.save(f); + return new FeatureSwitchDto(f.getKey(), f.isEnabled()); + } + + public record FeatureSwitchDto(String key, boolean enabled) {} +} diff --git a/src/main/java/com/lottery/lottery/service/GameHistoryService.java b/src/main/java/com/lottery/lottery/service/GameHistoryService.java new file mode 100644 index 0000000..65f1105 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/GameHistoryService.java @@ -0,0 +1,86 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.GameHistoryEntryDto; +import com.lottery.lottery.model.Transaction; +import com.lottery.lottery.repository.TransactionRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Service for retrieving game history for users. + * Fetches WIN transactions from the last 30 days. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class GameHistoryService { + + private final TransactionRepository transactionRepository; + private final LocalizationService localizationService; + private static final int PAGE_SIZE = 50; + private static final int DAYS_TO_FETCH = 30; + + /** + * Gets WIN transactions for a user from the last 30 days with pagination. + * + * @param userId User ID + * @param page Page number (0-indexed) + * @param timezone Optional timezone (e.g., "Europe/London"). If null, uses UTC. + * @param languageCode Optional language code for date formatting (e.g., "EN", "RU"). If null, uses "EN". + * @return Page of game history entries with amount and date + */ + public Page getUserGameHistory(Integer userId, int page, String timezone, String languageCode) { + Instant thirtyDaysAgo = Instant.now().minus(DAYS_TO_FETCH, ChronoUnit.DAYS); + Pageable pageable = PageRequest.of(page, PAGE_SIZE); + + // Fetch WIN transactions from the last 30 days + Page transactions = transactionRepository.findByUserIdAndTypeAndCreatedAtAfterOrderByCreatedAtDesc( + userId, Transaction.TransactionType.WIN, thirtyDaysAgo, pageable); + + // Determine timezone to use + ZoneId zoneId; + try { + zoneId = (timezone != null && !timezone.trim().isEmpty()) + ? ZoneId.of(timezone) + : ZoneId.of("UTC"); + } catch (Exception e) { + // Invalid timezone, fallback to UTC + zoneId = ZoneId.of("UTC"); + } + + // Get localized "at" word + String atWord = localizationService.getMessage("dateTime.at", languageCode); + if (atWord == null || atWord.isEmpty()) { + atWord = "at"; // Fallback to English + } + + // Create formatter with localized "at" word + final ZoneId finalZoneId = zoneId; + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM '" + atWord + "' HH:mm") + .withZone(finalZoneId); + + return transactions.map(transaction -> { + // Format date as dd.MM at HH:mm (with localized "at" word) + String date = formatter.format(transaction.getCreatedAt()); + + // Amount is the total payout (already positive in WIN transactions) + return GameHistoryEntryDto.builder() + .amount(transaction.getAmount()) + .date(date) + .build(); + }); + } +} + + diff --git a/src/main/java/com/lottery/lottery/service/GameRoomService.java b/src/main/java/com/lottery/lottery/service/GameRoomService.java new file mode 100644 index 0000000..cb97182 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/GameRoomService.java @@ -0,0 +1,1424 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.AdminRoomDetailDto; +import com.lottery.lottery.dto.AdminRoomOnlineUserDto; +import com.lottery.lottery.dto.AdminRoomParticipantDto; +import com.lottery.lottery.dto.AdminRoomSummaryDto; +import com.lottery.lottery.dto.AdminRoomViewerDto; +import com.lottery.lottery.dto.AdminRoomWinnerDto; +import com.lottery.lottery.dto.GameRoomStateDto; +import com.lottery.lottery.dto.JoinRoundResult; +import com.lottery.lottery.dto.ParticipantDto; +import com.lottery.lottery.dto.WinnerDto; +import com.lottery.lottery.exception.*; +import com.lottery.lottery.model.*; +import com.lottery.lottery.repository.*; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GameRoomService { + + private static final long COUNTDOWN_DURATION_SECONDS = 20; + private static final long SPIN_DURATION_MS = 5000; // 5 seconds + + /** + * Gets room-specific bet limits in bigint format (database format with 6 decimal places). + * Room1: 1-200, Room2: 10-5000, Room3: 1000-50000 tickets + */ + private static long getMinBet(Integer roomNumber) { + return switch (roomNumber) { + case 1 -> 1_000_000L; // 1 + case 2 -> 10_000_000L; // 10 + case 3 -> 1_000_000_000L; // 1000 + default -> 1_000_000L; // Default to Room1 limits + }; + } + + private static long getMaxBet(Integer roomNumber) { + return switch (roomNumber) { + case 1 -> 200_000_000L; // 200 + case 2 -> 5_000_000_000L; // 5000 + case 3 -> 50_000_000_000L; // 50000 + default -> 200_000_000L; // Default to Room1 limits + }; + } + + /** + * Returns bet limits for a room (min and max in bigint format). + * Used by remote bet API when rand=true to pick a random amount. + */ + public static BetLimits getBetLimitsForRoom(int roomNumber) { + return new BetLimits(getMinBet(roomNumber), getMaxBet(roomNumber)); + } + + /** Min and max bet in bigint format (1 ticket = 1_000_000). */ + public record BetLimits(long minBet, long maxBet) {} + + /** + * Returns the user's current total bet in the given room's current round (bigint). + * Returns 0 if room not found, no active round, or user is not in the round. + * Used by remote bet API when rand=true to cap the random additional bet. + */ + @Transactional(readOnly = true) + public long getCurrentUserBetInRoom(int userId, int roomNumber) { + Optional roomOpt = gameRoomRepository.findByRoomNumber(roomNumber); + if (roomOpt.isEmpty()) { + return 0L; + } + GameRoom room = roomOpt.get(); + GameRound round = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.WAITING, GamePhase.COUNTDOWN)).orElse(null); + if (round == null) { + return 0L; + } + List list = participantRepository.findByRoundIdAndUserId(round.getId(), userId); + return list.isEmpty() ? 0L : list.get(0).getBet(); + } + + private final GameRoomRepository gameRoomRepository; + private final GameRoundRepository gameRoundRepository; + private final GameRoundParticipantRepository participantRepository; + private final UserARepository userARepository; + private final UserBRepository userBRepository; + private final UserDRepository userDRepository; + private final AvatarService avatarService; + private final RoomConnectionService roomConnectionService; + private final ReferralCommissionService referralCommissionService; + private final TransactionService transactionService; + private final LocalizationService localizationService; + private final BotConfigService botConfigService; + private final PromotionService promotionService; + + // Track last bet time per user per round to prevent fast clicks (rate limiting) + // Key: "userId:roundId", Value: last bet timestamp + private final Map lastBetTimes = new ConcurrentHashMap<>(); + private static final long MIN_BET_INTERVAL_MS = 1000; // 1 second + + // Callback for balance update notifications (set by controller) + private BalanceUpdateCallback balanceUpdateCallback; + + // Callback for state broadcast notifications (set by controller) + private StateBroadcastCallback stateBroadcastCallback; + + /** + * Returns the single most recent active round for a room in the given phases. + * Uses limit 1 in DB so corrupted data (multiple rounds in same phase) never breaks the game. + */ + private Optional getMostRecentActiveRound(Integer roomId, List phases) { + List list = gameRoundRepository.findMostRecentActiveRoundsByRoomId( + roomId, phases, PageRequest.of(0, 1)); + return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); + } + + /** + * Sets the callback for balance update notifications. + * Called by GameWebSocketController during initialization. + */ + public void setBalanceUpdateCallback(BalanceUpdateCallback callback) { + this.balanceUpdateCallback = callback; + } + + /** + * Sets the callback for state broadcast notifications. + * Called by GameWebSocketController during initialization. + * Also sets up room connection change callback to broadcast state updates. + */ + public void setStateBroadcastCallback(StateBroadcastCallback callback) { + this.stateBroadcastCallback = callback; + + // Set callback to broadcast state when room connections change + // This is set here (not in @PostConstruct) to ensure stateBroadcastCallback is not null + roomConnectionService.setConnectionChangeCallback((roomNumber, connectedCount) -> { + // Broadcast updated state to ALL rooms when connections change + // This ensures all clients receive updated allRoomsConnectedUsers map, + // regardless of which room they're subscribed to + if (stateBroadcastCallback != null) { + try { + // Broadcast state for all rooms (1, 2, 3) so all clients get updated counts + for (int roomNum = 1; roomNum <= 3; roomNum++) { + try { + GameRoomStateDto state = getRoomState(roomNum); + stateBroadcastCallback.broadcastState(roomNum, state); + } catch (Exception e) { + log.error("Error broadcasting state update for room {} after connection change in room {}", + roomNum, roomNumber, e); + } + } + } catch (Exception e) { + log.error("Error broadcasting state updates after connection change in room {}", roomNumber, e); + } + } + }); + } + + /** + * Broadcasts current state for all rooms (1, 2, 3). + * Used when a client subscribes so all clients (including the new one) get updated allRoomsConnectedUsers. + */ + public void broadcastStateToAllRooms() { + if (stateBroadcastCallback == null) return; + for (int roomNum = 1; roomNum <= 3; roomNum++) { + try { + GameRoomStateDto state = getRoomState(roomNum); + stateBroadcastCallback.broadcastState(roomNum, state); + } catch (Exception e) { + log.error("Error broadcasting state for room {}", roomNum, e); + } + } + } + + /** + * Callback interface for balance update notifications. + */ + @FunctionalInterface + public interface BalanceUpdateCallback { + void notifyBalanceUpdate(Integer userId); + } + + /** + * Callback interface for state broadcast notifications. + */ + @FunctionalInterface + public interface StateBroadcastCallback { + void broadcastState(Integer roomNumber, GameRoomStateDto state); + } + + /** + * Joins a user to a game round (for in-app WebSocket). + * Validates bet, deducts balance, and starts countdown if needed. + */ + @Transactional(isolation = Isolation.READ_COMMITTED) + public GameRoomStateDto joinRound(Integer userId, Integer roomNumber, Long betAmount) { + return joinRoundWithResult(userId, roomNumber, betAmount, false).getState(); + } + + /** + * Joins a user to a game round with optional unique-bet behaviour. + * When {@code uniqueBet} is true, if the user has already placed a bet in this room's current round, + * no additional bet is placed and the current state is returned with their existing bet amount. + * + * @param uniqueBet when true, at most one bet per user per room per round (no accumulation) + * @return result with state and bet amount to report in API response + */ + @Transactional(isolation = Isolation.READ_COMMITTED) + public JoinRoundResult joinRoundWithResult(Integer userId, Integer roomNumber, Long betAmount, boolean uniqueBet) { + + // Validate room number range (1-3) + if (roomNumber == null || roomNumber < 1 || roomNumber > 3) { + throw new GameException(localizationService.getMessage("game.error.roomNumberInvalid")); + } + + // Validate betAmount is not null + if (betAmount == null) { + throw new GameException(localizationService.getMessage("validation.error.required", "Bet amount")); + } + + // Validate bet amount is a positive integer (defense against float values) + // Jackson may truncate floats when deserializing to Long (e.g., 50.99 -> 50) + // This explicit check ensures we reject any non-integer values + if (betAmount <= 0) { + throw new GameException(localizationService.getMessage("game.error.betMustBePositive")); + } + + // Additional validation: Check if betAmount represents a whole number + // Since betAmount is already Long (integer type), this is mainly for documentation + // and defense-in-depth. If a float was sent and Jackson truncated it, the value + // would still be a valid Long, but the original intent was a decimal. + // Note: We can't detect truncation at this point, but we ensure the value is valid. + + // Validate bet amount against room-specific limits + long minBet = getMinBet(roomNumber); + long maxBet = getMaxBet(roomNumber); + if (betAmount < minBet || betAmount > maxBet) { + throw InvalidBetAmountException.create(localizationService, minBet, maxBet); + } + + // Get room with pessimistic lock to prevent race conditions + // This ensures only one transaction can update the room at a time + GameRoom room = gameRoomRepository.findByRoomNumberWithLock(roomNumber) + .orElseThrow(() -> new GameException(localizationService.getMessage("game.error.roomNotFound"))); + + // Check if room is joinable + if (room.getCurrentPhase() == GamePhase.SPINNING || room.getCurrentPhase() == GamePhase.RESOLUTION) { + throw new RoomNotJoinableException(room.getCurrentPhase(), localizationService); + } + + // Get or create current round (must be done before checking participants) + GameRound currentRound = getOrCreateCurrentRound(room); + + // Ensure round is persisted and flushed before checking participants + if (currentRound.getId() == null) { + currentRound = gameRoundRepository.saveAndFlush(currentRound); + } else { + // Refresh from database to ensure it's properly loaded + currentRound = gameRoundRepository.findById(currentRound.getId()) + .orElseThrow(() -> new GameException(localizationService.getMessage("game.error.roundNotFound"))); + } + + // Check if user already joined this round + List existingParticipants = participantRepository + .findByRoundIdAndUserId(currentRound.getId(), userId); + + // When unique=true, do not add another bet if user already has a bet in this room + if (uniqueBet && !existingParticipants.isEmpty()) { + GameRoomStateDto state = buildRoomState(room, currentRound); + int currentBetTickets = (int) (existingParticipants.get(0).getBet() / 1_000_000L); + return new JoinRoundResult(state, currentBetTickets); + } + + // Rate limiting: prevent fast clicks (backend validation) + String rateLimitKey = userId + ":" + currentRound.getId(); + Instant lastBetTime = lastBetTimes.get(rateLimitKey); + if (lastBetTime != null) { + long timeSinceLastBet = Instant.now().toEpochMilli() - lastBetTime.toEpochMilli(); + if (timeSinceLastBet < MIN_BET_INTERVAL_MS) { + throw new GameException(localizationService.getMessage("game.error.rateLimitWait")); + } + } + + // Update last bet time + lastBetTimes.put(rateLimitKey, Instant.now()); + + boolean isNewParticipant = existingParticipants.isEmpty(); + GameRoundParticipant participant; + Long previousBet = 0L; + Long userTotalBet = 0L; + + if (isNewParticipant) { + // New participant - validate total bet doesn't exceed max + userTotalBet = betAmount; + if (userTotalBet > maxBet) { + long maxBetTickets = maxBet / 1_000_000L; + long currentTotalBetTickets = userTotalBet / 1_000_000L; + String message = localizationService.getMessage("game.error.maxBetExceeded", + String.valueOf(maxBetTickets), + String.valueOf(currentTotalBetTickets), + "0"); + throw new GameException(message); + } + + // New participant - create new entry + // Reload round from database to ensure it's in the persistence context + GameRound managedRound = gameRoundRepository.findById(currentRound.getId()) + .orElseThrow(() -> new GameException(localizationService.getMessage("game.error.roundNotFound"))); + + participant = GameRoundParticipant.builder() + .round(managedRound) + .userId(userId) + .bet(betAmount) + .build(); + } else { + // Existing participant - extend their bet + // Use pessimistic lock to prevent race conditions on bet updates + participant = existingParticipants.get(0); + previousBet = participant.getBet(); + // Reload participant with pessimistic lock to prevent concurrent updates + participant = participantRepository.findByIdWithLock(participant.getId()) + .orElseThrow(() -> new GameException(localizationService.getMessage("game.error.participantNotFound"))); + + // Validate total bet doesn't exceed max + userTotalBet = participant.getBet() + betAmount; + if (userTotalBet > maxBet) { + long currentTotalBetTickets = participant.getBet() / 1_000_000L; + long maxBetTickets = maxBet / 1_000_000L; + long remainingCapacity = maxBetTickets - currentTotalBetTickets; + String message = localizationService.getMessage("game.error.maxBetExceeded", + String.valueOf(maxBetTickets), + String.valueOf(currentTotalBetTickets), + String.valueOf(remainingCapacity)); + throw new GameException(message); + } + + participant.setBet(userTotalBet); + } + + // Check user balance + // betAmount is already in bigint format (database format) + UserB userB = userBRepository.findById(userId) + .orElseThrow(() -> new GameException(localizationService.getMessage("user.error.notFound"))); + + if (userB.getBalanceA() < betAmount) { + throw new InsufficientBalanceException(localizationService.getMessage("game.error.insufficientBalance")); + } + + // Deduct balance (both in bigint format) + userB.setBalanceA(userB.getBalanceA() - betAmount); + userBRepository.save(userB); + + // Save participant (new or updated) + participantRepository.saveAndFlush(participant); + + // CRITICAL FIX: Calculate totalBet from actual participants (source of truth) + // This prevents race conditions - don't use room.getTotalBet() which can be corrupted + List allParticipants = participantRepository.findByRoundId(currentRound.getId()); + Long actualTotalBet = allParticipants.stream() + .mapToLong(GameRoundParticipant::getBet) + .sum(); + + // Update room totals with calculated value (not increment) + room.setTotalBet(actualTotalBet); + int actualParticipantCount = allParticipants.size(); + room.setRegisteredPlayers(actualParticipantCount); + + // Start countdown when we have at least 2 players and room is still WAITING. + // Using >= 2 (not == 2) so that if countdown wasn't started when the 2nd joined (e.g. race/multi-instance), + // the 3rd or any later join will unblock the room. + if (actualParticipantCount >= 2 && room.getCurrentPhase() == GamePhase.WAITING) { + startCountdown(room, currentRound); + } + + gameRoomRepository.save(room); + + GameRoomStateDto state = buildRoomState(room, currentRound); + + // Immediately broadcast state update (event-driven) + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(roomNumber, state); + } + + int betTicketsForResponse = (int) (betAmount / 1_000_000L); + return new JoinRoundResult(state, betTicketsForResponse); + } + + /** + * Starts the countdown for a round. + */ + private void startCountdown(GameRoom room, GameRound round) { + Instant countdownEnd = Instant.now().plusSeconds(COUNTDOWN_DURATION_SECONDS); + room.setCountdownEndAt(countdownEnd); + room.setCurrentPhase(GamePhase.COUNTDOWN); + + round.setCountdownStartedAt(Instant.now()); + round.setPhase(GamePhase.COUNTDOWN); + + gameRoomRepository.save(room); + gameRoundRepository.save(round); + + // Immediately broadcast state update (event-driven) + GameRoomStateDto state = buildRoomState(room, round); + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(room.getRoomNumber(), state); + } + } + + /** + * Gets or creates the current active round for a room. + * Uses DB as single source of truth (no in-memory round cache). + */ + private GameRound getOrCreateCurrentRound(GameRoom room) { + Optional foundRound = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.WAITING, GamePhase.COUNTDOWN)); + return foundRound.orElseGet(() -> createNewRound(room)); + } + + /** + * Creates a new round for a room. + */ + private GameRound createNewRound(GameRoom room) { + GameRound newRound = GameRound.builder() + .room(room) + .phase(GamePhase.WAITING) + .totalBet(0L) + .startedAt(Instant.now()) + .build(); + newRound = gameRoundRepository.saveAndFlush(newRound); + return newRound; + } + + /** + * Checks for countdowns that have ended and starts spins. + * Lightweight check (only queries COUNTDOWN phase rooms, uses index). + * Most countdowns are handled event-driven, but this catches edge cases. + */ + @Scheduled(fixedRate = 500) // Check every 500ms for countdowns (lightweight query) + @Transactional + public void checkCountdowns() { + // Only query rooms in COUNTDOWN phase (indexed query, very fast) + List roomsInCountdown = gameRoomRepository.findByCurrentPhase(GamePhase.COUNTDOWN); + + for (GameRoom room : roomsInCountdown) { + if (room.getCountdownEndAt() != null && Instant.now().isAfter(room.getCountdownEndAt())) { + try { + startSpin(room); + } catch (Exception e) { + log.error("Error starting spin for room {}", room.getRoomNumber(), e); + } + } + } + } + + /** + * Starts the spin phase and selects winner. + */ + @Transactional + public void startSpin(GameRoom room) { + + // CRITICAL FIX: Prevent concurrent execution - if room is already in SPINNING, skip + // This can happen if checkCountdowns is called multiple times concurrently + if (room.getCurrentPhase() == GamePhase.SPINNING) { + log.debug("Room {} is already in SPINNING phase, skipping", room.getRoomNumber()); + return; + } + + // Also check if room is not in COUNTDOWN phase (shouldn't happen, but safety check) + if (room.getCurrentPhase() != GamePhase.COUNTDOWN) { + log.warn("Room {} is not in COUNTDOWN phase (current: {}), skipping", + room.getRoomNumber(), room.getCurrentPhase()); + return; + } + + GameRound round = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.COUNTDOWN, GamePhase.WAITING)).orElse(null); + if (round == null) { + log.warn("No active round found for room {}", room.getRoomNumber()); + return; + } + + // Check participant count before starting spin + // If only 1 participant, refund immediately without starting spin animation + List participants = participantRepository.findByRoundId(round.getId()); + if (participants.size() < 2) { + if (participants.size() == 1) { + GameRoundParticipant participant = participants.get(0); + UserB userB = userBRepository.findById(participant.getUserId()) + .orElseThrow(() -> new IllegalStateException("User balance not found")); + userB.setBalanceA(userB.getBalanceA() + participant.getBet()); + userBRepository.save(userB); + + round.setPhase(GamePhase.RESOLUTION); + round.setResolvedAt(Instant.now()); + gameRoundRepository.save(round); + + // Set room to RESOLUTION (not WAITING) to allow frontend to process + room.setCurrentPhase(GamePhase.RESOLUTION); + gameRoomRepository.save(room); + + // Notify balance update + if (balanceUpdateCallback != null) { + balanceUpdateCallback.notifyBalanceUpdate(participant.getUserId()); + } + + // Don't reset room immediately - let scheduled task handle it after delay + } else { + // No participants, just reset + resetRoom(room); + } + return; + } + + // CRITICAL FIX: Calculate totalBet from actual participants (source of truth) + // Don't use room.getTotalBet() which can be corrupted by race conditions + Long actualRoundTotalBet = participants.stream() + .mapToLong(GameRoundParticipant::getBet) + .sum(); + + // Update round with actual totals from participants + round.setTotalBet(actualRoundTotalBet); + round.setCountdownEndedAt(Instant.now()); + round.setPhase(GamePhase.SPINNING); + + // CRITICAL: Determine winner NOW (before SPINNING phase) so frontend can generate tape correctly + // This ensures stopIndex and winner are available during SPINNING phase + // The actual payout will be applied later in resolveWinner() during RESOLUTION phase + // Use actualRoundTotalBet (from participants) not room.getTotalBet() (which might be corrupted) + long totalBet = actualRoundTotalBet; + if (totalBet <= 0) { + log.error("Invalid totalBet for round - roundId={}, totalBet={}, participants={}", + round.getId(), totalBet, participants.size()); + throw new IllegalStateException("Cannot start spin with zero total bet"); + } + + // Bot override: safe bot (balance < threshold) or flexible bot (fixed win rate) may force winner + GameRoundParticipant winner = botConfigService.resolveWinnerOverride(participants, totalBet) + .orElse(null); + + if (winner == null) { + // Normal weighted random by bet + long randomValue = new Random().nextLong(totalBet); + if (randomValue < 0) { + randomValue = -randomValue; + } + long cumulative = 0; + for (GameRoundParticipant p : participants) { + cumulative += p.getBet(); + if (randomValue < cumulative) { + winner = p; + break; + } + } + if (winner == null) { + winner = participants.get(participants.size() - 1); + } + } + + // Set winner in round (but don't apply payout yet - that happens in resolveWinner) + round.setWinnerUserId(winner.getUserId()); + round.setWinnerBet(winner.getBet()); + gameRoundRepository.save(round); + + // Update room phase + room.setCurrentPhase(GamePhase.SPINNING); + gameRoomRepository.save(room); + + // Immediately broadcast SPINNING state (event-driven) + // This state will now include winner and stopIndex for tape generation + GameRoomStateDto state = buildRoomState(room, round); + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(room.getRoomNumber(), state); + } + + // Winner resolution is handled by resolveSpins() scheduled task + // It checks countdownEndedAt + SPIN_DURATION_MS to determine when to resolve + // Note: Winner is already determined above, resolveWinner() will only apply payout + } + + /** + * Resolves the winner and applies payouts. + * Called after spin duration. + * Checks only rooms in SPINNING phase (lightweight query). + */ + @Scheduled(fixedRate = 500) // Check every 500ms for spins (lightweight query) + @Transactional + public void resolveSpins() { + // Only query rooms in SPINNING phase (indexed query, very fast) + List roomsSpinning = gameRoomRepository.findByCurrentPhase(GamePhase.SPINNING); + + for (GameRoom room : roomsSpinning) { + GameRound round = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.SPINNING)).orElse(null); + if (round == null) { + log.warn("Room {} is in SPINNING phase but no round found in database", room.getRoomNumber()); + resetRoom(room); + continue; + } + + if (round.getCountdownEndedAt() == null) { + log.warn("Round {} is in SPINNING phase but countdownEndedAt is null", round.getId()); + // This shouldn't happen, but if it does, set it to now to allow resolution + round.setCountdownEndedAt(Instant.now()); + gameRoundRepository.save(round); + } + + // Check if spin duration has passed + Instant spinEndTime = round.getCountdownEndedAt().plusMillis(SPIN_DURATION_MS); + if (Instant.now().isBefore(spinEndTime)) { + continue; // Still spinning + } + + // Re-check participant count before resolving + // If participants dropped below 2, handle refund instead + List participants = participantRepository.findByRoundId(round.getId()); + if (participants.size() < 2) { + log.warn("Participant count dropped during spin: room={}, roundId={}, count={}", + room.getRoomNumber(), round.getId(), participants.size()); + if (participants.size() == 1) { + GameRoundParticipant participant = participants.get(0); + UserB userB = userBRepository.findById(participant.getUserId()) + .orElseThrow(() -> new IllegalStateException("User balance not found")); + userB.setBalanceA(userB.getBalanceA() + participant.getBet()); + userBRepository.save(userB); + + log.info("Round refunded (participant dropped during spin) - room={}, roundId={}, userId={}, refundAmount={}", + room.getRoomNumber(), round.getId(), participant.getUserId(), participant.getBet()); + + round.setPhase(GamePhase.RESOLUTION); + round.setResolvedAt(Instant.now()); + gameRoundRepository.save(round); + + room.setCurrentPhase(GamePhase.RESOLUTION); + gameRoomRepository.save(room); + + // Immediately broadcast RESOLUTION state (event-driven) + GameRoomStateDto state = buildRoomState(room, round); + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(room.getRoomNumber(), state); + } + + // Notify balance update (event-driven) + if (balanceUpdateCallback != null) { + balanceUpdateCallback.notifyBalanceUpdate(participant.getUserId()); + } + + continue; + } else { + // No participants, reset room + resetRoom(room); + continue; + } + } + + try { + Integer winnerUserId = resolveWinner(room, round); + + // Balance update is already sent by resolveWinner via callback (event-driven) + // State broadcast is already done by resolveWinner via callback (event-driven) + } catch (Exception e) { + log.error("Error resolving winner for room {}", room.getRoomNumber(), e); + } + } + + // Second, reset rooms that have been in RESOLUTION for at least 4 seconds + // This allows frontend to process RESOLUTION phase and clear tape via animation callback + // Only query rooms in RESOLUTION phase (indexed query, very fast) + // DB as authority: if round is not in cache (e.g. after restart), load from DB so we still reset + List roomsInResolution = gameRoomRepository.findByCurrentPhase(GamePhase.RESOLUTION); + + for (GameRoom room : roomsInResolution) { + GameRound round = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.RESOLUTION)).orElse(null); + if (round != null && round.getResolvedAt() != null) { + // Check if RESOLUTION phase has been visible for at least 4 seconds + long secondsSinceResolution = Instant.now().getEpochSecond() - round.getResolvedAt().getEpochSecond(); + if (secondsSinceResolution >= 4) { + resetRoom(room); + + // Immediately broadcast WAITING state (event-driven) + GameRoomStateDto state = buildRoomState(room, null); + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(room.getRoomNumber(), state); + } + } + } + } + } + + /** + * Selects winner and applies payouts. + * @return Winner user ID for balance update notification + */ + @Transactional + public Integer resolveWinner(GameRoom room, GameRound round) { + // Re-read room so we see latest phase (avoid double payout if called twice) + GameRoom currentRoom = gameRoomRepository.findByRoomNumber(room.getRoomNumber()).orElse(null); + if (currentRoom == null || currentRoom.getCurrentPhase() != GamePhase.SPINNING) { + log.debug("Room {} no longer SPINNING (phase={}), skipping resolveWinner", + room.getRoomNumber(), currentRoom != null ? currentRoom.getCurrentPhase() : null); + return null; + } + room = currentRoom; + + // Get all participants + List participants = participantRepository.findByRoundId(round.getId()); + + if (participants.isEmpty()) { + log.warn("No participants found for round {}", round.getId()); + resetRoom(room); + return null; + } + + if (participants.size() == 1) { + // Only one participant, refund + // All values are in bigint format (database format) + GameRoundParticipant participant = participants.get(0); + UserB userB = userBRepository.findById(participant.getUserId()) + .orElseThrow(() -> new IllegalStateException("User balance not found")); + userB.setBalanceA(userB.getBalanceA() + participant.getBet()); + // Increment rounds_played even for refunded rounds (user still participated) + userB.setRoundsPlayed(userB.getRoundsPlayed() + 1); + userBRepository.save(userB); + + log.info("Round refunded (single participant) - room={}, roundId={}, userId={}, refundAmount={}", + room.getRoomNumber(), round.getId(), participant.getUserId(), participant.getBet()); + + round.setPhase(GamePhase.RESOLUTION); + round.setResolvedAt(Instant.now()); + gameRoundRepository.save(round); + + // Set room to RESOLUTION (not WAITING) to allow frontend to process + room.setCurrentPhase(GamePhase.RESOLUTION); + gameRoomRepository.save(room); + + + // Immediately broadcast RESOLUTION state (event-driven) + GameRoomStateDto state = buildRoomState(room, round); + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(room.getRoomNumber(), state); + } + + // Notify user's balance update via callback (event-driven) + if (balanceUpdateCallback != null) { + balanceUpdateCallback.notifyBalanceUpdate(participant.getUserId()); + } + + return participant.getUserId(); + } + + // Winner was already determined in startSpin() to enable tape generation + // Here we only need to apply the payout + Integer winnerUserId = round.getWinnerUserId(); + if (winnerUserId == null) { + // Defensive recovery: round in SPINNING without winner (e.g. replication lag, restart). + // Pick winner using same weighted-random logic as startSpin() so we can resolve and unblock the room. + log.warn("Defensive recovery: winner was null for round {} (room {}), selecting winner from participants", + round.getId(), room.getRoomNumber()); + long actualTotalBetRecovery = participants.stream().mapToLong(GameRoundParticipant::getBet).sum(); + if (actualTotalBetRecovery <= 0) { + log.error("Cannot recover round {} - totalBet is zero", round.getId()); + resetRoom(room); + return null; + } + long randomValue = new Random().nextLong(actualTotalBetRecovery); + if (randomValue < 0) { + randomValue = -randomValue; + } + long cumulative = 0; + GameRoundParticipant selectedWinner = null; + for (GameRoundParticipant p : participants) { + cumulative += p.getBet(); + if (randomValue < cumulative) { + selectedWinner = p; + break; + } + } + if (selectedWinner == null) { + selectedWinner = participants.get(participants.size() - 1); + } + round.setWinnerUserId(selectedWinner.getUserId()); + round.setWinnerBet(selectedWinner.getBet()); + round.setTotalBet(actualTotalBetRecovery); + gameRoundRepository.save(round); + winnerUserId = round.getWinnerUserId(); + } + + final Integer winnerIdForLookup = winnerUserId; + GameRoundParticipant winner = participants.stream() + .filter(p -> p.getUserId().equals(winnerIdForLookup)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Winner participant not found")); + + // CRITICAL FIX: Calculate totalBet from actual participants (source of truth) + // Don't use round.getTotalBet() which can be corrupted by race conditions + long actualTotalBet = participants.stream() + .mapToLong(GameRoundParticipant::getBet) + .sum(); + + // Update round with actual totalBet to ensure consistency + if (actualTotalBet != round.getTotalBet()) { + log.warn("Round totalBet mismatch - roundId={}, roundTotalBet={}, actualTotalBet={}, updating round", + round.getId(), round.getTotalBet(), actualTotalBet); + round.setTotalBet(actualTotalBet); + } + + // Calculate commission and payout + // All values are in bigint format (database format) + long totalBet = actualTotalBet; + long winnerBet = winner.getBet(); + long commission = (long) ((totalBet - winnerBet) * 0.2); + long payout = totalBet - commission; + + // Update winner balance (all values in bigint format) + UserB winnerBalance = userBRepository.findById(winner.getUserId()) + .orElseThrow(() -> new IllegalStateException("Winner balance not found")); + winnerBalance.setBalanceA(winnerBalance.getBalanceA() + payout); + // Increment total winnings since last deposit (for withdrawal limit) + long currentWinAfterDeposit = winnerBalance.getTotalWinAfterDeposit() != null ? winnerBalance.getTotalWinAfterDeposit() : 0L; + winnerBalance.setTotalWinAfterDeposit(currentWinAfterDeposit + payout); + userBRepository.save(winnerBalance); + + log.info("Round completed: room={}, roundId={}, winner={}, totalBet={}, payout={}", + room.getRoomNumber(), round.getId(), winner.getUserId(), totalBet, payout); + + // Create win transaction (total payout, not net profit) + try { + transactionService.createWinTransaction(winner.getUserId(), payout, round.getId()); + } catch (Exception e) { + log.error("Error creating win transaction: userId={}, roundId={}", winner.getUserId(), round.getId(), e); + // Continue even if transaction record creation fails + } + + // Add net win to active NET_WIN promotions (net win = payout - winner's bet) + long netWinBigint = payout - winnerBet; + if (netWinBigint > 0) { + try { + promotionService.addNetWinPoints(winner.getUserId(), netWinBigint); + } catch (Exception e) { + log.error("Error adding promotion points: userId={}, roundId={}", winner.getUserId(), round.getId(), e); + // Continue even if promotion update fails + } + // NET_WIN_MAX_BET: same points but only when winner made max bet in this room + long roomMaxBet = getMaxBet(room.getRoomNumber()); + if (winnerBet == roomMaxBet) { + try { + promotionService.addNetWinMaxBetPoints(winner.getUserId(), netWinBigint); + } catch (Exception e) { + log.error("Error adding NET_WIN_MAX_BET promotion points: userId={}, roundId={}", winner.getUserId(), round.getId(), e); + } + } + } + + Set refererIdsToNotify = new HashSet<>(); + + // REF_COUNT: for each participant whose first round this is (rounds_played == 0), add 1 point to their referer (level 1) + // only if the referral was registered during the promotion's timeframe (not before promo start) + for (GameRoundParticipant participant : participants) { + try { + UserB userB = userBRepository.findById(participant.getUserId()) + .orElseThrow(() -> new IllegalStateException("User balance not found for userId=" + participant.getUserId())); + if (userB.getRoundsPlayed() != null && userB.getRoundsPlayed() == 0) { + userDRepository.findById(participant.getUserId()).ifPresent(userD -> { + Integer refererId1 = userD.getRefererId1(); + if (refererId1 != null && refererId1 > 0) { + Instant referralRegTime = userARepository.findById(participant.getUserId()) + .filter(ua -> ua.getDateReg() != null && ua.getDateReg() > 0) + .map(ua -> Instant.ofEpochSecond(ua.getDateReg().longValue())) + .orElse(null); + try { + promotionService.addRefCountPoints(refererId1, referralRegTime); + } catch (Exception e) { + log.error("Error adding REF_COUNT promotion point for refererId={}, roundId={}", refererId1, round.getId(), e); + } + } + }); + } + } catch (Exception e) { + log.error("Error checking REF_COUNT for userId={}, roundId={}", participant.getUserId(), round.getId(), e); + } + } + + // Increment rounds_played for all participants + for (GameRoundParticipant participant : participants) { + try { + UserB userB = userBRepository.findById(participant.getUserId()) + .orElseThrow(() -> new IllegalStateException("User balance not found for userId=" + participant.getUserId())); + userB.setRoundsPlayed(userB.getRoundsPlayed() + 1); + userBRepository.save(userB); + } catch (Exception e) { + log.error("Error incrementing rounds_played for userId={}, roundId={}", participant.getUserId(), round.getId(), e); + // Continue processing other participants even if one fails + } + } + + // Create bet transactions for all participants (winners and losers) + for (GameRoundParticipant participant : participants) { + try { + // Check if this will be the user's 3rd bet BEFORE creating the transaction + boolean isThirdBet = referralCommissionService.willBeThirdBet(participant.getUserId()); + + transactionService.createBetTransaction(participant.getUserId(), participant.getBet(), round.getId()); + + // If this was the 3rd bet, give bonus to referrer 1 + if (isThirdBet) { + try { + Integer referer1Id = referralCommissionService.giveThirdBetBonus(participant.getUserId()); + if (referer1Id != null) { + refererIdsToNotify.add(referer1Id); + } + } catch (Exception e) { + log.error("Error giving 3rd bet bonus for userId={}", participant.getUserId(), e); + // Continue even if bonus fails + } + } + } catch (Exception e) { + log.error("Error creating bet transaction: userId={}, roundId={}", participant.getUserId(), round.getId(), e); + // Continue processing other participants even if one fails + } + } + + // Delete all participants for this round immediately after round finishes + try { + participantRepository.deleteAll(participants); + // Participants deleted for round (no log needed - happens on every round completion) + } catch (Exception e) { + log.error("Error deleting participants for roundId={}", round.getId(), e); + // Continue even if participant deletion fails + } + + // Process referral commissions for the winner and collect referer IDs + try { + Set winnerReferers = referralCommissionService.processWinnerCommissions( + winner.getUserId(), winnerBet, totalBet, commission); + refererIdsToNotify.addAll(winnerReferers); + } catch (Exception e) { + log.error("Error processing winner referral commissions for userId={}", winner.getUserId(), e); + // Continue even if referral commission processing fails + } + + // Process referral commissions for all losers and collect referer IDs + for (GameRoundParticipant participant : participants) { + if (!participant.getUserId().equals(winnerUserId)) { + try { + Set loserReferers = referralCommissionService.processLoserCommissions( + participant.getUserId(), participant.getBet()); + refererIdsToNotify.addAll(loserReferers); + } catch (Exception e) { + log.error("Error processing loser referral commissions for userId={}", participant.getUserId(), e); + // Continue processing other participants even if one fails + } + } + } + + // Notify referers of balance updates + if (balanceUpdateCallback != null) { + for (Integer refererId : refererIdsToNotify) { + try { + balanceUpdateCallback.notifyBalanceUpdate(refererId); + } catch (Exception e) { + log.error("Error notifying balance update for refererId={}", refererId, e); + } + } + } + + // Update round (winner info already set in startSpin, just update phase and payout info) + round.setWinnerBet(winnerBet); + round.setCommission(commission); + round.setPayout(payout); + round.setPhase(GamePhase.RESOLUTION); + round.setResolvedAt(Instant.now()); + gameRoundRepository.save(round); + + // Set room phase to RESOLUTION + room.setCurrentPhase(GamePhase.RESOLUTION); + gameRoomRepository.save(room); + + // Immediately broadcast RESOLUTION state (event-driven) + GameRoomStateDto state = buildRoomState(room, round); + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(room.getRoomNumber(), state); + } + + // Send balance update to winner (event-driven) + if (balanceUpdateCallback != null) { + balanceUpdateCallback.notifyBalanceUpdate(winner.getUserId()); + } + + // Return winner ID + return winner.getUserId(); + } + + /** + * Resets room to WAITING state for next round. + */ + private void resetRoom(GameRoom room) { + room.setCurrentPhase(GamePhase.WAITING); + room.setTotalBet(0L); + room.setRegisteredPlayers(0); + room.setCountdownEndAt(null); + gameRoomRepository.save(room); + + // Clean up rate limit entries for the round we're leaving (prevent memory leaks) + getMostRecentActiveRound(room.getId(), + List.of(GamePhase.RESOLUTION, GamePhase.SPINNING, GamePhase.COUNTDOWN)) + .ifPresent(r -> { + if (r.getId() != null) { + Long roundId = r.getId(); + lastBetTimes.entrySet().removeIf(entry -> entry.getKey().endsWith(":" + roundId)); + } + }); + + log.info("Room {} reset to WAITING state", room.getRoomNumber()); + } + + /** + * Resets room after RESOLUTION phase has been broadcast. + * Called after balance update is sent to winner. + */ + public void resetRoomAfterResolution(Integer roomNumber) { + GameRoom room = gameRoomRepository.findByRoomNumber(roomNumber) + .orElse(null); + if (room != null && room.getCurrentPhase() == GamePhase.RESOLUTION) { + resetRoom(room); + } + } + + /** + * Gets current room state. + */ + @Transactional(readOnly = true) + public GameRoomStateDto getRoomState(Integer roomNumber) { + GameRoom room = gameRoomRepository.findByRoomNumber(roomNumber) + .orElseThrow(() -> new IllegalArgumentException("Room not found: " + roomNumber)); + List phases = room.getCurrentPhase() == GamePhase.RESOLUTION + ? List.of(GamePhase.RESOLUTION) + : List.of(GamePhase.WAITING, GamePhase.COUNTDOWN, GamePhase.SPINNING); + GameRound round = getMostRecentActiveRound(room.getId(), phases).orElse(null); + return buildRoomState(room, round); + } + + /** + * Builds room state DTO. + */ + private GameRoomStateDto buildRoomState(GameRoom room, GameRound round) { + List participants = new ArrayList<>(); + WinnerDto winner = null; + Long countdownRemaining = null; + Long spinDuration = null; + Long stopIndex = null; + + if (round != null) { + // Get participants + List participantList = participantRepository.findByRoundId(round.getId()); + + // Optimize: Fetch all avatar URLs in one query (avoids N+1 query problem) + List userIds = participantList.stream() + .map(GameRoundParticipant::getUserId) + .collect(Collectors.toList()); + Map avatarUrlMap = avatarService.getAvatarUrls(userIds); + + participants = participantList.stream() + .map(p -> { + String avatarUrl = avatarUrlMap.get(p.getUserId()); + return ParticipantDto.builder() + .userId(p.getUserId()) + .bet(p.getBet() / 1_000_000L) // Convert bigint to tickets + .avatarUrl(avatarUrl) + .build(); + }) + .collect(Collectors.toList()); + + // Calculate countdown remaining + if (room.getCurrentPhase() == GamePhase.COUNTDOWN && room.getCountdownEndAt() != null) { + long remaining = room.getCountdownEndAt().getEpochSecond() - Instant.now().getEpochSecond(); + countdownRemaining = Math.max(0, remaining); + } + + // Winner info - include during SPINNING phase (for tape generation) and RESOLUTION phase (for display) + if ((round.getPhase() == GamePhase.SPINNING || round.getPhase() == GamePhase.RESOLUTION) && round.getWinnerUserId() != null) { + // Fetch winner's screen name from UserA + String winnerScreenName = "-"; + Optional winnerUser = userARepository.findById(round.getWinnerUserId()); + if (winnerUser.isPresent()) { + winnerScreenName = winnerUser.get().getScreenName(); + } + + // Generate avatar URL for winner + String winnerAvatarUrl = avatarService.getAvatarUrl(round.getWinnerUserId()); + + // Calculate winner's chance percentage from actual participant bets (same as CompletedRoundDto) + // This ensures consistency - calculate totalBet from participants, not from round.getTotalBet() + // which might be out of sync with actual participant bets + Double winChance = null; + Long winnerBetBigint = round.getWinnerBet(); + Long winnerBetTickets = winnerBetBigint / 1_000_000L; + if (winnerBetBigint > 0) { + // Calculate actual totalBet from participants (source of truth) + Long actualTotalBetFromParticipants = participantList.stream() + .mapToLong(GameRoundParticipant::getBet) + .sum(); + + if (actualTotalBetFromParticipants > 0) { + winChance = ((double) winnerBetBigint / actualTotalBetFromParticipants) * 100.0; + } + } + + winner = WinnerDto.builder() + .userId(round.getWinnerUserId()) + .screenName(winnerScreenName) + .avatarUrl(winnerAvatarUrl) + .bet(winnerBetTickets) // Convert bigint to tickets + .payout(round.getPayout()) // Keep in bigint format + .commission(round.getCommission()) // Keep in bigint format + .winChance(winChance) // Calculated from actual participant bets + .build(); + } + + // Spin animation parameters + if (round.getPhase() == GamePhase.SPINNING || round.getPhase() == GamePhase.RESOLUTION) { + spinDuration = SPIN_DURATION_MS; + // Calculate stop index based on winner's position in cumulative bet + // stopIndex is in tickets (not bigint) for consistency + if (round.getWinnerUserId() != null) { + long cumulative = 0; + for (GameRoundParticipant p : participantList) { + if (p.getUserId().equals(round.getWinnerUserId())) { + // Calculate in bigint, then convert to tickets + long betInTickets = p.getBet() / 1_000_000L; + stopIndex = cumulative + (betInTickets / 2); // Middle of winner's range + break; + } + cumulative += p.getBet() / 1_000_000L; // Convert to tickets for cumulative + } + } + } + } + + // Get connected users count from room connection service + int connectedUsers = roomConnectionService.getConnectedUsersCount(room.getRoomNumber()); + + // Get connected users count for all rooms (1, 2, 3) so frontend can update all room counters + Map allRoomsConnectedUsers = new HashMap<>(); + for (int roomNum = 1; roomNum <= 3; roomNum++) { + allRoomsConnectedUsers.put(roomNum, roomConnectionService.getConnectedUsersCount(roomNum)); + } + + // Get room-specific bet limits (convert from bigint to tickets) + long minBetBigint = getMinBet(room.getRoomNumber()); + long maxBetBigint = getMaxBet(room.getRoomNumber()); + long minBet = minBetBigint / 1_000_000L; + long maxBet = maxBetBigint / 1_000_000L; + + // Convert phase enum to integer: 1=WAITING, 2=COUNTDOWN, 3=SPINNING, 4=RESOLUTION + int phaseInt = switch (room.getCurrentPhase()) { + case WAITING -> 1; + case COUNTDOWN -> 2; + case SPINNING -> 3; + case RESOLUTION -> 4; + }; + + // Convert totalBet from bigint to tickets + long totalBetTickets = room.getTotalBet() / 1_000_000L; + + return GameRoomStateDto.builder() + .roomNumber(room.getRoomNumber()) + .roundId(round != null ? round.getId() : null) + .phase(phaseInt) + .totalBet(totalBetTickets) + .registeredPlayers(room.getRegisteredPlayers()) + .connectedUsers(connectedUsers) + .allRoomsConnectedUsers(allRoomsConnectedUsers) + .minBet(minBet) + .maxBet(maxBet) + .countdownEndAt(room.getCountdownEndAt()) + .countdownRemainingSeconds(countdownRemaining) + .participants(participants) + .winner(winner) + .spinDuration(spinDuration) + .stopIndex(stopIndex) + .build(); + } + + // --- Admin room management --- + + private static final long REPAIR_COUNTDOWN_DEAD_GRACE_SECONDS = 30; + + /** + * Returns summary for all rooms (for admin list). Uses DB and connection service. + */ + @Transactional(readOnly = true) + public List getAdminRoomSummaries() { + List list = new ArrayList<>(); + for (int roomNumber = 1; roomNumber <= 3; roomNumber++) { + GameRoom room = gameRoomRepository.findByRoomNumber(roomNumber).orElse(null); + if (room == null) continue; + List phases = room.getCurrentPhase() == GamePhase.RESOLUTION + ? List.of(GamePhase.RESOLUTION) + : List.of(GamePhase.WAITING, GamePhase.COUNTDOWN, GamePhase.SPINNING); + GameRound round = getMostRecentActiveRound(room.getId(), phases).orElse(null); + long totalBetTickets = room.getTotalBet() != null ? room.getTotalBet() / 1_000_000L : 0L; + double totalBetUsd = totalBetTickets / 1000.0; + int connected = roomConnectionService.getConnectedUsersCount(roomNumber); + list.add(AdminRoomSummaryDto.builder() + .roomNumber(room.getRoomNumber()) + .phase(room.getCurrentPhase().name()) + .connectedUsers(connected) + .registeredPlayers(room.getRegisteredPlayers() != null ? room.getRegisteredPlayers() : 0) + .totalBetTickets(totalBetTickets) + .totalBetUsd(totalBetUsd) + .roundId(round != null ? round.getId() : null) + .build()); + } + return list; + } + + /** + * Returns full detail for one room (for admin room detail screen). + */ + @Transactional(readOnly = true) + public AdminRoomDetailDto getAdminRoomDetail(Integer roomNumber) { + GameRoom room = gameRoomRepository.findByRoomNumber(roomNumber) + .orElseThrow(() -> new IllegalArgumentException("Room not found: " + roomNumber)); + List phases = room.getCurrentPhase() == GamePhase.RESOLUTION + ? List.of(GamePhase.RESOLUTION) + : List.of(GamePhase.WAITING, GamePhase.COUNTDOWN, GamePhase.SPINNING); + GameRound round = getMostRecentActiveRound(room.getId(), phases).orElse(null); + long totalBetTickets = room.getTotalBet() != null ? room.getTotalBet() / 1_000_000L : 0L; + double totalBetUsd = totalBetTickets / 1000.0; + int connected = roomConnectionService.getConnectedUsersCount(roomNumber); + List connectedUserIds = roomConnectionService.getConnectedUserIds(roomNumber); + List connectedViewers = new ArrayList<>(); + for (Integer viewerId : connectedUserIds) { + String screenName = "-"; + Optional u = userARepository.findById(viewerId); + if (u.isPresent() && u.get().getScreenName() != null) screenName = u.get().getScreenName(); + connectedViewers.add(AdminRoomViewerDto.builder().userId(viewerId).screenName(screenName).build()); + } + + List participants = new ArrayList<>(); + AdminRoomWinnerDto winnerDto = null; + if (round != null) { + List participantList = participantRepository.findByRoundId(round.getId()); + long totalBetBigint = participantList.stream().mapToLong(GameRoundParticipant::getBet).sum(); + for (GameRoundParticipant p : participantList) { + double chancePct = totalBetBigint > 0 ? (p.getBet() * 100.0 / totalBetBigint) : 0; + String screenName = "-"; + Optional u = userARepository.findById(p.getUserId()); + if (u.isPresent() && u.get().getScreenName() != null) screenName = u.get().getScreenName(); + participants.add(AdminRoomParticipantDto.builder() + .userId(p.getUserId()) + .screenName(screenName) + .betTickets(p.getBet() / 1_000_000L) + .chancePct(Math.round(chancePct * 100.0) / 100.0) + .build()); + } + if ((round.getPhase() == GamePhase.SPINNING || round.getPhase() == GamePhase.RESOLUTION) && round.getWinnerUserId() != null) { + long winnerBetBigint = round.getWinnerBet() != null ? round.getWinnerBet() : 0L; + double winChancePct = totalBetBigint > 0 ? (winnerBetBigint * 100.0 / totalBetBigint) : 0; + String winnerScreenName = "-"; + Optional u = userARepository.findById(round.getWinnerUserId()); + if (u.isPresent() && u.get().getScreenName() != null) winnerScreenName = u.get().getScreenName(); + winnerDto = AdminRoomWinnerDto.builder() + .userId(round.getWinnerUserId()) + .screenName(winnerScreenName) + .betTickets(winnerBetBigint / 1_000_000L) + .winChancePct(Math.round(winChancePct * 100.0) / 100.0) + .build(); + } + } + + return AdminRoomDetailDto.builder() + .roomNumber(room.getRoomNumber()) + .phase(room.getCurrentPhase().name()) + .roundId(round != null ? round.getId() : null) + .totalBetTickets(totalBetTickets) + .totalBetUsd(totalBetUsd) + .registeredPlayers(room.getRegisteredPlayers() != null ? room.getRegisteredPlayers() : 0) + .connectedUsers(connected) + .participants(participants) + .connectedViewers(connectedViewers) + .winner(winnerDto) + .build(); + } + + /** + * Returns all users currently connected to any room (viewers + participants), with room, current bet, balance, deposits, withdrawals, rounds played. + * For admin "online users across all rooms" table. + */ + @Transactional(readOnly = true) + public List getAdminOnlineUsersAcrossRooms() { + List result = new ArrayList<>(); + for (int roomNumber = 1; roomNumber <= 3; roomNumber++) { + GameRoom room = gameRoomRepository.findByRoomNumber(roomNumber).orElse(null); + if (room == null) continue; + List phases = room.getCurrentPhase() == GamePhase.RESOLUTION + ? List.of(GamePhase.RESOLUTION) + : List.of(GamePhase.WAITING, GamePhase.COUNTDOWN, GamePhase.SPINNING); + GameRound round = getMostRecentActiveRound(room.getId(), phases).orElse(null); + List connectedUserIds = roomConnectionService.getConnectedUserIds(roomNumber); + for (Integer userId : connectedUserIds) { + Long currentBetTickets = null; + if (round != null) { + List participantList = participantRepository.findByRoundIdAndUserId(round.getId(), userId); + if (!participantList.isEmpty()) { + currentBetTickets = participantList.get(0).getBet() / 1_000_000L; + } + } + String screenName = "-"; + Optional ua = userARepository.findById(userId); + if (ua.isPresent() && ua.get().getScreenName() != null) screenName = ua.get().getScreenName(); + UserB userB = userBRepository.findById(userId).orElse(UserB.builder() + .id(userId) + .balanceA(0L) + .depositTotal(0L) + .depositCount(0) + .withdrawTotal(0L) + .withdrawCount(0) + .roundsPlayed(0) + .build()); + result.add(AdminRoomOnlineUserDto.builder() + .userId(userId) + .screenName(screenName) + .roomNumber(roomNumber) + .currentBetTickets(currentBetTickets) + .balanceA(userB.getBalanceA()) + .depositTotal(userB.getDepositTotal()) + .depositCount(userB.getDepositCount()) + .withdrawTotal(userB.getWithdrawTotal()) + .withdrawCount(userB.getWithdrawCount()) + .roundsPlayed(userB.getRoundsPlayed()) + .build()); + } + } + return result; + } + + /** + * Admin-only: run repair logic for one room (fix dead state). Uses room lock. + */ + @Transactional + public void repairRoom(int roomNumber) { + if (roomNumber < 1 || roomNumber > 3) { + throw new IllegalArgumentException("Invalid room number: " + roomNumber); + } + GameRoom room = gameRoomRepository.findByRoomNumberWithLock(roomNumber).orElse(null); + if (room == null) return; + GamePhase phase = room.getCurrentPhase(); + switch (phase) { + case WAITING -> recoverWaitingIfDead(room); + case COUNTDOWN -> recoverCountdownIfDead(room); + case SPINNING -> recoverSpinningIfDead(room); + case RESOLUTION -> recoverResolutionIfDead(room); + } + } + + private void recoverWaitingIfDead(GameRoom room) { + Optional roundOpt = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.WAITING)); + if (roundOpt.isEmpty()) return; + GameRound round = roundOpt.get(); + int count = participantRepository.findByRoundId(round.getId()).size(); + if (count < 2) return; + log.warn("Admin repair: room {} was WAITING with {} participants, starting countdown", room.getRoomNumber(), count); + startCountdown(room, round); + } + + private void recoverCountdownIfDead(GameRoom room) { + Instant now = Instant.now(); + if (room.getCountdownEndAt() == null) { + log.warn("Admin repair: room {} was COUNTDOWN with null countdownEndAt, setting to past", room.getRoomNumber()); + room.setCountdownEndAt(now.minusSeconds(1)); + gameRoomRepository.save(room); + return; + } + if (now.minusSeconds(REPAIR_COUNTDOWN_DEAD_GRACE_SECONDS).isBefore(room.getCountdownEndAt())) return; + log.warn("Admin repair: room {} was COUNTDOWN past end time, starting spin", room.getRoomNumber()); + startSpin(room); + } + + private void recoverSpinningIfDead(GameRoom room) { + Optional roundOpt = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.SPINNING)); + if (roundOpt.isEmpty()) return; + GameRound round = roundOpt.get(); + if (round.getCountdownEndedAt() == null) { + round.setCountdownEndedAt(Instant.now()); + gameRoundRepository.save(round); + } + Instant spinEnd = round.getCountdownEndedAt().plusMillis(SPIN_DURATION_MS); + if (Instant.now().isBefore(spinEnd)) return; + log.warn("Admin repair: room {} was SPINNING past spin end, resolving winner", room.getRoomNumber()); + try { + resolveWinner(room, round); + } catch (Exception e) { + log.error("Admin repair: failed to resolve winner for room {}", room.getRoomNumber(), e); + } + } + + private void recoverResolutionIfDead(GameRoom room) { + Optional roundOpt = getMostRecentActiveRound( + room.getId(), List.of(GamePhase.RESOLUTION)); + if (roundOpt.isEmpty()) return; + GameRound round = roundOpt.get(); + if (round.getResolvedAt() == null) return; + long secondsSince = Instant.now().getEpochSecond() - round.getResolvedAt().getEpochSecond(); + if (secondsSince < 4) return; + log.warn("Admin repair: room {} was RESOLUTION for {}s, resetting to WAITING", room.getRoomNumber(), secondsSince); + resetRoom(room); + GameRoomStateDto state = buildRoomState(room, null); + if (stateBroadcastCallback != null) { + stateBroadcastCallback.broadcastState(room.getRoomNumber(), state); + } + } +} diff --git a/src/main/java/com/lottery/lottery/service/LocalizationService.java b/src/main/java/com/lottery/lottery/service/LocalizationService.java new file mode 100644 index 0000000..fe223b5 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/LocalizationService.java @@ -0,0 +1,84 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.config.LocaleConfig; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.security.UserContext; +import lombok.RequiredArgsConstructor; +import org.springframework.context.MessageSource; +import org.springframework.stereotype.Service; + +import java.util.Locale; +import java.util.Optional; + +/** + * Service for localization and message retrieval. + * Gets user's language from UserContext and provides localized messages. + */ +@Service +@RequiredArgsConstructor +public class LocalizationService { + + private final MessageSource messageSource; + private final UserARepository userARepository; + + /** + * Gets the current user's locale based on their languageCode. + * Falls back to English if user not found or languageCode is invalid. + */ + public Locale getCurrentUserLocale() { + try { + UserA user = UserContext.get(); + if (user != null && user.getLanguageCode() != null) { + return LocaleConfig.languageCodeToLocale(user.getLanguageCode()); + } + } catch (Exception e) { + // UserContext might not be available in all contexts (e.g., scheduled tasks) + // Fall back to English + } + return Locale.ENGLISH; + } + + /** + * Gets a localized message for the current user. + * + * @param code Message code (e.g., "game.error.roomNotFound") + * @param args Optional arguments for message formatting + * @return Localized message string + */ + public String getMessage(String code, Object... args) { + Locale locale = getCurrentUserLocale(); + return messageSource.getMessage(code, args, code, locale); + } + + /** + * Gets a localized message for a specific user by userId. + * + * @param userId User ID + * @param code Message code + * @param args Optional arguments for message formatting + * @return Localized message string + */ + public String getMessageForUser(Integer userId, String code, Object... args) { + Locale locale = Locale.ENGLISH; // Default + Optional userOpt = userARepository.findById(userId); + if (userOpt.isPresent() && userOpt.get().getLanguageCode() != null) { + locale = LocaleConfig.languageCodeToLocale(userOpt.get().getLanguageCode()); + } + return messageSource.getMessage(code, args, code, locale); + } + + /** + * Gets a localized message for a specific locale. + * + * @param locale Locale to use + * @param code Message code + * @param args Optional arguments for message formatting + * @return Localized message string + */ + public String getMessage(Locale locale, String code, Object... args) { + return messageSource.getMessage(code, args, code, locale); + } +} + + diff --git a/src/main/java/com/lottery/lottery/service/LotteryBotSchedulerService.java b/src/main/java/com/lottery/lottery/service/LotteryBotSchedulerService.java new file mode 100644 index 0000000..8adac50 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/LotteryBotSchedulerService.java @@ -0,0 +1,223 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.exception.BetDecisionException; +import com.lottery.lottery.exception.GameException; +import com.lottery.lottery.model.GamePhase; +import com.lottery.lottery.model.GameRound; +import com.lottery.lottery.model.GameRoundParticipant; +import com.lottery.lottery.model.GameRoom; +import com.lottery.lottery.model.LotteryBotConfig; +import com.lottery.lottery.repository.GameRoomRepository; +import com.lottery.lottery.repository.GameRoundParticipantRepository; +import com.lottery.lottery.repository.GameRoundRepository; +import com.lottery.lottery.repository.LotteryBotConfigRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Scheduler that registers lottery bots into joinable rounds based on + * lottery_bot_configs (time window, room flags, active). Joins only when: + * - round has no participants for at least 1 minute, or + * - round has exactly one participant who has been waiting longer than 10 seconds. + * Does not join when 2+ participants. Uses BetDecisionService (persona + streak) for bet amount. + * Does not use /remotebet. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class LotteryBotSchedulerService { + + private static final long TICKETS_TO_BIGINT = 1_000_000L; + /** Round empty (0 participants) for at least this long before a bot may join. */ + private static final long EMPTY_ROOM_THRESHOLD_SECONDS = 1L; + /** Single participant must be waiting at least this long before a bot may join. */ + private static final long ONE_PARTICIPANT_WAIT_THRESHOLD_SECONDS = 3L; + + private static final int BOT_HISTORY_SIZE = 10; + + private final GameRoomRepository gameRoomRepository; + private final GameRoundRepository gameRoundRepository; + private final GameRoundParticipantRepository participantRepository; + private final LotteryBotConfigRepository lotteryBotConfigRepository; + private final GameRoomService gameRoomService; + private final BetDecisionService betDecisionService; + private final BotBetHistoryService botBetHistoryService; + private final FeatureSwitchService featureSwitchService; + private final ConfigurationService configurationService; + + /** Per room: first time we observed no active round (for EMPTY_ROOM_THRESHOLD). Cleared when room has an active round again. */ + private final Map roomFirstSeenNoRound = new ConcurrentHashMap<>(); + + /** + * Every 15 seconds: for each room, if joinable and round state allows (0 participants >= 1 min, or 1 participant >= 10 sec), register eligible bots. + * Controlled by feature switch {@code lottery_bot_scheduler_enabled} (default on). + */ + @Scheduled(fixedDelayString = "${app.lottery-bot.schedule-fixed-delay-ms:5000}") + public void registerBotsForJoinableRooms() { + boolean featureOn = featureSwitchService.isLotteryBotSchedulerEnabled(); + List activeConfigs = lotteryBotConfigRepository.findAllByActiveTrue(); + + if (!featureOn) { + return; + } + if (activeConfigs.isEmpty()) { + return; + } + + LocalTime nowUtc = LocalTime.now(ZoneOffset.UTC); + + for (int roomNumber = 1; roomNumber <= 3; roomNumber++) { + Optional roomOpt = gameRoomRepository.findByRoomNumber(roomNumber); + if (roomOpt.isEmpty()) { + continue; + } + GameRoom room = roomOpt.get(); + if (room.getCurrentPhase() != GamePhase.WAITING && room.getCurrentPhase() != GamePhase.COUNTDOWN) { + continue; + } + + // 0 participants: no round exists yet (round is created on first join). Check no active round in WAITING/COUNTDOWN/SPINNING. + // 1 participant: we need an active round in WAITING phase only (not COUNTDOWN). + List roundsActive = gameRoundRepository.findMostRecentActiveRoundsByRoomId( + room.getId(), List.of(GamePhase.WAITING, GamePhase.COUNTDOWN, GamePhase.SPINNING), PageRequest.of(0, 1)); + + GameRound round = null; + List participants = List.of(); + int participantCount = 0; + boolean mayJoin = false; + + if (roundsActive.isEmpty()) { + // No active round → 0 participants. Enforce EMPTY_ROOM_THRESHOLD: only join after room has been empty for that long. + participantCount = 0; + Instant now = Instant.now(); + Instant firstSeenNoRound = roomFirstSeenNoRound.computeIfAbsent(roomNumber, k -> now); + mayJoin = !now.isBefore(firstSeenNoRound.plusSeconds(EMPTY_ROOM_THRESHOLD_SECONDS)); + if (!mayJoin) { + continue; + } + } else { + roomFirstSeenNoRound.remove(roomNumber); // room has an active round again, clear so next "no round" starts 30s from scratch + round = roundsActive.get(0); + participants = participantRepository.findByRoundId(round.getId()); + participantCount = participants.size(); + + int maxParticipantsBeforeBotJoin = configurationService.getMaxParticipantsBeforeBotJoin(); + if (participantCount > maxParticipantsBeforeBotJoin) { + continue; + } + + Instant now = Instant.now(); + if (participantCount == 0) { + mayJoin = round.getStartedAt() != null + && round.getStartedAt().plusSeconds(EMPTY_ROOM_THRESHOLD_SECONDS).isBefore(now); + } else { + // 1..N participants: only join if round is in WAITING (not COUNTDOWN) and oldest participant waited long enough + if (round.getPhase() != GamePhase.WAITING) { + continue; + } + Instant oldestJoined = participants.stream() + .map(GameRoundParticipant::getJoinedAt) + .min(Instant::compareTo) + .orElse(Instant.EPOCH); + mayJoin = oldestJoined.plusSeconds(ONE_PARTICIPANT_WAIT_THRESHOLD_SECONDS).isBefore(now); + } + if (!mayJoin) { + continue; + } + } + + // Shuffle so we don't always try the same bot first (e.g. by config id) + List configsToTry = new ArrayList<>(activeConfigs); + Collections.shuffle(configsToTry); + + for (LotteryBotConfig config : configsToTry) { + if (!isRoomEnabledForConfig(config, roomNumber)) { + continue; + } + if (!isCurrentTimeInWindow(nowUtc, config.getTimeUtcStart(), config.getTimeUtcEnd())) { + continue; + } + int userId = config.getUserId(); + if (gameRoomService.getCurrentUserBetInRoom(userId, roomNumber) > 0L) { + continue; + } + + GameRoomService.BetLimits limits = GameRoomService.getBetLimitsForRoom(roomNumber); + long roomMinTickets = limits.minBet() / TICKETS_TO_BIGINT; + long roomMaxTickets = limits.maxBet() / TICKETS_TO_BIGINT; + long botMinTickets = config.getBetMin() != null ? config.getBetMin() / TICKETS_TO_BIGINT : roomMinTickets; + long botMaxTickets = config.getBetMax() != null ? config.getBetMax() / TICKETS_TO_BIGINT : roomMaxTickets; + long currentRoundTotalBetTickets = participants.stream() + .mapToLong(p -> p.getBet() != null ? p.getBet() / TICKETS_TO_BIGINT : 0L) + .sum(); + var history = botBetHistoryService.getLastBetsAndResults(userId, BOT_HISTORY_SIZE); + + try { + long tickets = betDecisionService.decideBetAmountTickets( + BotBetContext.builder() + .roomNumber(roomNumber) + .roundId(round != null ? round.getId() : null) + .participantCount(participantCount) + .config(config) + .roomMinTickets(roomMinTickets) + .roomMaxTickets(roomMaxTickets) + .botMinTickets(botMinTickets) + .botMaxTickets(botMaxTickets) + .currentRoundTotalBetTickets(currentRoundTotalBetTickets) + .lastBets10(history.lastBets()) + .lastResults10(history.lastResults()) + .build()); + long betBigint = Math.max(1L, tickets) * TICKETS_TO_BIGINT; + betBigint = Math.max(limits.minBet(), Math.min(limits.maxBet(), betBigint)); + + gameRoomService.joinRoundWithResult(userId, roomNumber, betBigint, true); + log.info("Lottery bot registered: userId={}, room={}, betTickets={}", userId, roomNumber, tickets); + // Only one bot per room per run; next run will see updated participant count and enforce 7s wait for second bot + break; + } catch (BetDecisionException e) { + log.warn("Bot not registered (bet decision failed): userId={}, room={}, reason={}", userId, roomNumber, e.getMessage()); + } catch (GameException e) { + log.warn("Bot join skipped: userId={}, room={}, reason={}", userId, roomNumber, e.getUserMessage()); + } catch (Exception e) { + log.warn("Bot join failed: userId={}, room={}", userId, roomNumber, e); + } + } + } + } + + private static boolean isRoomEnabledForConfig(LotteryBotConfig config, int roomNumber) { + return switch (roomNumber) { + case 1 -> Boolean.TRUE.equals(config.getRoom1()); + case 2 -> Boolean.TRUE.equals(config.getRoom2()); + case 3 -> Boolean.TRUE.equals(config.getRoom3()); + default -> false; + }; + } + + /** + * Returns true if current UTC time is within [start, end]. + * Handles overnight window: e.g. start 22:00, end 06:00 → true if now >= 22:00 or now <= 06:00. + */ + private static boolean isCurrentTimeInWindow(LocalTime now, LocalTime start, LocalTime end) { + if (start == null || end == null) { + return false; + } + if (!start.isAfter(end)) { + return !now.isBefore(start) && !now.isAfter(end); + } + return !now.isBefore(start) || !now.isAfter(end); + } +} diff --git a/src/main/java/com/lottery/lottery/service/NotificationBroadcastService.java b/src/main/java/com/lottery/lottery/service/NotificationBroadcastService.java new file mode 100644 index 0000000..c4f5d84 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/NotificationBroadcastService.java @@ -0,0 +1,183 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.config.TelegramProperties; +import com.lottery.lottery.dto.TelegramSendResult; +import com.lottery.lottery.model.NotificationAudit; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.NotificationAuditRepository; +import com.lottery.lottery.repository.UserARepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Broadcasts a notification (text + optional image or video) to users via Telegram. + * Runs asynchronously; respects Telegram rate limit (~30 msg/s) and a stop flag. + * No @Transactional on the send loop; users are read in pages (500) to avoid memory issues. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class NotificationBroadcastService { + + /** Delay between sends to stay under 30 messages per second (Telegram limit). */ + private static final long DELAY_MS_BETWEEN_SENDS = 40; + private static final int PAGE_SIZE = 500; + + private final TelegramProperties telegramProperties; + private final TelegramBotApiService telegramBotApiService; + private final UserARepository userARepository; + private final NotificationAuditRepository notificationAuditRepository; + + private final AtomicBoolean stopRequested = new AtomicBoolean(false); + + /** + * Request running broadcast to stop. Checked between each user send. + */ + public void requestStop() { + stopRequested.set(true); + } + + /** Mini app URL for the optional inline button (same as in TelegramWebhookController). */ + private static final String MINI_APP_URL = "https://win-spin.live/auth"; + + /** + * Run broadcast asynchronously. Uses userIdFrom/userIdTo (internal user ids); if null, uses 1 and max id. + * Only one of imageUrl or videoUrl is used; video takes priority if both are set. + * If buttonText is non-empty, each message gets an inline button with that text opening the mini app. + * When ignoreBlocked is true, skips users whose latest notification_audit record has status FAILED (e.g. blocked the bot). + */ + @Async + public void runBroadcast(String message, String imageUrl, String videoUrl, + Integer userIdFrom, Integer userIdTo, String buttonText, Boolean ignoreBlocked) { + stopRequested.set(false); + String botToken = telegramProperties.getBotToken(); + if (!StringUtils.hasText(botToken)) { + log.error("Notification broadcast skipped: bot token not configured"); + return; + } + + int fromId = userIdFrom != null && userIdFrom > 0 ? userIdFrom : 1; + int toId = userIdTo != null && userIdTo > 0 ? userIdTo : userARepository.getMaxId(); + if (fromId > toId) { + log.warn("Notification broadcast: empty range fromId={} toId={}", fromId, toId); + return; + } + + boolean skipBlocked = Boolean.TRUE.equals(ignoreBlocked); + log.info("Notification broadcast started: user id range [{}, {}], ignoreBlocked={}", fromId, toId, skipBlocked); + int pageNumber = 0; + boolean hasNext = true; + long sent = 0; + long failed = 0; + long skippedBlocked = 0; + + while (hasNext && !stopRequested.get()) { + Pageable pageable = PageRequest.of(pageNumber, PAGE_SIZE); + Page page = userARepository.findByIdBetween(fromId, toId, pageable); + for (UserA user : page.getContent()) { + if (stopRequested.get()) break; + if (user.getTelegramId() == null) continue; + + if (skipBlocked) { + var latest = notificationAuditRepository.findTopByUserIdOrderByCreatedAtDesc(user.getId()); + if (latest.isPresent() && NotificationAudit.STATUS_FAILED.equals(latest.get().getStatus())) { + skippedBlocked++; + continue; + } + } + + TelegramSendResult result = sendOne(botToken, user.getTelegramId(), message, imageUrl, videoUrl, buttonText); + int statusCode = result.getStatusCode(); + boolean success = result.isSuccess(); + if (success) sent++; else failed++; + + NotificationAudit audit = NotificationAudit.builder() + .userId(user.getId()) + .status(success ? NotificationAudit.STATUS_SUCCESS : NotificationAudit.STATUS_FAILED) + .telegramStatusCode(statusCode != 0 ? statusCode : null) + .createdAt(Instant.now()) + .build(); + notificationAuditRepository.save(audit); + + try { + Thread.sleep(DELAY_MS_BETWEEN_SENDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.warn("Broadcast interrupted"); + return; + } + } + hasNext = page.hasNext(); + pageNumber++; + } + + if (stopRequested.get()) { + log.info("Notification broadcast stopped by request. Sent={}, Failed={}, Skipped(blocked)={}", sent, failed, skippedBlocked); + } else { + log.info("Notification broadcast finished. Sent={}, Failed={}, Skipped(blocked)={}", sent, failed, skippedBlocked); + } + } + + /** + * Normalize message so Telegram renders it correctly: literal \n and \" (e.g. from admin panel paste) + * become real newline and quote. parse_mode=HTML is sent separately. + */ + private static String normalizeMessage(String message) { + if (message == null || message.isEmpty()) return message; + return message.replace("\\n", "\n").replace("\\\"", "\""); + } + + /** + * Send one notification to a chat: text only, or photo+caption, or video+caption (video over image if both set). + * If buttonText is non-empty, adds an inline keyboard with one button (Web App -> mini app). + */ + private TelegramSendResult sendOne(String botToken, Long chatId, String message, + String imageUrl, String videoUrl, String buttonText) { + String normalizedMessage = normalizeMessage(message); + String baseUrl = "https://api.telegram.org/bot" + botToken; + Map body = new HashMap<>(); + body.put("chat_id", chatId); + body.put("parse_mode", "HTML"); + String url; + if (StringUtils.hasText(videoUrl)) { + url = baseUrl + "/sendVideo"; + body.put("video", videoUrl); + if (StringUtils.hasText(normalizedMessage)) body.put("caption", normalizedMessage); + } else if (StringUtils.hasText(imageUrl)) { + url = baseUrl + "/sendPhoto"; + body.put("photo", imageUrl); + if (StringUtils.hasText(normalizedMessage)) body.put("caption", normalizedMessage); + } else { + url = baseUrl + "/sendMessage"; + body.put("text", StringUtils.hasText(normalizedMessage) ? normalizedMessage : "(no text)"); + } + + if (StringUtils.hasText(buttonText)) { + Map webApp = new HashMap<>(); + webApp.put("url", MINI_APP_URL); + Map button = new HashMap<>(); + button.put("text", buttonText.trim()); + button.put("web_app", webApp); + body.put("reply_markup", Map.of("inline_keyboard", java.util.List.of(java.util.List.of(button)))); + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity> entity = new HttpEntity<>(body, headers); + return telegramBotApiService.postForBroadcast(url, entity); + } +} diff --git a/src/main/java/com/lottery/lottery/service/PaymentService.java b/src/main/java/com/lottery/lottery/service/PaymentService.java new file mode 100644 index 0000000..cdfae4f --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/PaymentService.java @@ -0,0 +1,449 @@ +package com.lottery.lottery.service; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.lottery.lottery.config.TelegramProperties; +import com.lottery.lottery.dto.CreatePaymentRequest; +import com.lottery.lottery.dto.DepositAddressApiRequest; +import com.lottery.lottery.dto.DepositAddressResponse; +import com.lottery.lottery.dto.DepositAddressResultDto; +import com.lottery.lottery.dto.PaymentInvoiceResponse; +import com.lottery.lottery.dto.PaymentWebhookRequest; +import com.lottery.lottery.model.CryptoDepositMethod; +import com.lottery.lottery.model.Payment; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.repository.CryptoDepositMethodRepository; +import com.lottery.lottery.repository.PaymentRepository; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.repository.UserBRepository; +import com.lottery.lottery.util.IpUtils; +import com.lottery.lottery.util.TelegramTokenRedactor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +/** + * Service for handling Telegram Stars payment operations. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class PaymentService { + + private final PaymentRepository paymentRepository; + private final UserARepository userARepository; + private final UserBRepository userBRepository; + private final CryptoDepositMethodRepository cryptoDepositMethodRepository; + private final TelegramProperties telegramProperties; + private final TransactionService transactionService; + private final LocalizationService localizationService; + private final CryptoDepositService cryptoDepositService; + private final RestTemplate restTemplate = new RestTemplate(); + + // Conversion rate: 1 Star = 9 Tickets, so in bigint: 1 Star = 9,000,000 (legacy) + private static final long STARS_TO_TICKETS_MULTIPLIER = 9_000_000L; + + // USD stored as decimal (e.g. 1.25). Tickets in DB: 1 ticket = 1_000_000; 1 USD = 1000 tickets -> ticketsAmount = round(usdAmount * 1_000_000_000) + private static final long TICKETS_DB_UNITS_PER_USD = 1_000_000_000L; + + private static final double MIN_USD = 0.01; + private static final double MAX_USD = 10_000.0; + + // Minimum and maximum stars amounts (legacy) + private static final int MIN_STARS = 50; + private static final int MAX_STARS = 100000; + + /** + * Creates a payment invoice for the user. + * Validates the stars amount and creates a pending payment record. + * + * @param userId The user ID + * @param request The payment request with stars amount + * @return Payment invoice response with order ID and amounts + */ + @Transactional + public PaymentInvoiceResponse createPaymentInvoice(Integer userId, CreatePaymentRequest request) { + Double usdAmountDouble = request.getUsdAmount(); + Integer starsAmount = request.getStarsAmount(); + + if (usdAmountDouble != null) { + validateUsd(usdAmountDouble); + BigDecimal usdAmount = BigDecimal.valueOf(usdAmountDouble).setScale(2, RoundingMode.UNNECESSARY); + long ticketsAmount = usdToTicketsAmount(usdAmountDouble); + + String orderId = generateOrderId(userId); + Payment payment = Payment.builder() + .userId(userId) + .orderId(orderId) + .starsAmount(0) + .usdAmount(usdAmount) + .ticketsAmount(ticketsAmount) + .status(Payment.PaymentStatus.PENDING) + .build(); + paymentRepository.save(payment); + log.info("Payment invoice created (crypto): orderId={}, userId={}, usdAmount={}, ticketsAmount={}", orderId, userId, usdAmount, ticketsAmount); + + return PaymentInvoiceResponse.builder() + .invoiceId(orderId) + .invoiceUrl(null) + .starsAmount(null) + .usdAmount(usdAmountDouble) + .ticketsAmount(ticketsAmount) + .build(); + } + + // Legacy payment flow no longer supported (crypto-only) + throw new IllegalArgumentException(localizationService.getMessage("payment.error.legacyNotSupported")); + } + + /** + * Creates an invoice link via Telegram Bot API for Stars payment. + * The invoice link can be used with tg.openInvoice() in the Mini App. + */ + private String createInvoiceLink(String orderId, Integer starsAmount) { + String botToken = telegramProperties.getBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Bot token is not configured"); + throw new IllegalStateException(localizationService.getMessage("payment.error.botTokenNotConfigured")); + } + + String url = "https://api.telegram.org/bot" + botToken + "/createInvoiceLink"; + // Do not log url or any string that may contain the bot token (use TelegramTokenRedactor if needed). + + // Build request body + var requestBody = new java.util.HashMap(); + requestBody.put("title", "Purchase Tickets"); + requestBody.put("description", String.format("Purchase %d Stars to receive tickets", starsAmount)); + requestBody.put("payload", orderId); // Order ID as payload + requestBody.put("provider_token", ""); // Empty for Stars + requestBody.put("currency", "XTR"); // XTR is the currency code for Stars + requestBody.put("prices", java.util.List.of( + java.util.Map.of("label", starsAmount + " Stars", "amount", starsAmount) + )); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + try { + log.debug("Creating invoice link: orderId={}, starsAmount={}", orderId, starsAmount); + ResponseEntity response = restTemplate.postForEntity( + url, + entity, + TelegramCreateInvoiceLinkResponse.class + ); + + if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) { + String invoiceUrl = response.getBody().getResult(); + log.debug("Invoice link created: orderId={}", orderId); + return invoiceUrl; + } else { + log.error("Failed to create invoice link: orderId={}, status={}", orderId, response.getStatusCode()); + throw new IllegalStateException(localizationService.getMessage("payment.error.failedToCreateInvoice")); + } + } catch (Exception e) { + log.error("Error creating invoice link: orderId={}, error={}", orderId, TelegramTokenRedactor.redact(e.getMessage()), e); + throw new IllegalStateException(localizationService.getMessage("payment.error.failedToCreateInvoice") + ": " + e.getMessage(), e); + } + } + + /** + * Processes a payment webhook from Telegram bot. + * Validates the payment and credits the user's balance. + * Ensures idempotency by checking if payment was already processed. + * + * @param request The webhook request with payment details + * @return true if payment was successfully processed, false if already processed + */ + @Transactional + public boolean processPaymentWebhook(PaymentWebhookRequest request) { + String orderId = request.getOrderId(); + Long telegramUserId = request.getTelegramUserId(); + Integer starsAmount = request.getStarsAmount(); + + // Find payment by order ID + Payment payment = paymentRepository.findByOrderId(orderId) + .orElseThrow(() -> { + log.info("Payment webhook rejected: orderId={}, reason=paymentNotFound", orderId); + return new IllegalArgumentException(localizationService.getMessage("payment.error.paymentNotFound", orderId)); + }); + + log.info("Payment webhook processing: orderId={}, paymentUserId={}, currentStatus={}, starsAmount={}", + orderId, payment.getUserId(), payment.getStatus(), starsAmount); + + // Check if payment was already processed (idempotency) + if (payment.getStatus() == Payment.PaymentStatus.COMPLETED) { + log.warn("Payment already processed: orderId={}", orderId); + return false; // Already processed + } + + // Validate payment details: Look up user by Telegram ID and compare internal user IDs + UserA user = userARepository.findByTelegramId(telegramUserId) + .orElseThrow(() -> { + log.info("Payment webhook rejected: orderId={}, reason=userNotFound, telegramUserId={}", orderId, telegramUserId); + return new IllegalArgumentException(localizationService.getMessage("payment.error.userNotFound", String.valueOf(telegramUserId))); + }); + + if (!payment.getUserId().equals(user.getId())) { + log.info("Payment webhook rejected: orderId={}, reason=userIdMismatch, paymentUserId={}, telegramUserId={} -> userId={}", + orderId, payment.getUserId(), telegramUserId, user.getId()); + throw new IllegalArgumentException(localizationService.getMessage("payment.error.userIdMismatch", + String.valueOf(payment.getUserId()), String.valueOf(user.getId()))); + } + + if (!payment.getStarsAmount().equals(starsAmount)) { + log.info("Payment webhook rejected: orderId={}, reason=starsAmountMismatch, expected={}, received={}", + orderId, payment.getStarsAmount(), starsAmount); + throw new IllegalArgumentException(localizationService.getMessage("payment.error.starsAmountMismatch", + String.valueOf(payment.getStarsAmount()), String.valueOf(starsAmount))); + } + + // Update payment status + payment.setStatus(Payment.PaymentStatus.COMPLETED); + payment.setTelegramPaymentChargeId(request.getTelegramPaymentChargeId()); + payment.setTelegramProviderPaymentChargeId(request.getTelegramProviderPaymentChargeId()); + payment.setCompletedAt(Instant.now()); + paymentRepository.save(payment); + + // Credit user balance + UserB userB = userBRepository.findById(payment.getUserId()) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + + Long ticketsAmount = payment.getTicketsAmount(); + userB.setBalanceA(userB.getBalanceA() + ticketsAmount); + + // Update deposit statistics + userB.setDepositTotal(userB.getDepositTotal() + ticketsAmount); + userB.setDepositCount(userB.getDepositCount() + 1); + // Reset total winnings since last deposit (withdrawal limit is based on this) + userB.setTotalWinAfterDeposit(0L); + + userBRepository.save(userB); + + // Create transaction record + try { + transactionService.createDepositTransaction(payment.getUserId(), ticketsAmount); + } catch (Exception e) { + log.error("Error creating deposit transaction: userId={}, amount={}", payment.getUserId(), ticketsAmount, e); + // Continue even if transaction record creation fails + } + + log.info("Payment completed: orderId={}, userId={}, ticketsAmount={}", + orderId, payment.getUserId(), ticketsAmount); + + return true; // Successfully processed + } + + /** + * Requests a crypto deposit address from the external API. Does not create any payment record. + * Used when user selects a payment method on Payment Options screen. + * + * @param userId current user id (from db_users_a) + * @param pid deposit method PID from deposit-methods + * @param usdAmount USD as decimal, e.g. 3.25 + * @return result with address, amountCoins, name, network, psId for Payment Confirmation screen + */ + public DepositAddressResultDto requestCryptoDepositAddress(Integer userId, Integer pid, Double usdAmount) { + if (pid == null) { + throw new IllegalArgumentException(localizationService.getMessage("payment.error.invalidPid")); + } + if (usdAmount == null) { + throw new IllegalArgumentException(localizationService.getMessage("payment.error.usdRange", "2", "10000")); + } + validateUsd(usdAmount); + + UserA user = userARepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException(localizationService.getMessage("user.error.notFound"))); + + String userIp = formatIpForCryptoApi(user.getIp()); + DepositAddressApiRequest.UserData userData = DepositAddressApiRequest.UserData.builder() + .internalId(user.getId()) + .screenName(user.getScreenName() != null ? user.getScreenName() : "") + .tgUsername(user.getTelegramName() != null ? user.getTelegramName() : "") + .tgId(user.getTelegramId() != null ? String.valueOf(user.getTelegramId()) : "") + .countryCode(user.getCountryCode() != null ? user.getCountryCode() : "XX") + .deviceCode(user.getDeviceCode() != null ? user.getDeviceCode() : "XX") + .languageCode(user.getLanguageCode() != null ? user.getLanguageCode() : "XX") + .userIp(userIp != null ? userIp : "0.0.0.0") + .build(); + + DepositAddressApiRequest apiRequest = DepositAddressApiRequest.builder() + .pid(pid) + .amountUsd(usdAmount) + .userData(userData) + .build(); + + DepositAddressResponse apiResponse = cryptoDepositService.postDepositAddress(apiRequest); + if (apiResponse == null || apiResponse.getResult() == null) { + throw new IllegalStateException(localizationService.getMessage("payment.error.depositAddressFailed")); + } + if (apiResponse.getRequestInfo() != null && apiResponse.getRequestInfo().getErrorCode() != null + && apiResponse.getRequestInfo().getErrorCode() != 0) { + String msg = apiResponse.getRequestInfo().getErrorMessage() != null + ? apiResponse.getRequestInfo().getErrorMessage() + : localizationService.getMessage("payment.error.depositAddressFailed"); + throw new IllegalStateException(msg); + } + + DepositAddressResponse.Result result = apiResponse.getResult(); + + log.debug("Crypto deposit address requested: userId={}, psId={}, usdAmount={}", userId, result.getPsId(), usdAmount); + + int pidForLookup = result.getPsId() != null ? result.getPsId() : pid; + Double minAmount = cryptoDepositMethodRepository.findByPid(pidForLookup) + .map(CryptoDepositMethod::getMinDepositSum) + .map(BigDecimal::doubleValue) + .orElse(null); + + return DepositAddressResultDto.builder() + .address(result.getAddress()) + .amountCoins(result.getAmountCoins()) + .name(result.getName()) + .network(result.getNetwork()) + .psId(result.getPsId()) + .minAmount(minAmount) + .build(); + } + + /** + * Format stored IP (varbinary 16) for crypto API. IPv6 -> "0.0.0.0" per spec. + */ + private static String formatIpForCryptoApi(byte[] ipBytes) { + if (ipBytes == null || ipBytes.length == 0) { + return "0.0.0.0"; + } + if (ipBytes.length == 16) { + return "0.0.0.0"; + } + String s = IpUtils.bytesToIp(ipBytes); + return s != null ? s : "0.0.0.0"; + } + + /** + * Processes a deposit completion from 3rd party webhook (e.g. crypto payment provider). + * Creates a COMPLETED payment record, credits user balance, updates deposit stats, creates DEPOSIT transaction. + * Same effect as Telegram Stars webhook completion but for USD amount. + * + * @param userId internal user id (db_users_a.id) + * @param usdAmount USD as decimal, e.g. 1.45 (3rd party sends as number) + */ + @Transactional + public void processExternalDepositCompletion(Integer userId, Double usdAmount) { + if (userId == null) { + throw new IllegalArgumentException("user_id is required"); + } + if (usdAmount == null) { + throw new IllegalArgumentException(localizationService.getMessage("payment.error.usdRange", "2", "10000")); + } + validateUsd(usdAmount); + + String orderId = "ext-deposit-" + userId + "-" + UUID.randomUUID().toString(); + + if (!userARepository.existsById(userId)) { + throw new IllegalArgumentException(localizationService.getMessage("user.error.notFound")); + } + UserB userB = userBRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException(localizationService.getMessage("user.error.balanceNotFound"))); + + long ticketsAmount = usdToTicketsAmount(usdAmount); + BigDecimal usdAmountBd = BigDecimal.valueOf(usdAmount).setScale(2, RoundingMode.HALF_UP); + Instant now = Instant.now(); + + Payment payment = Payment.builder() + .userId(userId) + .orderId(orderId) + .starsAmount(0) + .usdAmount(usdAmountBd) + .ticketsAmount(ticketsAmount) + .status(Payment.PaymentStatus.COMPLETED) + .completedAt(now) + .build(); + paymentRepository.save(payment); + + userB.setBalanceA(userB.getBalanceA() + ticketsAmount); + userB.setDepositTotal(userB.getDepositTotal() + ticketsAmount); + userB.setDepositCount(userB.getDepositCount() + 1); + userB.setTotalWinAfterDeposit(0L); + userBRepository.save(userB); + + try { + transactionService.createDepositTransaction(userId, ticketsAmount); + } catch (Exception e) { + log.error("Error creating deposit transaction: userId={}, amount={}", userId, ticketsAmount, e); + } + + log.info("External deposit completed: orderId={}, userId={}, usdAmount={}, ticketsAmount={}", orderId, userId, usdAmountBd, ticketsAmount); + } + + /** USD range 2–10000 and at most 2 decimal places. */ + private void validateUsd(double usdAmount) { + if (usdAmount < MIN_USD || usdAmount > MAX_USD) { + throw new IllegalArgumentException(localizationService.getMessage("payment.error.usdRange", "2", "10000")); + } + double rounded = Math.round(usdAmount * 100) / 100.0; + if (Math.abs(usdAmount - rounded) > 1e-9) { + throw new IllegalArgumentException(localizationService.getMessage("payment.error.usdMaxTwoDecimals")); + } + } + + /** Converts USD (e.g. 1.45) to tickets amount in DB. 1 USD = 1000 tickets; 1 ticket = 1_000_000 in DB. */ + private long usdToTicketsAmount(double usdAmount) { + return Math.round(usdAmount * TICKETS_DB_UNITS_PER_USD); + } + + /** + * Marks a payment as cancelled (e.g., user cancelled in Telegram UI). + * + * @param orderId The order ID + */ + @Transactional + public void cancelPayment(String orderId) { + Payment payment = paymentRepository.findByOrderId(orderId) + .orElseThrow(() -> new IllegalArgumentException(localizationService.getMessage("payment.error.paymentNotFound", orderId))); + + if (payment.getStatus() == Payment.PaymentStatus.COMPLETED) { + log.warn("Cannot cancel completed payment: orderId={}", orderId); + return; + } + + payment.setStatus(Payment.PaymentStatus.CANCELLED); + paymentRepository.save(payment); + + log.info("Payment cancelled: orderId={}, userId={}", orderId, payment.getUserId()); + } + + /** + * Generates a unique order ID for the payment. + * Format: payment-{userId}-{uuid} + */ + private String generateOrderId(Integer userId) { + return "payment-" + userId + "-" + UUID.randomUUID().toString(); + } + + // Response DTOs for Telegram API + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + private static class TelegramCreateInvoiceLinkResponse { + @JsonProperty("ok") + private Boolean ok; + + @JsonProperty("result") + private String result; + } +} + diff --git a/src/main/java/com/lottery/lottery/service/PayoutService.java b/src/main/java/com/lottery/lottery/service/PayoutService.java new file mode 100644 index 0000000..239873a --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/PayoutService.java @@ -0,0 +1,607 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.CreateCryptoWithdrawalRequest; +import com.lottery.lottery.dto.CreatePayoutRequest; +import com.lottery.lottery.dto.PayoutHistoryEntryDto; +import com.lottery.lottery.dto.PayoutResponse; +import com.lottery.lottery.dto.WithdrawalApiResponse; +import com.lottery.lottery.model.Payout; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.repository.PayoutRepository; +import com.lottery.lottery.repository.UserBRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.HttpStatusCodeException; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.data.domain.PageRequest; + +/** + * Service for handling payout operations. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class PayoutService { + + private final PayoutRepository payoutRepository; + private final UserBRepository userBRepository; + private final TransactionService transactionService; + private final LocalizationService localizationService; + private final CryptoWithdrawalService cryptoWithdrawalService; + private final TaskService taskService; + private final FeatureSwitchService featureSwitchService; + + // Gift type to stars amount mapping + private static final Map GIFT_TO_STARS = new HashMap<>(); + + // Gift type to total (tickets in bigint format) mapping + private static final Map GIFT_TO_TOTAL = new HashMap<>(); + + // Conversion rate for STARS payout: 1 Star = 12 Tickets, so in bigint: 1 Star = 12,000,000 + private static final long STARS_TO_TOTAL_MULTIPLIER = 12_000_000L; + + // Allowed stars amounts for STARS payout (user must pick one of these) + private static final Set ALLOWED_STARS_AMOUNTS = Set.of( + 50, 75, 100, 150, 250, 350, 500, 750, 2500, 10000, 25000, 35000 + ); + + static { + GIFT_TO_STARS.put(Payout.GiftType.HEART, 15); + GIFT_TO_STARS.put(Payout.GiftType.BEAR, 15); + GIFT_TO_STARS.put(Payout.GiftType.GIFTBOX, 25); + GIFT_TO_STARS.put(Payout.GiftType.FLOWER, 25); + GIFT_TO_STARS.put(Payout.GiftType.CAKE, 50); + GIFT_TO_STARS.put(Payout.GiftType.BOUQUET, 50); + GIFT_TO_STARS.put(Payout.GiftType.ROCKET, 50); + GIFT_TO_STARS.put(Payout.GiftType.CHAMPAGNE, 50); + GIFT_TO_STARS.put(Payout.GiftType.CUP, 100); + GIFT_TO_STARS.put(Payout.GiftType.RING, 100); + GIFT_TO_STARS.put(Payout.GiftType.DIAMOND, 100); + + // Gift type to total mapping (tickets in bigint format: tickets * 1,000,000) + // All values multiplied by 10 (e.g., 18 → 180) + GIFT_TO_TOTAL.put(Payout.GiftType.HEART, 180_000_000L); // 180 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.BEAR, 180_000_000L); // 180 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.GIFTBOX, 280_000_000L); // 280 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.FLOWER, 280_000_000L); // 280 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.CAKE, 550_000_000L); // 550 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.BOUQUET, 550_000_000L); // 550 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.ROCKET, 550_000_000L); // 550 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.CHAMPAGNE, 550_000_000L); // 550 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.CUP, 1_100_000_000L); // 1100 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.RING, 1_100_000_000L); // 1100 tickets + GIFT_TO_TOTAL.put(Payout.GiftType.DIAMOND, 1_100_000_000L); // 1100 tickets + } + + /** + * Validates and creates a payout request. + * + * @param userId User ID + * @param request Payout request data + * @return Created payout + * @throws IllegalArgumentException if validation fails + */ + @Transactional + public Payout createPayout(Integer userId, CreatePayoutRequest request) { + // Require at least one deposit before allowing withdrawal + UserB userB = userBRepository.findById(userId) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + if (Boolean.TRUE.equals(userB.getWithdrawalsDisabled())) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawalsRestrictedForAccount")); + } + if (userB.getDepositTotal() == null || userB.getDepositTotal() == 0) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.depositRequiredToWithdraw")); + } + + // Validate username pattern + validateUsername(request.getUsername()); + + // Validate and normalize quantity + Integer quantity = request.getQuantity(); + if (quantity == null) { + quantity = 1; // Default to 1 if not provided + } + validateQuantity(quantity, request.getType()); + + // For STARS type, quantity must always be 1 + if ("STARS".equalsIgnoreCase(request.getType()) && quantity != 1) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.quantityMustBeOne")); + } + + // Validate and process based on type + Payout payout; + if ("STARS".equalsIgnoreCase(request.getType())) { + // Validate that total matches stars amount * conversion rate BEFORE creating payout + validateStarsTotal(request.getStarsAmount(), request.getTotal(), quantity); + payout = createStarsPayout(userId, request, quantity); + } else if ("GIFT".equalsIgnoreCase(request.getType())) { + // Validate that total matches the gift type's expected value * quantity BEFORE creating payout + validateGiftTotal(request.getGiftName(), request.getTotal(), quantity); + payout = createGiftPayout(userId, request, quantity); + } else { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.invalidPayoutType")); + } + + // Withdrawal cannot exceed total winnings since last deposit + long maxWinAfterDeposit = userB.getTotalWinAfterDeposit() != null ? userB.getTotalWinAfterDeposit() : 0L; + if (payout.getTotal() > maxWinAfterDeposit) { + long maxTickets = maxWinAfterDeposit / 1_000_000L; + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawExceedsWinAfterDeposit", String.valueOf(maxTickets))); + } + + // Validate tickets amount and user balance + validateTicketsAmount(userId, payout.getTotal()); + + // Deduct balance and reduce total_win_after_deposit + deductBalance(userId, payout.getTotal()); + + // Save payout + payout = payoutRepository.save(payout); + + log.info("Payout created: id={}, userId={}, type={}, total={}", + payout.getId(), userId, payout.getType(), payout.getTotal()); + + return payout; + } + + /** + * Creates a CRYPTO withdrawal: calls external crypto API, then on success creates payout and deducts balance. + * Uses in-memory lock so only one withdrawal per user at a time. Keeps deposit total and maxWinAfterDeposit validation. + */ + @Transactional + public Payout createCryptoPayout(Integer userId, CreateCryptoWithdrawalRequest request) { + UserB userB = userBRepository.findById(userId) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + if (Boolean.TRUE.equals(userB.getWithdrawalsDisabled())) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawalsRestrictedForAccount")); + } + if (userB.getDepositTotal() == null || userB.getDepositTotal() == 0) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.depositRequiredToWithdraw")); + } + + Long total = request.getTotal(); + validateTicketsAmount(userId, total); + validateCryptoWithdrawalMaxTwoDecimals(total); + + long maxWinAfterDeposit = userB.getTotalWinAfterDeposit() != null ? userB.getTotalWinAfterDeposit() : 0L; + if (total > maxWinAfterDeposit) { + long maxTickets = maxWinAfterDeposit / 1_000_000L; + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawExceedsWinAfterDeposit", String.valueOf(maxTickets))); + } + + if (payoutRepository.existsByUserIdAndStatus(userId, Payout.PayoutStatus.PROCESSING)) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawalInProgress")); + } + + if (!cryptoWithdrawalService.tryAcquireWithdrawal(userId)) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawalInProgress")); + } + + try { + // Lock UserB row before calling external API so concurrent requests (e.g. from another instance) block + // and cannot double-spend. We hold the lock until the transaction commits. + userB = userBRepository.findByIdForUpdate(userId) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + if (userB.getBalanceA() < total) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.insufficientBalanceDetailed", + String.valueOf(userB.getBalanceA() / 1_000_000.0), String.valueOf(total / 1_000_000.0))); + } + long maxWin = userB.getTotalWinAfterDeposit() != null ? userB.getTotalWinAfterDeposit() : 0L; + if (total > maxWin) { + long maxTickets = maxWin / 1_000_000L; + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawExceedsWinAfterDeposit", String.valueOf(maxTickets))); + } + + double amountUsd = total / 1_000_000_000.0; + boolean noWithdrawalsYet = (userB.getWithdrawCount() != null ? userB.getWithdrawCount() : 0) == 0; + boolean manualPay = featureSwitchService.isManualPayForAllPayoutsEnabled() + || (noWithdrawalsYet && taskService.hasCompletedReferral50Or100(userId)); + WithdrawalApiResponse response; + try { + response = cryptoWithdrawalService.postWithdrawal(userId, request.getPid(), request.getWallet().trim(), amountUsd, manualPay); + } catch (HttpStatusCodeException e) { + log.warn("Crypto withdrawal API HTTP error: userId={}, status={}, body={}", userId, e.getStatusCode(), e.getResponseBodyAsString()); + throw new IllegalStateException(localizationService.getMessage("withdraw.error.tryLater")); + } + + if (response == null) { + log.warn("Crypto withdrawal API returned null body: userId={}", userId); + throw new IllegalStateException(localizationService.getMessage("withdraw.error.tryLater")); + } + if (response.getResult() != null && response.getResult().getError() != null) { + WithdrawalApiResponse.ResultError err = response.getResult().getError(); + int code = err.getErrorCode() != null ? err.getErrorCode() : -1; + if (code == 1) { + throw new IllegalArgumentException(localizationService.getMessage("withdraw.error.walletInvalidFormat")); + } + log.warn("Crypto withdrawal API business error: userId={}, error_code={}, error_text={}", userId, code, err.getErrorText()); + throw new IllegalStateException(localizationService.getMessage("withdraw.error.tryLater")); + } + if (response.getResult() == null || response.getResult().getPayment() == null) { + log.warn("Crypto withdrawal API success but no payment in result: userId={}", userId); + throw new IllegalStateException(localizationService.getMessage("withdraw.error.tryLater")); + } + + WithdrawalApiResponse.Payment payment = response.getResult().getPayment(); + if (payment.getPaymentId() == null) { + log.warn("Crypto withdrawal API returned payment with null payment_id: userId={}", userId); + throw new IllegalStateException(localizationService.getMessage("withdraw.error.tryLater")); + } + BigDecimal usdAmount = BigDecimal.valueOf(total / 1_000_000_000.0).setScale(2, RoundingMode.DOWN); + + Payout payout = Payout.builder() + .userId(userId) + .username("") + .wallet(request.getWallet().trim()) + .type(Payout.PayoutType.CRYPTO) + .giftName(null) + .cryptoName(payment.getTicker() != null ? payment.getTicker() : "") + .total(total) + .starsAmount(0) + .usdAmount(usdAmount) + .amountCoins(payment.getAmountCoins()) + .commissionCoins(payment.getComissionCoins()) + .amountToSend(payment.getAmountToSend()) + .paymentId(payment.getPaymentId()) + .quantity(1) + .status(Payout.PayoutStatus.PROCESSING) + .build(); + + applyDeductToUserB(userB, userId, total); + payout = payoutRepository.save(payout); + log.debug("Crypto payout created: id={}, userId={}, pid={}, total={}", payout.getId(), userId, request.getPid(), total); + return payout; + } finally { + cryptoWithdrawalService.releaseWithdrawal(userId); + } + } + + /** + * Marks a payout as COMPLETED: sets status, resolvedAt, updates user withdrawTotal and withdrawCount. + * Allowed only when payout status is PROCESSING or WAITING (e.g. from admin or cron sync). + */ + @Transactional + public void markPayoutCompleted(Long payoutId) { + Payout payout = payoutRepository.findById(payoutId) + .orElseThrow(() -> new IllegalArgumentException(localizationService.getMessage("payout.error.notFound", String.valueOf(payoutId)))); + if (payout.getStatus() != Payout.PayoutStatus.PROCESSING && payout.getStatus() != Payout.PayoutStatus.WAITING) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.onlyProcessingCanComplete", payout.getStatus().name())); + } + Instant now = Instant.now(); + payout.setStatus(Payout.PayoutStatus.COMPLETED); + payout.setResolvedAt(now); + payout.setUpdatedAt(now); + payoutRepository.save(payout); + + UserB userB = userBRepository.findById(payout.getUserId()) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + userB.setWithdrawTotal(userB.getWithdrawTotal() + payout.getTotal()); + userB.setWithdrawCount(userB.getWithdrawCount() + 1); + userBRepository.save(userB); + log.info("Payout completed: id={}, userId={}", payoutId, payout.getUserId()); + } + + /** + * Marks a payout as CANCELLED: sets status, resolvedAt, refunds balance, creates cancellation transaction. + * Allowed only when payout status is PROCESSING or WAITING (e.g. from admin or cron sync). + */ + @Transactional + public void markPayoutCancelled(Long payoutId) { + Payout payout = payoutRepository.findById(payoutId) + .orElseThrow(() -> new IllegalArgumentException(localizationService.getMessage("payout.error.notFound", String.valueOf(payoutId)))); + if (payout.getStatus() != Payout.PayoutStatus.PROCESSING && payout.getStatus() != Payout.PayoutStatus.WAITING) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.onlyProcessingCanCancel", payout.getStatus().name())); + } + Instant now = Instant.now(); + payout.setStatus(Payout.PayoutStatus.CANCELLED); + payout.setResolvedAt(now); + payout.setUpdatedAt(now); + payoutRepository.save(payout); + + UserB userB = userBRepository.findById(payout.getUserId()) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + userB.setBalanceA(userB.getBalanceA() + payout.getTotal()); + userBRepository.save(userB); + + transactionService.createCancellationOfWithdrawalTransaction(payout.getUserId(), payout.getTotal(), now); + log.info("Payout cancelled: id={}, userId={}", payoutId, payout.getUserId()); + } + + /** + * Creates a STARS type payout. + * Stars amount must be one of the allowed values; total is calculated as stars * 12 (in bigint). + */ + private Payout createStarsPayout(Integer userId, CreatePayoutRequest request, Integer quantity) { + Integer starsAmount = request.getStarsAmount(); + if (starsAmount == null || starsAmount < 0) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.starsAmountNonNegative")); + } + if (!ALLOWED_STARS_AMOUNTS.contains(starsAmount)) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.starsAmountNotAllowed")); + } + + // Calculate expected total from stars amount (quantity is always 1 for STARS) + long expectedTotal = (long) starsAmount * STARS_TO_TOTAL_MULTIPLIER; + + return Payout.builder() + .userId(userId) + .username(request.getUsername()) + .type(Payout.PayoutType.STARS) + .giftName(null) + .total(expectedTotal) // Use calculated total, not from request + .starsAmount(request.getStarsAmount()) + .quantity(1) // Always 1 for STARS + .status(Payout.PayoutStatus.PROCESSING) + .build(); + } + + /** + * Validates that stars amount is allowed and total matches stars * 12 (in bigint). + * Quantity is always 1 for STARS type. + */ + private void validateStarsTotal(Integer starsAmount, Long total, Integer quantity) { + if (starsAmount == null || total == null) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.starsAmountAndTotalRequired")); + } + if (!ALLOWED_STARS_AMOUNTS.contains(starsAmount)) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.starsAmountNotAllowed")); + } + if (quantity == null || quantity != 1) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.quantityMustBeOne")); + } + + long expectedTotal = (long) starsAmount * STARS_TO_TOTAL_MULTIPLIER; + if (!total.equals(expectedTotal)) { + throw new IllegalArgumentException( + localizationService.getMessage("payout.error.totalMustEqualStars", + String.valueOf(expectedTotal), String.valueOf(total))); + } + } + + /** + * Creates a GIFT type payout. + */ + private Payout createGiftPayout(Integer userId, CreatePayoutRequest request, Integer quantity) { + // Validate gift name + if (request.getGiftName() == null || request.getGiftName().isEmpty()) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.giftNameRequired")); + } + + Payout.GiftType giftType; + try { + giftType = Payout.GiftType.valueOf(request.getGiftName().toUpperCase()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.invalidGiftName", request.getGiftName())); + } + + // Calculate stars amount from gift type + Integer starsAmount = GIFT_TO_STARS.get(giftType); + if (starsAmount == null) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.starsAmountNotDefined", giftType.name())); + } + + // Get expected total for this gift type (single gift) + Long singleGiftTotal = GIFT_TO_TOTAL.get(giftType); + if (singleGiftTotal == null) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.totalNotDefined", giftType.name())); + } + + // Calculate total with quantity + long expectedTotal = singleGiftTotal * quantity; + + return Payout.builder() + .userId(userId) + .username(request.getUsername()) + .type(Payout.PayoutType.GIFT) + .giftName(giftType) + .total(expectedTotal) // Use calculated total (single gift * quantity) + .starsAmount(starsAmount * quantity) // Stars amount also multiplied by quantity + .quantity(quantity) + .status(Payout.PayoutStatus.PROCESSING) + .build(); + } + + /** + * Validates that the total matches the expected value for the gift type * quantity. + */ + private void validateGiftTotal(String giftName, Long total, Integer quantity) { + if (giftName == null || giftName.isEmpty() || total == null || quantity == null) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.giftNameTotalQuantityRequired")); + } + + Payout.GiftType giftType; + try { + giftType = Payout.GiftType.valueOf(giftName.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.invalidGiftName", giftName)); + } + + Long singleGiftTotal = GIFT_TO_TOTAL.get(giftType); + if (singleGiftTotal == null) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.totalNotDefined", giftType.name())); + } + + long expectedTotal = singleGiftTotal * quantity; + + if (!total.equals(expectedTotal)) { + throw new IllegalArgumentException( + localizationService.getMessage("payout.error.totalForGiftMismatch", + giftType.name(), String.valueOf(quantity), String.valueOf(expectedTotal), String.valueOf(total))); + } + } + + /** + * Validates quantity is between 1 and 100. + */ + private void validateQuantity(Integer quantity, String type) { + if (quantity == null) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.quantityRequired")); + } + + if (quantity < 1 || quantity > 100) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.quantityRange")); + } + } + + /** + * Validates username pattern. + */ + private void validateUsername(String username) { + if (username == null || username.isEmpty()) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.usernameRequired")); + } + + // Username should start with @ followed by at least 1 English letter + if (!username.matches("^@[a-zA-Z].*")) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.usernamePattern")); + } + } + + /** Units per 0.01 tickets when 1 ticket = 1_000_000. Ensures withdrawal amount has at most 2 decimal places. */ + private static final long CRYPTO_WITHDRAWAL_MIN_STEP = 10_000L; + + /** + * Validates that crypto withdrawal total has at most 2 decimal places (e.g. 125.25 allowed, 125.125 not). + * Total is in units where 1 ticket = 1_000_000, so 0.01 tickets = 10_000. + */ + private void validateCryptoWithdrawalMaxTwoDecimals(Long total) { + if (total != null && total % CRYPTO_WITHDRAWAL_MIN_STEP != 0) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.withdrawalAmountMaxTwoDecimals")); + } + } + + /** + * Validates tickets amount and user balance. + */ + private void validateTicketsAmount(Integer userId, Long total) { + if (total == null || total < 0) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.ticketsAmountNonNegative")); + } + + Optional userBOpt = userBRepository.findById(userId); + if (userBOpt.isEmpty()) { + throw new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound")); + } + + UserB userB = userBOpt.get(); + if (userB.getBalanceA() < total) { + throw new IllegalArgumentException(localizationService.getMessage("payout.error.insufficientBalanceDetailed", + String.valueOf(userB.getBalanceA() / 1_000_000.0), String.valueOf(total / 1_000_000.0))); + } + } + + /** + * Deducts balance from user's balance_a. Uses pessimistic lock (SELECT FOR UPDATE) on the UserB row + * so that concurrent withdrawal requests for the same user are serialized and cannot double-spend. + */ + private void deductBalance(Integer userId, Long total) { + UserB userB = userBRepository.findByIdForUpdate(userId) + .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound"))); + applyDeductToUserB(userB, userId, total); + } + + /** + * Applies balance and totalWinAfterDeposit deduction to an already-loaded (and locked) UserB. + * Caller must hold a pessimistic lock on the UserB row (e.g. from findByIdForUpdate). + */ + private void applyDeductToUserB(UserB userB, Integer userId, Long total) { + if (userB.getBalanceA() < total) { + throw new IllegalStateException(localizationService.getMessage("payout.error.insufficientBalance")); + } + userB.setBalanceA(userB.getBalanceA() - total); + long currentWinAfterDeposit = userB.getTotalWinAfterDeposit() != null ? userB.getTotalWinAfterDeposit() : 0L; + userB.setTotalWinAfterDeposit(Math.max(0L, currentWinAfterDeposit - total)); + userBRepository.save(userB); + + try { + transactionService.createWithdrawalTransaction(userId, total); + } catch (Exception e) { + log.error("Error creating withdrawal transaction: userId={}, amount={}", userId, total, e); + } + log.info("Balance deducted for payout: userId={}, amount={}, newBalance={}", userId, total, userB.getBalanceA()); + } + + /** + * Converts Payout entity to PayoutResponse DTO. + */ + public PayoutResponse toResponse(Payout payout) { + return PayoutResponse.builder() + .id(payout.getId()) + .username(payout.getUsername()) + .type(payout.getType().name()) + .giftName(payout.getGiftName() != null ? payout.getGiftName().name() : null) + .total(payout.getTotal()) + .starsAmount(payout.getStarsAmount()) + .quantity(payout.getQuantity()) + .status(payout.getStatus().name()) + .createdAt(payout.getCreatedAt() != null ? payout.getCreatedAt().toEpochMilli() : null) + .resolvedAt(payout.getResolvedAt() != null ? payout.getResolvedAt().toEpochMilli() : null) + .build(); + } + + /** + * Gets the last 20 payouts for the current user as history entries. + * Uses index for optimal performance. + * + * @param userId User ID + * @param timezone Optional timezone (e.g., "Europe/London"). If null, uses UTC. + * @param languageCode User's language code for localization (e.g., "EN", "RU") + */ + public List getUserPayoutHistory(Integer userId, String timezone, String languageCode) { + List payouts = payoutRepository.findLastPayoutsByUserId( + userId, + PageRequest.of(0, 20) + ); + + // Determine timezone to use + ZoneId zoneId; + try { + zoneId = (timezone != null && !timezone.trim().isEmpty()) + ? ZoneId.of(timezone) + : ZoneId.of("UTC"); + } catch (Exception e) { + // Invalid timezone, fallback to UTC + zoneId = ZoneId.of("UTC"); + } + + // Get localized "at" word + String atWord = localizationService.getMessage("dateTime.at", languageCode); + if (atWord == null || atWord.isEmpty()) { + atWord = "at"; // Fallback to English + } + + // Create formatter with localized "at" word + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd.MM '" + atWord + "' HH:mm") + .withZone(zoneId); + + return payouts.stream() + .map(payout -> { + // Format date + String date = dateFormatter.format(payout.getCreatedAt()); + + // Status + String status = payout.getStatus().name(); + + return PayoutHistoryEntryDto.builder() + .amount(payout.getTotal()) // Return raw bigint value + .date(date) + .status(status) + .build(); + }) + .collect(Collectors.toList()); + } +} + diff --git a/src/main/java/com/lottery/lottery/service/PersonaBetDecisionService.java b/src/main/java/com/lottery/lottery/service/PersonaBetDecisionService.java new file mode 100644 index 0000000..8012a6d --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/PersonaBetDecisionService.java @@ -0,0 +1,137 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.exception.BetDecisionException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Bet decision using persona + loss streak and zone logic (no external API). + * Zones are defined as percentages of the room/bot range [min, max]; actual ticket + * bounds are computed from (minPct, maxPct) so the same behaviour applies for any range. + */ +@Slf4j +@Service +public class PersonaBetDecisionService implements BetDecisionService { + + private static final String LOSS = "L"; + + @Override + public long decideBetAmountTickets(BotBetContext context) { + long botMin = context.getBotMinTickets(); + long botMax = context.getBotMaxTickets(); + if (botMin <= 0 || botMax < botMin) { + throw new BetDecisionException("Invalid bot range [" + botMin + ", " + botMax + "]"); + } + + String persona = context.getConfig() != null && context.getConfig().getPersona() != null + ? context.getConfig().getPersona().trim().toLowerCase() : "balanced"; + List results = context.getLastResults10(); + if (results == null) results = List.of(); + + int streak = countConsecutiveLossesFromEnd(results); + long[] zone = getZoneTicketsForPersonaAndStreak(persona, streak, botMin, botMax); + long zoneMin = zone[0]; + long zoneMax = zone[1]; + + // Clamp zone to bot range + long clampedMin = Math.max(zoneMin, botMin); + long clampedMax = Math.min(zoneMax, botMax); + if (clampedMin > clampedMax) { + clampedMin = botMin; + clampedMax = botMax; + } + + // Pick random in [clampedMin, clampedMax] for variety + long rawBet = clampedMin == clampedMax ? clampedMin + : clampedMin + ThreadLocalRandom.current().nextLong(clampedMax - clampedMin + 1); + + long step = getStep(botMin); + long bet = roundToStep(rawBet, botMin, botMax, step); + + log.debug("Persona bet decision: persona={}, streak={}, zone=[{}, {}] -> {} tickets", persona, streak, zoneMin, zoneMax, bet); + return bet; + } + + /** Step 1 if min < 10, step 10 if 10 <= min < 1000, step 100 if min >= 1000. */ + private static long getStep(long botMin) { + if (botMin >= 1000) return 100; + if (botMin >= 10) return 10; + return 1; + } + + /** Count consecutive L from the end of last results (oldest → newest). */ + private static int countConsecutiveLossesFromEnd(List results) { + int count = 0; + for (int i = results.size() - 1; i >= 0; i--) { + if (LOSS.equals(results.get(i))) count++; + else break; + } + return count; + } + + /** + * Zone rule: when loss streak >= streakThreshold, use [minPct, maxPct] of range (0 = min, 100 = max). + * Rules are evaluated in descending streak order (highest threshold first). + */ + private static final class ZoneRule { + final int streakThreshold; + final double minPct; + final double maxPct; + + ZoneRule(int streakThreshold, double minPct, double maxPct) { + this.streakThreshold = streakThreshold; + this.minPct = minPct; + this.maxPct = maxPct; + } + } + + // Conservative: usually 1–10%, streak 5 → 19–24%, streak 7 → 39–50% + private static final List CONSERVATIVE_RULES = List.of( + new ZoneRule(7, 45, 68), + new ZoneRule(5, 17, 28), + new ZoneRule(0, 1, 10) + ); + // Balanced: usually 5–15%, streak 3 → 29–39%, streak 5 → 59–69% + private static final List BALANCED_RULES = List.of( + new ZoneRule(5, 64, 79), + new ZoneRule(3, 25, 37), + new ZoneRule(0, 2, 12) + ); + // Aggressive: usually 15–25%, streak 2 → 39–50%, streak 3 → 79–100% + private static final List AGGRESSIVE_RULES = List.of( + new ZoneRule(3, 79, 100), + new ZoneRule(2, 33, 42), + new ZoneRule(0, 5, 15) + ); + + /** Returns [zoneMinTickets, zoneMaxTickets] from percentage-of-range rules for persona and streak. */ + private static long[] getZoneTicketsForPersonaAndStreak(String persona, int streak, long rangeMin, long rangeMax) { + List rules = switch (persona) { + case "conservative" -> CONSERVATIVE_RULES; + case "aggressive" -> AGGRESSIVE_RULES; + default -> BALANCED_RULES; + }; + ZoneRule rule = rules.stream() + .filter(r -> streak >= r.streakThreshold) + .findFirst() + .orElse(rules.get(rules.size() - 1)); + long range = rangeMax - rangeMin; + long zoneMin = rangeMin + Math.round(range * rule.minPct / 100.0); + long zoneMax = rangeMin + Math.round(range * rule.maxPct / 100.0); + zoneMin = Math.max(rangeMin, Math.min(zoneMin, rangeMax)); + zoneMax = Math.max(rangeMin, Math.min(zoneMax, rangeMax)); + if (zoneMin > zoneMax) zoneMin = zoneMax; + return new long[]{zoneMin, zoneMax}; + } + + /** Round value to nearest valid step in [min, max]. */ + private static long roundToStep(long value, long min, long max, long step) { + long offset = value - min; + long steps = Math.round((double) offset / step); + long rounded = min + steps * step; + return Math.max(min, Math.min(max, rounded)); + } +} diff --git a/src/main/java/com/lottery/lottery/service/PromotionService.java b/src/main/java/com/lottery/lottery/service/PromotionService.java new file mode 100644 index 0000000..efe8132 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/PromotionService.java @@ -0,0 +1,121 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.Promotion; +import com.lottery.lottery.model.Promotion.PromotionType; +import com.lottery.lottery.model.PromotionUser; +import com.lottery.lottery.repository.PromotionRepository; +import com.lottery.lottery.repository.PromotionUserRepository; +import com.lottery.lottery.repository.UserDRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Instant; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PromotionService { + + /** 1 ticket = 1_000_000 in balance/bet storage; points are stored as ticket count with 2 decimals. */ + private static final long TICKETS_MULTIPLIER = 1_000_000L; + + private final PromotionRepository promotionRepository; + private final PromotionUserRepository promotionUserRepository; + private final UserDRepository userDRepository; + + /** + * Add net win points to the user for all currently active NET_WIN promotions. + * Called when a user wins a round: netWinBigint = payout - winnerBet (in bigint). + * Points are stored as ticket count with 2 decimal places (e.g. 82.25 tickets → points += 82.25). + */ + @Transactional + public void addNetWinPoints(int userId, long netWinBigint) { + if (netWinBigint <= 0) { + return; + } + BigDecimal pointsToAdd = BigDecimal.valueOf(netWinBigint) + .divide(BigDecimal.valueOf(TICKETS_MULTIPLIER), 2, RoundingMode.HALF_UP); + if (pointsToAdd.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + Instant now = Instant.now(); + List activePromos = promotionRepository.findActiveByTypeAndTimeRange(PromotionType.NET_WIN, now); + for (Promotion promo : activePromos) { + addPointsToPromoUser(promo.getId(), userId, pointsToAdd, now); + } + } + + /** + * Add net win points for all active NET_WIN_MAX_BET promotions (only when winner made max bet in the room). + * Same points logic as NET_WIN: netWinBigint = payout - winnerBet. + */ + @Transactional + public void addNetWinMaxBetPoints(int userId, long netWinBigint) { + if (netWinBigint <= 0) { + return; + } + BigDecimal pointsToAdd = BigDecimal.valueOf(netWinBigint) + .divide(BigDecimal.valueOf(TICKETS_MULTIPLIER), 2, RoundingMode.HALF_UP); + if (pointsToAdd.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + Instant now = Instant.now(); + List activePromos = promotionRepository.findActiveByTypeAndTimeRange(PromotionType.NET_WIN_MAX_BET, now); + for (Promotion promo : activePromos) { + addPointsToPromoUser(promo.getId(), userId, pointsToAdd, now); + } + } + + /** + * Add 1 point for the referer (level 1) in active REF_COUNT promotions where the referral's + * registration date falls within the promotion timeframe. + * Called when a referral completes their first round (rounds_played was 0 before this round). + * Only promotions for which referralRegistrationTime is between startTime and endTime (inclusive) get the point. + * Masters (users where userId = masterId) are excluded and never receive REF_COUNT points. + */ + @Transactional + public void addRefCountPoints(Integer refererUserId, Instant referralRegistrationTime) { + if (refererUserId == null || refererUserId <= 0 || referralRegistrationTime == null) { + return; + } + // Exclude masters: do not add REF_COUNT points for referrers who are masters (id = masterId and masterId > 0) + if (userDRepository.findById(refererUserId) + .filter(ud -> ud.getMasterId() != null && ud.getMasterId() > 0 && refererUserId.equals(ud.getMasterId())) + .isPresent()) { + return; + } + BigDecimal onePoint = BigDecimal.ONE; + Instant now = Instant.now(); + List activePromos = promotionRepository.findActiveByTypeAndTimeRange(PromotionType.REF_COUNT, now); + for (Promotion promo : activePromos) { + // Only add point if the referral was registered during this promotion's timeframe + if (!referralRegistrationTime.isBefore(promo.getStartTime()) && !referralRegistrationTime.isAfter(promo.getEndTime())) { + addPointsToPromoUser(promo.getId(), refererUserId, onePoint, now); + } + } + } + + private void addPointsToPromoUser(int promoId, int userId, BigDecimal pointsToAdd, Instant now) { + promotionUserRepository.findByPromoIdAndUserId(promoId, userId) + .ifPresentOrElse( + pu -> { + pu.setPoints(pu.getPoints().add(pointsToAdd)); + promotionUserRepository.save(pu); + }, + () -> { + PromotionUser newPu = PromotionUser.builder() + .promoId(promoId) + .userId(userId) + .points(pointsToAdd) + .updatedAt(now) + .build(); + promotionUserRepository.save(newPu); + } + ); + } +} diff --git a/src/main/java/com/lottery/lottery/service/PublicPromotionService.java b/src/main/java/com/lottery/lottery/service/PublicPromotionService.java new file mode 100644 index 0000000..4b63ba1 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/PublicPromotionService.java @@ -0,0 +1,135 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.PromotionDetailDto; +import com.lottery.lottery.dto.PromotionLeaderboardEntryDto; +import com.lottery.lottery.dto.PromotionListItemDto; +import com.lottery.lottery.model.Promotion; +import com.lottery.lottery.model.Promotion.PromotionStatus; +import com.lottery.lottery.model.PromotionReward; +import com.lottery.lottery.model.PromotionUser; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.PromotionRepository; +import com.lottery.lottery.repository.PromotionRewardRepository; +import com.lottery.lottery.repository.PromotionUserRepository; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.security.UserContext; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class PublicPromotionService { + + private static final int LEADERBOARD_SIZE = 30; + private static final long TICKETS_MULTIPLIER = 1_000_000L; + + private final PromotionRepository promotionRepository; + private final PromotionUserRepository promotionUserRepository; + private final PromotionRewardRepository promotionRewardRepository; + private final UserARepository userARepository; + + /** + * List promotions for app: ACTIVE and FINISHED only (exclude INACTIVE and PLANNED). + */ + @Transactional(readOnly = true) + public List listForApp() { + List allowed = List.of(PromotionStatus.ACTIVE, PromotionStatus.FINISHED); + List list = promotionRepository.findByStatusInOrderByStartTimeDesc(allowed); + return list.stream().map(this::toListItemDto).collect(Collectors.toList()); + } + + /** + * Get promotion detail for app. Returns empty if not found or status is INACTIVE or PLANNED. + */ + @Transactional(readOnly = true) + public Optional getDetailForApp(int promotionId) { + Optional promoOpt = promotionRepository.findById(promotionId); + if (promoOpt.isEmpty()) { + return Optional.empty(); + } + Promotion promo = promoOpt.get(); + if (promo.getStatus() == PromotionStatus.INACTIVE || promo.getStatus() == PromotionStatus.PLANNED) { + return Optional.empty(); + } + + int userId = UserContext.get().getId(); + + List topUsers = promotionUserRepository.findByPromoId( + promo.getId(), + PageRequest.of(0, LEADERBOARD_SIZE, Sort.by(Sort.Direction.DESC, "points")) + ).getContent(); + + List rewards = promotionRewardRepository.findByPromotionIdOrderByPlaceAsc(promo.getId()); + Map rewardTicketsByPlace = rewards.stream() + .collect(Collectors.toMap(PromotionReward::getPlace, r -> r.getReward() / TICKETS_MULTIPLIER)); + + List userIds = topUsers.stream().map(PromotionUser::getUserId).distinct().toList(); + Map screenNameByUserId = userARepository.findAllById(userIds).stream() + .collect(Collectors.toMap(UserA::getId, u -> u.getScreenName() != null ? u.getScreenName() : "-")); + + List leaderboard = new ArrayList<>(); + for (int i = 0; i < LEADERBOARD_SIZE; i++) { + int place = i + 1; + String screenName = "-"; + BigDecimal points = BigDecimal.ZERO; + Long rewardTickets = rewardTicketsByPlace.get(place); + if (i < topUsers.size()) { + PromotionUser pu = topUsers.get(i); + screenName = screenNameByUserId.getOrDefault(pu.getUserId(), "-"); + points = pu.getPoints() != null ? pu.getPoints() : BigDecimal.ZERO; + } + leaderboard.add(PromotionLeaderboardEntryDto.builder() + .place(place) + .screenName(screenName) + .points(points) + .rewardTickets(rewardTickets) + .build()); + } + + int totalParticipants = (int) promotionUserRepository.countByPromoId(promo.getId()); + Optional currentUserEntry = promotionUserRepository.findByPromoIdAndUserId(promo.getId(), userId); + BigDecimal userPoints = currentUserEntry.map(pu -> pu.getPoints() != null ? pu.getPoints() : BigDecimal.ZERO).orElse(BigDecimal.ZERO); + + int userPosition = 0; + if (currentUserEntry.isPresent()) { + BigDecimal myPoints = currentUserEntry.get().getPoints(); + long countBetter = promotionUserRepository.countByPromoIdAndPointsGreaterThan(promo.getId(), myPoints != null ? myPoints : BigDecimal.ZERO); + userPosition = (int) countBetter + 1; + } + + PromotionDetailDto dto = PromotionDetailDto.builder() + .id(promo.getId()) + .type(promo.getType().name()) + .status(promo.getStatus().name()) + .startTime(promo.getStartTime()) + .endTime(promo.getEndTime()) + .totalReward(promo.getTotalReward()) + .leaderboard(leaderboard) + .userPosition(userPosition) + .userTotal(totalParticipants) + .userPoints(userPoints) + .build(); + return Optional.of(dto); + } + + private PromotionListItemDto toListItemDto(Promotion p) { + return PromotionListItemDto.builder() + .id(p.getId()) + .type(p.getType().name()) + .status(p.getStatus().name()) + .startTime(p.getStartTime()) + .endTime(p.getEndTime()) + .totalReward(p.getTotalReward()) + .build(); + } +} diff --git a/src/main/java/com/lottery/lottery/service/ReferralCommissionService.java b/src/main/java/com/lottery/lottery/service/ReferralCommissionService.java new file mode 100644 index 0000000..c66f06b --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/ReferralCommissionService.java @@ -0,0 +1,289 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.model.Transaction; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.model.UserD; +import com.lottery.lottery.repository.TransactionRepository; +import com.lottery.lottery.repository.UserBRepository; +import com.lottery.lottery.repository.UserDRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashSet; +import java.util.Set; + +/** + * Service for handling referral commission logic. + * Processes commissions for referers when their referrals win or lose game rounds. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ReferralCommissionService { + + private final UserDRepository userDRepository; + private final UserBRepository userBRepository; + private final TransactionRepository transactionRepository; + + // Commission rates (as percentages) + private static final double WIN_PROFIT_COMMISSION_RATE = 0.01; // 1% of net profit for winners (all levels) + // Loss commission rates per referrer level + private static final double LOSS_COMMISSION_RATE_LEVEL1 = 0.04; // 4% of loss for referrer 1 + private static final double LOSS_COMMISSION_RATE_LEVEL2 = 0.02; // 2% of loss for referrer 2 + private static final double LOSS_COMMISSION_RATE_LEVEL3 = 0.01; // 1% of loss for referrer 3 + + /** + * Processes referral commissions for a user who won a round. + * + * @param userId The user who won + * @param userBet The user's bet amount (in bigint format) + * @param totalBet The total bet of the round (in bigint format) + * @param houseCommission The house commission amount (20% of totalBet - userBet, in bigint format) + * @return Set of referer user IDs who received commissions (for balance update notifications) + */ + @Transactional + public Set processWinnerCommissions(Integer userId, Long userBet, Long totalBet, Long houseCommission) { + Set refererIds = new HashSet<>(); + // Calculate user's net profit: (totalBet - houseCommission) - userBet + // This is the actual profit after the house takes its 20% commission + long userProfit = totalBet - houseCommission - userBet; + + if (userProfit <= 0) { + log.debug("No profit to distribute for winner userId={}, userProfit={}", userId, userProfit); + return refererIds; + } + + // Calculate referral commission amount: 1% of user's net profit + long commissionAmount = (long) (userProfit * WIN_PROFIT_COMMISSION_RATE); + + if (commissionAmount <= 0) { + log.debug("Commission amount too small for winner userId={}, commissionAmount={}", userId, commissionAmount); + return refererIds; + } + + log.info("Processing winner commissions: userId={}, userProfit={}, houseCommission={}, commissionAmount={}", + userId, userProfit, houseCommission, commissionAmount); + + // Get user's referral chain + UserD userD = userDRepository.findById(userId).orElse(null); + if (userD == null) { + log.warn("UserD not found for userId={}, skipping referral commissions", userId); + return refererIds; + } + + // Process commissions for referer_1, referer_2, referer_3 + Integer referer1 = processRefererCommission(userD.getRefererId1(), commissionAmount, 1, userId, true); + if (referer1 != null) refererIds.add(referer1); + + Integer referer2 = processRefererCommission(userD.getRefererId2(), commissionAmount, 2, userId, true); + if (referer2 != null) refererIds.add(referer2); + + Integer referer3 = processRefererCommission(userD.getRefererId3(), commissionAmount, 3, userId, true); + if (referer3 != null) refererIds.add(referer3); + + return refererIds; + } + + /** + * Processes referral commissions for a user who lost a round. + * + * @param userId The user who lost + * @param userBet The user's bet amount (in bigint format) + * @return Set of referer user IDs who received commissions (for balance update notifications) + */ + @Transactional + public Set processLoserCommissions(Integer userId, Long userBet) { + Set refererIds = new HashSet<>(); + if (userBet <= 0) { + log.debug("No bet to process commissions for loser userId={}, userBet={}", userId, userBet); + return refererIds; + } + + log.info("Processing loser commissions: userId={}, userBet={}, rates: level1={}%, level2={}%, level3={}%", + userId, userBet, LOSS_COMMISSION_RATE_LEVEL1 * 100, LOSS_COMMISSION_RATE_LEVEL2 * 100, LOSS_COMMISSION_RATE_LEVEL3 * 100); + + // Get user's referral chain + UserD userD = userDRepository.findById(userId).orElse(null); + if (userD == null) { + log.warn("UserD not found for userId={}, skipping referral commissions", userId); + return refererIds; + } + + // Process commissions for referer_1, referer_2, referer_3 with different rates + // Referrer 1: 4% of user's bet + long commissionAmount1 = (long) (userBet * LOSS_COMMISSION_RATE_LEVEL1); + if (commissionAmount1 > 0) { + Integer referer1 = processRefererCommission(userD.getRefererId1(), commissionAmount1, 1, userId, false); + if (referer1 != null) refererIds.add(referer1); + } + + // Referrer 2: 2% of user's bet + long commissionAmount2 = (long) (userBet * LOSS_COMMISSION_RATE_LEVEL2); + if (commissionAmount2 > 0) { + Integer referer2 = processRefererCommission(userD.getRefererId2(), commissionAmount2, 2, userId, false); + if (referer2 != null) refererIds.add(referer2); + } + + // Referrer 3: 1% of user's bet + long commissionAmount3 = (long) (userBet * LOSS_COMMISSION_RATE_LEVEL3); + if (commissionAmount3 > 0) { + Integer referer3 = processRefererCommission(userD.getRefererId3(), commissionAmount3, 3, userId, false); + if (referer3 != null) refererIds.add(referer3); + } + + return refererIds; + } + + /** + * Processes commission for a single referer. + * + * @param refererId The referer's user ID (0 if no referer) + * @param commissionAmount The commission amount to award (in bigint format) + * @param refererLevel The referer level (1, 2, or 3) + * @param userId The user who triggered the commission + * @param isWinner Whether the user won (true) or lost (false) + * @return The referer ID if commission was processed, null otherwise + */ + private Integer processRefererCommission(Integer refererId, Long commissionAmount, int refererLevel, + Integer userId, boolean isWinner) { + // Skip if no referer + if (refererId == null || refererId <= 0) { + return null; + } + + try { + // Get referer's UserB (balance) and UserD (referral stats) + UserB refererBalance = userBRepository.findById(refererId).orElse(null); + UserD refererD = userDRepository.findById(refererId).orElse(null); + + if (refererBalance == null || refererD == null) { + log.warn("Referer not found: refererId={}, refererLevel={}, userId={}", + refererId, refererLevel, userId); + return null; + } + + // Credit referer's balance + refererBalance.setBalanceA(refererBalance.getBalanceA() + commissionAmount); + userBRepository.save(refererBalance); + + // Update referer's from_referals_X (they earned from their referral) + switch (refererLevel) { + case 1: + refererD.setFromReferals1(refererD.getFromReferals1() + commissionAmount); + break; + case 2: + refererD.setFromReferals2(refererD.getFromReferals2() + commissionAmount); + break; + case 3: + refererD.setFromReferals3(refererD.getFromReferals3() + commissionAmount); + break; + } + userDRepository.save(refererD); + + // Update user's to_referer_X (they paid commission to their referer) + UserD userD = userDRepository.findById(userId).orElse(null); + if (userD != null) { + switch (refererLevel) { + case 1: + userD.setToReferer1(userD.getToReferer1() + commissionAmount); + break; + case 2: + userD.setToReferer2(userD.getToReferer2() + commissionAmount); + break; + case 3: + userD.setToReferer3(userD.getToReferer3() + commissionAmount); + break; + } + userDRepository.save(userD); + } + + log.info("Commission processed: refererId={}, refererLevel={}, userId={}, " + + "commissionAmount={}, isWinner={}", + refererId, refererLevel, userId, commissionAmount, isWinner); + + return refererId; + + } catch (Exception e) { + log.error("Error processing commission for refererId={}, refererLevel={}, userId={}", + refererId, refererLevel, userId, e); + // Continue processing other referers even if one fails + return null; + } + } + + /** + * Checks if the next bet will be the user's 3rd bet. + * + * @param userId The user who is about to place a bet + * @return true if this will be the 3rd bet, false otherwise + */ + public boolean willBeThirdBet(Integer userId) { + try { + // Get user's rounds_played count from db_users_b + UserB userB = userBRepository.findById(userId).orElse(null); + if (userB == null) { + log.warn("UserB not found for userId={}", userId); + return false; + } + // If current rounds_played is 2, the next round will be the 3rd + return userB.getRoundsPlayed() == 2; + } catch (Exception e) { + log.error("Error checking rounds_played for userId={}", userId, e); + return false; + } + } + + /** + * Gives a one-time bonus of 1 ticket to referrer 1 when user places their 3rd bet. + * + * @param userId The user who placed their 3rd bet + * @return The referrer 1 user ID if bonus was given, null otherwise + */ + @Transactional + public Integer giveThirdBetBonus(Integer userId) { + try { + // Get user's referral chain + UserD userD = userDRepository.findById(userId).orElse(null); + if (userD == null || userD.getRefererId1() == null || userD.getRefererId1() <= 0) { + log.debug("No referrer 1 for userId={}, skipping 3rd bet bonus", userId); + return null; + } + + Integer referer1Id = userD.getRefererId1(); + + // Get referrer's balance and referral stats + UserB refererBalance = userBRepository.findById(referer1Id).orElse(null); + UserD refererD = userDRepository.findById(referer1Id).orElse(null); + + if (refererBalance == null || refererD == null) { + log.warn("Referrer 1 not found: refererId={}, userId={}", referer1Id, userId); + return null; + } + + // Give 1 ticket bonus (1,000,000 in bigint format) + long bonusAmount = 1_000_000L; + refererBalance.setBalanceA(refererBalance.getBalanceA() + bonusAmount); + userBRepository.save(refererBalance); + + // Update referrer's from_referals_1 (they earned from their referral) + refererD.setFromReferals1(refererD.getFromReferals1() + bonusAmount); + userDRepository.save(refererD); + + // Update user's to_referer_1 (they gave bonus to their referrer) + userD.setToReferer1(userD.getToReferer1() + bonusAmount); + userDRepository.save(userD); + + log.info("3rd bet bonus given: userId={}, referer1Id={}, bonusAmount={}", + userId, referer1Id, bonusAmount); + + return referer1Id; + } catch (Exception e) { + log.error("Error giving 3rd bet bonus for userId={}", userId, e); + // Don't throw - this is a bonus, shouldn't break the main flow + return null; + } + } +} + diff --git a/src/main/java/com/lottery/lottery/service/RoomConnectionService.java b/src/main/java/com/lottery/lottery/service/RoomConnectionService.java new file mode 100644 index 0000000..c816f42 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/RoomConnectionService.java @@ -0,0 +1,309 @@ +package com.lottery.lottery.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.function.BiConsumer; + +/** + * Tracks room-level WebSocket connections. + * Tracks which users are connected to which rooms, regardless of round participation. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RoomConnectionService { + + // Callback to notify when room connections change (set by GameRoomService) + private BiConsumer connectionChangeCallback; + + // Track room connections: roomNumber -> userId -> Set of sessionIds + // This allows tracking multiple sessions per user (e.g., web + iOS) + private final Map>> roomConnections = new ConcurrentHashMap<>(); + + // Track session to user mapping: sessionId -> userId (for disconnect events when principal is lost) + private final Map sessionToUser = new ConcurrentHashMap<>(); + + /** + * Sets callback to be notified when room connections change. + * Called by GameRoomService during initialization. + */ + public void setConnectionChangeCallback(BiConsumer callback) { + this.connectionChangeCallback = callback; + } + + /** + * Registers a session-to-user mapping. + * Called when user connects to track sessions for disconnect events. + */ + public void registerSession(String sessionId, Integer userId) { + sessionToUser.put(sessionId, userId); + log.debug("Registered session {} for user {}", sessionId, userId); + } + + /** + * Removes a session-to-user mapping. + * Called when user disconnects. + */ + public Integer removeSession(String sessionId) { + Integer userId = sessionToUser.remove(sessionId); + if (userId != null) { + log.debug("Removed session {} for user {}", sessionId, userId); + } + return userId; + } + + /** + * Adds a user to a room's connection list. + * Called when user subscribes to room topic. + * + * @param userId The user ID + * @param roomNumber The room number + * @param sessionId The WebSocket session ID + */ + public void addUserToRoom(Integer userId, Integer roomNumber, String sessionId) { + if (userId == null || roomNumber == null || sessionId == null) { + log.warn("Attempted to add user to room with null parameters: userId={}, roomNumber={}, sessionId={}", + userId, roomNumber, sessionId); + return; + } + + // Get or create the map of users for this room + Map> roomUsers = roomConnections.computeIfAbsent(roomNumber, k -> new ConcurrentHashMap<>()); + + // Get or create the set of sessions for this user in this room + Set userSessions = roomUsers.computeIfAbsent(userId, k -> ConcurrentHashMap.newKeySet()); + + // Add the session + boolean isNewUser = userSessions.isEmpty(); + userSessions.add(sessionId); + + int connectedCount = getConnectedUsersCount(roomNumber); + + if (isNewUser) { + log.debug("User {} connected to room {} (session: {}). Total connected users: {}", + userId, roomNumber, sessionId, connectedCount); + } else { + log.debug("User {} added additional session to room {} (session: {}). Total sessions for user: {}, Total connected users: {}", + userId, roomNumber, sessionId, userSessions.size(), connectedCount); + } + + // Notify callback to broadcast updated state (only if this is a new user, not just a new session) + if (connectionChangeCallback != null && isNewUser) { + connectionChangeCallback.accept(roomNumber, connectedCount); + } + } + + /** + * Legacy method for backward compatibility. Uses sessionId from sessionToUser mapping. + * @deprecated Use addUserToRoom(userId, roomNumber, sessionId) instead + */ + @Deprecated + public void addUserToRoom(Integer userId, Integer roomNumber) { + // Try to find a session for this user (not ideal, but for backward compatibility) + String sessionId = sessionToUser.entrySet().stream() + .filter(entry -> entry.getValue().equals(userId)) + .map(Map.Entry::getKey) + .findFirst() + .orElse("legacy-" + userId + "-" + System.currentTimeMillis()); + + addUserToRoom(userId, roomNumber, sessionId); + } + + /** + * Removes a user's session from a room's connection list. + * Only removes the user from the room if this is their last session. + * Called when user disconnects or unsubscribes. + * + * @param userId The user ID + * @param roomNumber The room number + * @param sessionId The WebSocket session ID + */ + public void removeUserFromRoom(Integer userId, Integer roomNumber, String sessionId) { + if (userId == null || roomNumber == null || sessionId == null) { + log.warn("Attempted to remove user from room with null parameters: userId={}, roomNumber={}, sessionId={}", + userId, roomNumber, sessionId); + return; + } + + Map> roomUsers = roomConnections.get(roomNumber); + if (roomUsers == null) { + return; + } + + Set userSessions = roomUsers.get(userId); + if (userSessions == null) { + return; + } + + // Remove the session + boolean removed = userSessions.remove(sessionId); + if (!removed) { + log.debug("Session {} not found for user {} in room {}", sessionId, userId, roomNumber); + return; + } + + // Check if this was the last session for this user in this room + boolean wasLastSession = userSessions.isEmpty(); + + if (wasLastSession) { + // Remove the user from the room + roomUsers.remove(userId); + log.debug("User {} disconnected from room {} (last session: {}). Total connected users: {}", + userId, roomNumber, sessionId, getConnectedUsersCount(roomNumber)); + } else { + log.debug("User {} removed session from room {} (session: {}). Remaining sessions: {}, Total connected users: {}", + userId, roomNumber, sessionId, userSessions.size(), getConnectedUsersCount(roomNumber)); + } + + // Clean up empty room + if (roomUsers.isEmpty()) { + roomConnections.remove(roomNumber); + } + + int connectedCount = getConnectedUsersCount(roomNumber); + + // Notify callback to broadcast updated state (only if user was actually removed) + if (connectionChangeCallback != null && wasLastSession) { + connectionChangeCallback.accept(roomNumber, connectedCount); + } + } + + /** + * Legacy method for backward compatibility. Removes all sessions for the user. + * @deprecated Use removeUserFromRoom(userId, roomNumber, sessionId) instead + */ + @Deprecated + public void removeUserFromRoom(Integer userId, Integer roomNumber) { + // Remove all sessions for this user in this room + Map> roomUsers = roomConnections.get(roomNumber); + if (roomUsers == null) { + return; + } + + Set userSessions = roomUsers.get(userId); + if (userSessions == null || userSessions.isEmpty()) { + return; + } + + // Remove all sessions (create a copy to avoid concurrent modification) + Set sessionsToRemove = new java.util.HashSet<>(userSessions); + for (String sessionId : sessionsToRemove) { + removeUserFromRoom(userId, roomNumber, sessionId); + } + } + + /** + * Removes a specific session from all rooms. + * Only removes the user from a room if this is their last session in that room. + * Called when a session disconnects completely. + * + * @param userId The user ID + * @param sessionId The WebSocket session ID + */ + public void removeUserFromAllRooms(Integer userId, String sessionId) { + if (userId == null || sessionId == null) { + log.warn("Attempted to remove user from all rooms with null parameters: userId={}, sessionId={}", + userId, sessionId); + return; + } + + // Iterate through all rooms and remove this session + roomConnections.forEach((roomNumber, roomUsers) -> { + Set userSessions = roomUsers.get(userId); + if (userSessions != null && userSessions.contains(sessionId)) { + // Use the existing method which handles the logic correctly + removeUserFromRoom(userId, roomNumber, sessionId); + } + }); + } + + /** + * Legacy method that removes all sessions for a user from all rooms. + * @deprecated Use removeUserFromAllRooms(userId, sessionId) instead + */ + @Deprecated + public void removeUserFromAllRooms(Integer userId) { + if (userId == null) { + log.warn("Attempted to remove null user from all rooms"); + return; + } + + // Find all sessions for this user and remove them + roomConnections.forEach((roomNumber, roomUsers) -> { + Set userSessions = roomUsers.get(userId); + if (userSessions != null && !userSessions.isEmpty()) { + // Remove all sessions (create a copy to avoid concurrent modification) + Set sessionsToRemove = new java.util.HashSet<>(userSessions); + for (String sessionId : sessionsToRemove) { + removeUserFromRoom(userId, roomNumber, sessionId); + } + } + }); + } + + /** + * Removes a user from all rooms by session ID. + * Used when principal is lost during disconnect. + * + * @param sessionId The WebSocket session ID + */ + public void removeUserFromAllRoomsBySession(String sessionId) { + if (sessionId == null) { + log.warn("Attempted to remove user from all rooms with null sessionId"); + return; + } + + Integer userId = sessionToUser.get(sessionId); + if (userId != null) { + // Remove this specific session from all rooms + removeUserFromAllRooms(userId, sessionId); + // Also remove session mapping + removeSession(sessionId); + } else { + log.warn("Session {} not found in session-to-user mapping", sessionId); + } + } + + /** + * Gets the count of connected users in a room. + * Counts unique users, not sessions (a user with multiple sessions counts as 1). + */ + public int getConnectedUsersCount(Integer roomNumber) { + Map> roomUsers = roomConnections.get(roomNumber); + return roomUsers != null ? roomUsers.size() : 0; + } + + /** + * Checks if a user is connected to a room. + * Returns true if the user has at least one active session in the room. + */ + public boolean isUserConnectedToRoom(Integer userId, Integer roomNumber) { + Map> roomUsers = roomConnections.get(roomNumber); + if (roomUsers == null) { + return false; + } + Set userSessions = roomUsers.get(userId); + return userSessions != null && !userSessions.isEmpty(); + } + + /** + * Gets the list of user IDs currently connected (viewing) a room. + * Used by admin room management. + */ + public List getConnectedUserIds(Integer roomNumber) { + Map> roomUsers = roomConnections.get(roomNumber); + if (roomUsers == null || roomUsers.isEmpty()) { + return Collections.emptyList(); + } + return roomUsers.keySet().stream().sorted().collect(Collectors.toList()); + } +} + diff --git a/src/main/java/com/honey/honey/service/SessionCleanupService.java b/src/main/java/com/lottery/lottery/service/SessionCleanupService.java similarity index 95% rename from src/main/java/com/honey/honey/service/SessionCleanupService.java rename to src/main/java/com/lottery/lottery/service/SessionCleanupService.java index cf5a8eb..8c9b6a3 100644 --- a/src/main/java/com/honey/honey/service/SessionCleanupService.java +++ b/src/main/java/com/lottery/lottery/service/SessionCleanupService.java @@ -1,6 +1,6 @@ -package com.honey.honey.service; +package com.lottery.lottery.service; -import com.honey.honey.repository.SessionRepository; +import com.lottery.lottery.repository.SessionRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/honey/honey/service/SessionService.java b/src/main/java/com/lottery/lottery/service/SessionService.java similarity index 80% rename from src/main/java/com/honey/honey/service/SessionService.java rename to src/main/java/com/lottery/lottery/service/SessionService.java index c436443..0846429 100644 --- a/src/main/java/com/honey/honey/service/SessionService.java +++ b/src/main/java/com/lottery/lottery/service/SessionService.java @@ -1,9 +1,9 @@ -package com.honey.honey.service; +package com.lottery.lottery.service; -import com.honey.honey.model.Session; -import com.honey.honey.model.UserA; -import com.honey.honey.repository.SessionRepository; -import com.honey.honey.repository.UserARepository; +import com.lottery.lottery.model.Session; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.SessionRepository; +import com.lottery.lottery.repository.UserARepository; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,7 +27,7 @@ public class SessionService { private final SessionRepository sessionRepository; private final UserARepository userARepository; - private static final int SESSION_TTL_HOURS = 24; // 1 day + private static final int SESSION_TTL_HOURS = 1; // 1 hour private static final SecureRandom secureRandom = new SecureRandom(); /** @@ -40,7 +40,7 @@ public class SessionService { /** * Creates a new session for a user. - * Enforces MAX_ACTIVE_SESSIONS_PER_USER by deleting oldest active sessions if limit exceeded. + * Enforces MAX_ACTIVE_SESSIONS_PER_USER by deleting oldest sessions (active or expired) if limit exceeded. * Returns the raw session ID (to be sent to frontend) and stores the hash in DB. */ @Transactional @@ -71,31 +71,33 @@ public class SessionService { sessionRepository.save(session); - log.info("Created session for userId={}, expiresAt={}", user.getId(), expiresAt); + log.debug("Session created: userId={}", user.getId()); return sessionId; } /** - * Enforces MAX_ACTIVE_SESSIONS_PER_USER by deleting oldest active sessions if limit exceeded. + * Enforces MAX_ACTIVE_SESSIONS_PER_USER by deleting oldest sessions (active or expired) if limit exceeded. + * Counts ALL sessions regardless of expiration status. */ private void enforceMaxActiveSessions(Integer userId, LocalDateTime now) { - long activeCount = sessionRepository.countActiveSessionsByUserId(userId, now); + // Count ALL sessions for the user (active + expired) + long totalCount = sessionRepository.countByUserId(userId); - if (activeCount >= maxActiveSessionsPerUser) { - // Calculate how many to delete - int toDelete = (int) (activeCount - maxActiveSessionsPerUser + 1); + if (totalCount >= maxActiveSessionsPerUser) { + // Calculate how many to delete to stay under limit + // If user has 5 sessions and limit is 5, we need to delete 1 to make room for the new one + int toDelete = (int) (totalCount - maxActiveSessionsPerUser + 1); - // Get oldest active sessions - List oldestSessions = sessionRepository.findOldestActiveSessionsByUserId( + // Get oldest sessions (active or expired, ordered by createdAt ASC) + List oldestSessions = sessionRepository.findOldestSessionsByUserId( userId, - now, PageRequest.of(0, toDelete) ); // Delete oldest sessions if (!oldestSessions.isEmpty()) { sessionRepository.deleteAll(oldestSessions); - log.info("Deleted {} oldest active session(s) for userId={} to enforce max limit of {}", + log.debug("Deleted {} oldest session(s) for userId={} (limit: {})", oldestSessions.size(), userId, maxActiveSessionsPerUser); } } @@ -115,14 +117,12 @@ public class SessionService { Optional sessionOpt = sessionRepository.findBySessionIdHash(sessionIdHash); if (sessionOpt.isEmpty()) { - log.debug("Session not found: {}", maskSessionId(sessionId)); return Optional.empty(); } Session session = sessionOpt.get(); if (session.isExpired()) { - log.debug("Session expired: {}", maskSessionId(sessionId)); // Optionally delete expired session sessionRepository.delete(session); return Optional.empty(); @@ -143,7 +143,7 @@ public class SessionService { String sessionIdHash = hashSessionId(sessionId); sessionRepository.deleteBySessionIdHash(sessionIdHash); - log.info("Invalidated session: {}", maskSessionId(sessionId)); + log.debug("Session invalidated: userId={}", maskSessionId(sessionId)); } /** diff --git a/src/main/java/com/lottery/lottery/service/SupportTicketService.java b/src/main/java/com/lottery/lottery/service/SupportTicketService.java new file mode 100644 index 0000000..56db236 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/SupportTicketService.java @@ -0,0 +1,225 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.*; +import com.lottery.lottery.exception.GameException; +import com.lottery.lottery.model.SupportMessage; +import com.lottery.lottery.model.SupportTicket; +import com.lottery.lottery.model.SupportTicket.TicketStatus; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.repository.SupportMessageRepository; +import com.lottery.lottery.repository.SupportTicketRepository; +import com.lottery.lottery.repository.UserARepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class SupportTicketService { + + private static final int MAX_OPENED_TICKETS = 5; + private static final int MAX_MESSAGES_PER_TICKET = 20; + private static final int RATE_LIMIT_SECONDS = 10; + + private final SupportTicketRepository ticketRepository; + private final SupportMessageRepository messageRepository; + private final UserARepository userARepository; + private final LocalizationService localizationService; + + /** + * Creates a new support ticket with the first message. + * Validates limits and rate limiting. + */ + @Transactional + public TicketDto createTicket(Integer userId, CreateTicketRequest request) { + // Validate user exists + UserA user = userARepository.findById(userId) + .orElseThrow(() -> new GameException(localizationService.getMessage("user.error.notFound"))); + + // Check limit: max 5 OPENED tickets per user + long openedTicketsCount = ticketRepository.countByUserIdAndStatus(userId, TicketStatus.OPENED); + if (openedTicketsCount >= MAX_OPENED_TICKETS) { + throw new GameException(localizationService.getMessage("support.error.maxTicketsReached", + String.valueOf(MAX_OPENED_TICKETS))); + } + + // Create ticket + SupportTicket ticket = SupportTicket.builder() + .user(user) + .subject(request.getSubject().trim()) + .status(TicketStatus.OPENED) + .build(); + + ticket = ticketRepository.save(ticket); + + // Create first message + SupportMessage message = SupportMessage.builder() + .ticket(ticket) + .user(user) + .message(request.getMessage().trim()) + .build(); + + messageRepository.save(message); + + log.info("Created support ticket {} for user {}", ticket.getId(), userId); + + return mapToTicketDto(ticket, 1); + } + + /** + * Adds a message to an existing ticket. + * Validates ticket status, message limit, and rate limiting. + */ + @Transactional + public MessageDto addMessage(Integer userId, Long ticketId, CreateMessageRequest request) { + // Validate user exists + UserA user = userARepository.findById(userId) + .orElseThrow(() -> new GameException(localizationService.getMessage("user.error.notFound"))); + + // Get ticket (user can only access their own tickets) + SupportTicket ticket = ticketRepository.findByIdAndUserId(ticketId, userId) + .orElseThrow(() -> new GameException(localizationService.getMessage("support.error.ticketNotFound"))); + + // Check if ticket is closed + if (ticket.getStatus() == TicketStatus.CLOSED) { + throw new GameException(localizationService.getMessage("support.error.ticketClosed")); + } + + // Check limit: max 20 messages per OPENED ticket + long messageCount = messageRepository.countByTicketId(ticketId); + if (messageCount >= MAX_MESSAGES_PER_TICKET) { + throw new GameException(localizationService.getMessage("support.error.maxMessagesReached", + String.valueOf(MAX_MESSAGES_PER_TICKET))); + } + + // Rate limiting: not more than once per 10 seconds + List lastMessages = messageRepository.findLastMessageByTicketIdAndUserId( + ticketId, userId, PageRequest.of(0, 1)); + if (!lastMessages.isEmpty()) { + SupportMessage lastMessage = lastMessages.get(0); + long secondsSinceLastMessage = ChronoUnit.SECONDS.between(lastMessage.getCreatedAt(), Instant.now()); + if (secondsSinceLastMessage < RATE_LIMIT_SECONDS) { + long remainingSeconds = RATE_LIMIT_SECONDS - secondsSinceLastMessage; + throw new GameException(localizationService.getMessage("support.error.rateLimitWait", + String.valueOf(remainingSeconds))); + } + } + + // Create message + SupportMessage message = SupportMessage.builder() + .ticket(ticket) + .user(user) + .message(request.getMessage().trim()) + .build(); + + message = messageRepository.save(message); + + log.info("Added message {} to ticket {} for user {}", message.getId(), ticketId, userId); + + return mapToMessageDto(message, ticket.getUser().getId()); + } + + /** + * Closes a ticket. + */ + @Transactional + public void closeTicket(Integer userId, Long ticketId) { + // Get ticket (user can only access their own tickets) + SupportTicket ticket = ticketRepository.findByIdAndUserId(ticketId, userId) + .orElseThrow(() -> new GameException(localizationService.getMessage("support.error.ticketNotFound"))); + + if (ticket.getStatus() == TicketStatus.CLOSED) { + throw new GameException(localizationService.getMessage("support.error.ticketAlreadyClosed")); + } + + ticket.setStatus(TicketStatus.CLOSED); + ticketRepository.save(ticket); + + log.info("Closed ticket {} for user {}", ticketId, userId); + } + + /** + * Gets ticket details with all messages. + */ + @Transactional(readOnly = true) + public TicketDetailDto getTicketDetail(Integer userId, Long ticketId) { + // Get ticket (user can only access their own tickets) + SupportTicket ticket = ticketRepository.findByIdAndUserId(ticketId, userId) + .orElseThrow(() -> new GameException(localizationService.getMessage("support.error.ticketNotFound"))); + + // Get all messages + List messages = messageRepository.findByTicketIdOrderByCreatedAtAsc(ticketId); + + // Map to DTOs + List messageDtos = messages.stream() + .map(msg -> mapToMessageDto(msg, ticket.getUser().getId())) + .collect(Collectors.toList()); + + return TicketDetailDto.builder() + .id(ticket.getId()) + .subject(ticket.getSubject()) + .status(ticket.getStatus()) + .createdAt(ticket.getCreatedAt()) + .updatedAt(ticket.getUpdatedAt()) + .messages(messageDtos) + .build(); + } + + /** + * Gets ticket history for a user (last 20 tickets). + */ + @Transactional(readOnly = true) + public List getTicketHistory(Integer userId) { + Pageable pageable = PageRequest.of(0, 20); + Page tickets = ticketRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable); + + return tickets.getContent().stream() + .map(ticket -> { + long messageCount = messageRepository.countByTicketId(ticket.getId()); + return mapToTicketDto(ticket, (int) messageCount); + }) + .collect(Collectors.toList()); + } + + /** + * Maps SupportTicket to TicketDto. + */ + private TicketDto mapToTicketDto(SupportTicket ticket, int messageCount) { + return TicketDto.builder() + .id(ticket.getId()) + .subject(ticket.getSubject()) + .status(ticket.getStatus()) + .createdAt(ticket.getCreatedAt()) + .updatedAt(ticket.getUpdatedAt()) + .messageCount(messageCount) + .build(); + } + + /** + * Maps SupportMessage to MessageDto. + * isFromSupport is true if the message is from a different user than the ticket owner. + */ + private MessageDto mapToMessageDto(SupportMessage message, Integer ticketOwnerUserId) { + boolean isFromSupport = !message.getUser().getId().equals(ticketOwnerUserId); + + return MessageDto.builder() + .id(message.getId()) + .ticketId(message.getTicket().getId()) + .userId(message.getUser().getId()) + .message(message.getMessage()) + .createdAt(message.getCreatedAt()) + .isFromSupport(isFromSupport) + .build(); + } +} + diff --git a/src/main/java/com/lottery/lottery/service/TaskService.java b/src/main/java/com/lottery/lottery/service/TaskService.java new file mode 100644 index 0000000..da49d66 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/TaskService.java @@ -0,0 +1,654 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.config.LocaleConfig; +import com.lottery.lottery.config.TelegramProperties; +import com.lottery.lottery.dto.RecentBonusClaimDto; +import com.lottery.lottery.dto.TaskDto; +import com.lottery.lottery.model.Task; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.model.UserD; +import com.lottery.lottery.model.UserTaskClaim; +import com.lottery.lottery.model.UserDailyBonusClaim; +import com.lottery.lottery.repository.TaskRepository; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.repository.UserBRepository; +import com.lottery.lottery.repository.UserDRepository; +import com.lottery.lottery.repository.UserTaskClaimRepository; +import com.lottery.lottery.repository.UserDailyBonusClaimRepository; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TaskService { + + private final TaskRepository taskRepository; + private final UserTaskClaimRepository userTaskClaimRepository; + private final UserDailyBonusClaimRepository userDailyBonusClaimRepository; + private final UserDRepository userDRepository; + private final UserBRepository userBRepository; + private final UserARepository userARepository; + private final TelegramService telegramService; + private final TelegramProperties telegramProperties; + private final TransactionService transactionService; + private final EntityManager entityManager; + private final LocalizationService localizationService; + private final FeatureSwitchService featureSwitchService; + + /** + * Gets all tasks for a specific type. + * For referral tasks, includes user progress and claim status. + * @param userId User ID to get tasks for + * @param type Task type (referral, follow, other) + * @param languageCode User's language code for localization (optional, defaults to EN) + */ + public List getTasksByType(Integer userId, String type, String languageCode) { + List tasks = taskRepository.findByTypeOrderByDisplayOrderAsc(type); + + // Get user progress for different task types + Integer userReferals1 = null; + Long userDepositTotal = null; + if ("referral".equals(type)) { + userReferals1 = getUserReferals1(userId); + } else if ("other".equals(type)) { + userDepositTotal = getUserDepositTotal(userId); + } + + // Get user's claimed tasks + List claimedTaskIds = userTaskClaimRepository.findByUserId(userId) + .stream() + .map(UserTaskClaim::getTaskId) + .collect(Collectors.toList()); + + final Integer finalUserReferals1 = userReferals1; + final Long finalUserDepositTotal = userDepositTotal; + final List finalClaimedTaskIds = claimedTaskIds; + + return tasks.stream() + .filter(task -> !finalClaimedTaskIds.contains(task.getId())) + .filter(task -> isReferralTaskEnabled(task)) + .map(task -> { + boolean isClaimed = finalClaimedTaskIds.contains(task.getId()); + String progress = buildProgressString(task, finalUserReferals1, finalUserDepositTotal, isClaimed, languageCode); + Long currentValue = getCurrentValue(task, finalUserReferals1, finalUserDepositTotal); + + // Localize title and description + String localizedTitle = localizeTaskTitle(task, languageCode); + String localizedDescription = localizeTaskDescription(task, languageCode); + String localizedRewardText = localizeRewardText(task, languageCode); + + return TaskDto.builder() + .id(task.getId()) + .type(task.getType()) + .requirement(task.getRequirement()) + .rewardAmount(task.getRewardAmount()) + .rewardType(task.getRewardType()) + .title(localizedTitle) + .description(localizedDescription) + .displayOrder(task.getDisplayOrder()) + .claimed(isClaimed) + .progress(progress) + .currentValue(currentValue) + .localizedRewardText(localizedRewardText) + .build(); + }) + .collect(Collectors.toList()); + } + + /** + * Returns true if the task should be shown/claimable. Referral tasks with requirement 50 or 100 + * are gated by feature switches (task_referral_50_enabled, task_referral_100_enabled). + */ + private boolean isReferralTaskEnabled(Task task) { + if (!"referral".equals(task.getType())) { + return true; + } + long req = task.getRequirement() != null ? task.getRequirement() : 0; + if (req == 50) { + return featureSwitchService.isTaskReferral50Enabled(); + } + if (req == 100) { + return featureSwitchService.isTaskReferral100Enabled(); + } + return true; + } + + /** + * Returns true if the user has claimed at least one of the referral tasks with requirement 50 or 100. + * Used to set manual_pay=1 on crypto withdrawal requests when applicable. + */ + @Transactional(readOnly = true) + public boolean hasCompletedReferral50Or100(Integer userId) { + List tasks = taskRepository.findByTypeAndRequirementIn("referral", List.of(50L, 100L)); + if (tasks.isEmpty()) { + return false; + } + List taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList()); + return userTaskClaimRepository.existsByUserIdAndTaskIdIn(userId, taskIds); + } + + /** + * Gets user's level 1 referrals count (referals_1). + */ + private Integer getUserReferals1(Integer userId) { + Optional userDOpt = userDRepository.findById(userId); + return userDOpt.map(UserD::getReferals1).orElse(0); + } + + /** + * Gets user's total deposit amount (deposit_total). + */ + private Long getUserDepositTotal(Integer userId) { + Optional userBOpt = userBRepository.findById(userId); + return userBOpt.map(UserB::getDepositTotal).orElse(0L); + } + + /** + * Builds progress string for a task. + * For referral tasks: shows "current / requirement" or "CLAIMED" if already claimed. + * For other tasks and follow tasks: returns null - frontend will build progress string from currentValue and requirement. + */ + private String buildProgressString(Task task, Integer userReferals1, Long userDepositTotal, boolean isClaimed, String languageCode) { + if (isClaimed) { + // Return localized "CLAIMED" text + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + Locale locale = LocaleConfig.languageCodeToLocale(languageCode); + return localizationService.getMessage(locale, "task.claimed"); + } + + if ("referral".equals(task.getType()) && userReferals1 != null) { + Long requirement = task.getRequirement(); + return userReferals1 + " / " + requirement; + } + + // For other and follow tasks, return null - frontend will build progress from currentValue and requirement + return null; + } + + /** + * Gets current value for progress calculation (in bigint format). + * Frontend will convert to display format. + */ + private Long getCurrentValue(Task task, Integer userReferals1, Long userDepositTotal) { + if ("referral".equals(task.getType()) && userReferals1 != null) { + return (long) userReferals1; + } + + if ("other".equals(task.getType()) && userDepositTotal != null) { + return userDepositTotal; + } + + return null; + } + + /** + * Claims a task for a user. + * Checks if task is completed, and if so, marks it as claimed and gives reward. + * + * IMPORTANT: For non-daily tasks, this method prevents abuse by checking if the task is already claimed FIRST, + * before checking completion. This ensures that even if a user leaves and rejoins a channel, + * they cannot claim the reward multiple times. Once a task is claimed, the claim record + * persists and prevents any further claims for that user-task combination. + * + * For daily tasks, the method allows claiming again after 24 hours have passed since the last claim. + * The old claim record is deleted and a new one is created with the current timestamp. + * + * @return true if task was successfully claimed, false if task is not completed or already claimed (for non-daily tasks) + */ + @Transactional + public boolean claimTask(Integer userId, Integer taskId) { + // Get task first to check its type + Optional taskOpt = taskRepository.findById(taskId); + if (taskOpt.isEmpty()) { + log.warn("Task not found: taskId={}", taskId); + return false; + } + + Task task = taskOpt.get(); + + // Reject claim if this referral task (50 or 100 friends) is temporarily disabled + if (!isReferralTaskEnabled(task)) { + log.debug("Task disabled by feature switch: taskId={}, type={}, requirement={}", taskId, task.getType(), task.getRequirement()); + return false; + } + + // For non-daily tasks, check if already claimed FIRST to prevent abuse + // This prevents users from claiming rewards multiple times by leaving/rejoining channels + if (!"daily".equals(task.getType()) && userTaskClaimRepository.existsByUserIdAndTaskId(userId, taskId)) { + log.debug("Task already claimed: userId={}, taskId={}", userId, taskId); + return false; + } + + // Check if task is completed + // For daily tasks, this checks if 24 hours have passed since last claim + if (!isTaskCompleted(userId, task)) { + log.debug("Task not completed: userId={}, taskId={}", userId, taskId); + return false; + } + + // For daily tasks, save to user_daily_bonus_claims table with user info + if ("daily".equals(task.getType())) { + // Get user data for the claim record + Optional userOpt = userARepository.findById(userId); + String avatarUrl = null; + String screenName = "-"; + if (userOpt.isPresent()) { + UserA user = userOpt.get(); + avatarUrl = user.getAvatarUrl(); + screenName = user.getScreenName() != null ? user.getScreenName() : "-"; + } + + // Save to user_daily_bonus_claims table + UserDailyBonusClaim dailyClaim = UserDailyBonusClaim.builder() + .userId(userId) + .avatarUrl(avatarUrl) + .screenName(screenName) + .build(); + userDailyBonusClaimRepository.save(dailyClaim); + } else { + // For non-daily tasks, save to user_task_claims table + UserTaskClaim claim = UserTaskClaim.builder() + .userId(userId) + .taskId(taskId) + .build(); + userTaskClaimRepository.save(claim); + } + + // Give reward (rewardAmount is already in bigint format) + giveReward(userId, task.getRewardAmount()); + + // Create transaction - use DAILY_BONUS for daily tasks, TASK_BONUS for others + try { + if ("daily".equals(task.getType())) { + transactionService.createDailyBonusTransaction(userId, task.getRewardAmount()); + } else { + transactionService.createTaskBonusTransaction(userId, task.getRewardAmount(), taskId); + } + } catch (Exception e) { + log.error("Error creating transaction: userId={}, taskId={}, type={}", userId, taskId, task.getType(), e); + // Continue even if transaction record creation fails + } + + log.info("Task claimed: userId={}, taskId={}, reward={}", + userId, taskId, task.getRewardAmount()); + + return true; + } + + /** + * Checks if a task is completed by the user. + */ + private boolean isTaskCompleted(Integer userId, Task task) { + if ("referral".equals(task.getType())) { + Integer referals1 = getUserReferals1(userId); + return referals1 >= task.getRequirement(); + } + + if ("other".equals(task.getType())) { + // For other tasks, requirement is deposit_total threshold in bigint format + Long depositTotal = getUserDepositTotal(userId); + return depositTotal >= task.getRequirement(); + } + + if ("follow".equals(task.getType())) { + // For follow tasks, check if user is a member of the Telegram channel + Optional userAOpt = userARepository.findById(userId); + if (userAOpt.isEmpty() || userAOpt.get().getTelegramId() == null) { + log.warn("User not found or has no telegram ID: userId={}", userId); + return false; + } + + Long telegramUserId = userAOpt.get().getTelegramId(); + // Get channel ID from configuration based on task requirement + // requirement=1 for News channel, requirement=2 for Withdrawals channel + String chatId; + Long requirement = task.getRequirement(); + if (requirement != null && requirement.equals(1L)) { + chatId = telegramProperties.getFollowTaskChannelId(); + } else if (requirement != null && requirement.equals(2L)) { + chatId = telegramProperties.getFollowTaskChannelId2(); + } else { + // Fallback to first channel for backward compatibility + chatId = telegramProperties.getFollowTaskChannelId(); + } + + if (chatId == null || chatId.isEmpty()) { + log.error("Follow task channel ID is not configured for requirement={}", task.getRequirement()); + return false; + } + + try { + return telegramService.isUserMemberOfChat(telegramUserId, chatId); + } catch (IllegalStateException e) { + // Handle Telegram API errors gracefully + // If it's a "member not found" error, it's already handled in TelegramService + // and returns false. Other errors should be logged but not crash the task check. + log.warn("Error checking channel membership for follow task: userId={}, chatId={}", + telegramUserId, chatId, e); + return false; + } + } + + if ("daily".equals(task.getType())) { + // For daily bonus, check if 24 hours have passed since last claim + // Use user_daily_bonus_claims table instead of user_task_claims + Optional claimOpt = userDailyBonusClaimRepository.findFirstByUserIdOrderByClaimedAtDesc(userId); + if (claimOpt.isEmpty()) { + // Never claimed, so it's available + return true; + } + + UserDailyBonusClaim claim = claimOpt.get(); + LocalDateTime claimedAt = claim.getClaimedAt(); + LocalDateTime now = LocalDateTime.now(); + long hoursSinceClaim = java.time.Duration.between(claimedAt, now).toHours(); + + // Available if 24 hours or more have passed + return hoursSinceClaim >= 24; + } + + return false; + } + + /** + * Gets daily bonus status for a user. + * Returns availability status and cooldown time if on cooldown. + */ + public com.lottery.lottery.dto.DailyBonusStatusDto getDailyBonusStatus(Integer userId) { + // Find daily bonus task + List dailyTasks = taskRepository.findByTypeOrderByDisplayOrderAsc("daily"); + if (dailyTasks.isEmpty()) { + log.warn("Daily bonus task not found"); + return com.lottery.lottery.dto.DailyBonusStatusDto.builder() + .available(false) + .cooldownSeconds(0L) + .rewardAmount(0L) + .build(); + } + + Task dailyTask = dailyTasks.get(0); + + // Check if user has claimed before using user_daily_bonus_claims table + Optional claimOpt = userDailyBonusClaimRepository.findFirstByUserIdOrderByClaimedAtDesc(userId); + + if (claimOpt.isEmpty()) { + // Never claimed, so it's available + return com.lottery.lottery.dto.DailyBonusStatusDto.builder() + .taskId(dailyTask.getId()) + .available(true) + .cooldownSeconds(null) + .rewardAmount(dailyTask.getRewardAmount()) + .build(); + } + + // Check cooldown + UserDailyBonusClaim claim = claimOpt.get(); + LocalDateTime claimedAt = claim.getClaimedAt(); + LocalDateTime now = LocalDateTime.now(); + long secondsSinceClaim = java.time.Duration.between(claimedAt, now).getSeconds(); + long hoursSinceClaim = secondsSinceClaim / 3600; + + if (hoursSinceClaim >= 24) { + // Cooldown expired, available + return com.lottery.lottery.dto.DailyBonusStatusDto.builder() + .taskId(dailyTask.getId()) + .available(true) + .cooldownSeconds(null) + .rewardAmount(dailyTask.getRewardAmount()) + .build(); + } else { + // Still on cooldown + long secondsUntilAvailable = (24 * 3600) - secondsSinceClaim; + return com.lottery.lottery.dto.DailyBonusStatusDto.builder() + .taskId(dailyTask.getId()) + .available(false) + .cooldownSeconds(secondsUntilAvailable) + .rewardAmount(dailyTask.getRewardAmount()) + .build(); + } + } + + /** + * Gets the 50 most recent daily bonus claims with user information. + * Returns claims ordered by claimed_at DESC (most recent first). + * Simple query without JOINs - all data is in user_daily_bonus_claims table. + * + * @param timezone Optional timezone (e.g., "Europe/Kiev"). If null, uses UTC. + * @param languageCode User's language code for localization (e.g., "EN", "RU") + * @return List of RecentBonusClaimDto with avatar URL, screen name, and formatted claim timestamp + */ + public List getRecentDailyBonusClaims(String timezone, String languageCode) { + // Get recent claims - simple query, no JOINs needed + List claims = userDailyBonusClaimRepository.findTop50ByOrderByClaimedAtDesc(); + + // Determine timezone to use + java.time.ZoneId zoneId; + try { + zoneId = (timezone != null && !timezone.trim().isEmpty()) + ? java.time.ZoneId.of(timezone) + : java.time.ZoneId.of("UTC"); + } catch (Exception e) { + // Invalid timezone, fallback to UTC + zoneId = java.time.ZoneId.of("UTC"); + } + + // Get localized "at" word + String atWord = localizationService.getMessage("dateTime.at", languageCode); + if (atWord == null || atWord.isEmpty()) { + atWord = "at"; // Fallback to English + } + + // Create formatter with localized "at" word + final java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("dd.MM '" + atWord + "' HH:mm") + .withZone(zoneId); + + // Convert to DTOs with formatted date + return claims.stream() + .map(claim -> { + // Convert LocalDateTime to Instant (assuming it's stored in UTC) + // LocalDateTime doesn't have timezone info, so we treat it as UTC + java.time.Instant instant = claim.getClaimedAt().atZone(java.time.ZoneId.of("UTC")).toInstant(); + String formattedDate = formatter.format(instant); + + return RecentBonusClaimDto.builder() + .avatarUrl(claim.getAvatarUrl()) + .screenName(claim.getScreenName()) + .claimedAt(claim.getClaimedAt()) + .date(formattedDate) + .build(); + }) + .collect(Collectors.toList()); + } + + /** + * Gives reward to user's balance. + * rewardAmount is already in bigint format from frontend. + */ + private void giveReward(Integer userId, Long rewardAmount) { + Optional userBOpt = userBRepository.findById(userId); + if (userBOpt.isEmpty()) { + log.warn("User balance not found: userId={}", userId); + return; + } + + UserB userB = userBOpt.get(); + userB.setBalanceA(userB.getBalanceA() + rewardAmount); + userBRepository.save(userB); + + log.debug("Reward given: userId={}, amount={}", userId, rewardAmount); + } + + /** + * Localizes task title based on task type and requirement. + * Falls back to database title if no translation key matches. + */ + private String localizeTaskTitle(Task task, String languageCode) { + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + + Locale locale = LocaleConfig.languageCodeToLocale(languageCode); + String key = null; + + if ("referral".equals(task.getType())) { + // Use task-specific key based on requirement for proper grammar + key = "task.title.inviteFriends." + task.getRequirement(); + } else if ("follow".equals(task.getType())) { + // Pattern: "Follow our News channel" or "Follow Proof of payment channel" + // Use requirement to distinguish: 1=News, 2=Withdrawals + Long requirement = task.getRequirement(); + if (requirement != null && requirement.equals(1L)) { + key = "task.title.followChannel"; + } else if (requirement != null && requirement.equals(2L)) { + key = "task.title.followChannelWithdrawals"; + } else { + key = "task.title.followChannel"; // Fallback + } + } else if ("daily".equals(task.getType())) { + // Pattern: "Daily Bonus" + key = "task.title.dailyBonus"; + } else if ("other".equals(task.getType())) { + // Pattern: "Deposit {0} tickets" + key = "task.title.deposit"; + } + + if (key != null) { + try { + if ("referral".equals(task.getType())) { + // For referral tasks, use task-specific key (no parameters needed) + return localizationService.getMessage(locale, key); + } else if ("other".equals(task.getType())) { + // Convert requirement (bigint) to tickets for display + long tickets = task.getRequirement() / 1_000_000L; + return localizationService.getMessage(locale, key, tickets); + } else { + return localizationService.getMessage(locale, key); + } + } catch (Exception e) { + log.warn("Failed to localize task title for taskId={}, key={}, languageCode={}", + task.getId(), key, languageCode, e); + } + } + + // Fallback to database title + return task.getTitle(); + } + + /** + * Localizes task description based on task type and requirement. + * Falls back to database description if no translation key matches. + */ + private String localizeTaskDescription(Task task, String languageCode) { + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + + Locale locale = LocaleConfig.languageCodeToLocale(languageCode); + String key = null; + + if ("referral".equals(task.getType())) { + // Use task-specific key based on requirement for proper grammar + key = "task.description.inviteFriends." + task.getRequirement(); + } else if ("follow".equals(task.getType())) { + // Pattern: "Follow our Telegram channel" or "Follow Proof of payment channel" + // Use requirement to distinguish: 1=News, 2=Withdrawals + Long requirement = task.getRequirement(); + if (requirement != null && requirement.equals(1L)) { + key = "task.description.followChannel"; + } else if (requirement != null && requirement.equals(2L)) { + key = "task.description.followChannelWithdrawals"; + } else { + key = "task.description.followChannel"; // Fallback + } + } else if ("daily".equals(task.getType())) { + // Pattern: "Claim your daily free ticket!" + key = "task.description.dailyBonus"; + } else if ("other".equals(task.getType())) { + // Pattern: "Deposit {0} tickets" + key = "task.description.deposit"; + } + + if (key != null) { + try { + if ("referral".equals(task.getType())) { + // For referral tasks, use task-specific key (no parameters needed) + return localizationService.getMessage(locale, key); + } else if ("other".equals(task.getType())) { + // Convert requirement (bigint) to tickets for display + long tickets = task.getRequirement() / 1_000_000L; + return localizationService.getMessage(locale, key, tickets); + } else { + return localizationService.getMessage(locale, key); + } + } catch (Exception e) { + log.warn("Failed to localize task description for taskId={}, key={}, languageCode={}", + task.getId(), key, languageCode, e); + } + } + + // Fallback to database description + return task.getDescription(); + } + + /** + * Localizes reward text based on task type and reward amount. + * Returns formatted string like "+2 Билеты" or "+5 Билетов" for proper grammar. + */ + private String localizeRewardText(Task task, String languageCode) { + if (languageCode == null || languageCode.isEmpty() || "XX".equals(languageCode)) { + languageCode = "EN"; + } + + Locale locale = LocaleConfig.languageCodeToLocale(languageCode); + + // Convert reward amount from bigint to tickets + long tickets = task.getRewardAmount() / 1_000_000L; + + // Build key based on task type and requirement for proper grammar + String key = null; + if ("referral".equals(task.getType())) { + key = "task.reward.tickets." + task.getRequirement(); + } else if ("follow".equals(task.getType())) { + key = "task.reward.tickets.follow"; + } else if ("daily".equals(task.getType())) { + key = "task.reward.tickets.daily"; + } else if ("other".equals(task.getType())) { + // For other tasks, use the requirement (deposit amount) as key + long requirementTickets = task.getRequirement() / 1_000_000L; + key = "task.reward.tickets.other." + requirementTickets; + } + + if (key != null) { + try { + return localizationService.getMessage(locale, key, tickets); + } catch (Exception e) { + log.warn("Failed to localize reward text for taskId={}, key={}, languageCode={}", + task.getId(), key, languageCode, e); + } + } + + // Fallback: use generic tickets translation + try { + String ticketsKey = "tasks.tickets"; + String ticketsText = localizationService.getMessage(locale, ticketsKey); + return "+" + tickets + " " + ticketsText; + } catch (Exception e) { + // Final fallback + return "+" + tickets + " Tickets"; + } + } +} + diff --git a/src/main/java/com/honey/honey/service/TelegramAuthService.java b/src/main/java/com/lottery/lottery/service/TelegramAuthService.java similarity index 83% rename from src/main/java/com/honey/honey/service/TelegramAuthService.java rename to src/main/java/com/lottery/lottery/service/TelegramAuthService.java index d45d8e7..74d5e31 100644 --- a/src/main/java/com/honey/honey/service/TelegramAuthService.java +++ b/src/main/java/com/lottery/lottery/service/TelegramAuthService.java @@ -1,8 +1,8 @@ -package com.honey.honey.service; +package com.lottery.lottery.service; import com.fasterxml.jackson.databind.ObjectMapper; -import com.honey.honey.config.TelegramProperties; -import com.honey.honey.exception.UnauthorizedException; +import com.lottery.lottery.config.TelegramProperties; +import com.lottery.lottery.exception.UnauthorizedException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -26,18 +26,19 @@ public class TelegramAuthService { private static final String WEB_APP_DATA_CONSTANT = "WebAppData"; private final TelegramProperties telegramProperties; + private final LocalizationService localizationService; private final ObjectMapper objectMapper = new ObjectMapper(); /** * Validates and parses Telegram initData string. * Returns a map containing: * - "user": parsed user data (Map) - * - "start": referral start parameter from URL (e.g., "774876" from /honey?start=774876) (String, can be null) + * Note: Referral handling is done via bot, not through WebApp initData. */ public Map validateAndParseInitData(String initData) { if (initData == null || initData.isBlank()) { - throw new UnauthorizedException("Telegram initData is missing"); + throw new UnauthorizedException(localizationService.getMessage("auth.error.initDataMissing")); } try { @@ -46,7 +47,7 @@ public class TelegramAuthService { String receivedHash = parsedData.remove("hash"); if (receivedHash == null) { - throw new UnauthorizedException("Missing Telegram hash"); + throw new UnauthorizedException(localizationService.getMessage("auth.error.missingHash")); } // Step 2. Build data check string. @@ -60,22 +61,22 @@ public class TelegramAuthService { if (!receivedHash.equals(calculatedHash)) { log.warn("Telegram signature mismatch. Expected={}, Received={}", calculatedHash, receivedHash); - throw new UnauthorizedException("Invalid Telegram signature"); + throw new UnauthorizedException(localizationService.getMessage("auth.error.invalidSignature")); } - // Step 5. Extract the user JSON and start parameter from initData. + // Step 5. Extract the user JSON from initData. Map decoded = decodeQueryParams(initData); String userJson = decoded.get("user"); - String start = decoded.get("start"); // Referral parameter from URL: /honey?start=774876 if (userJson == null) { - throw new UnauthorizedException("initData does not contain 'user' field"); + throw new UnauthorizedException(localizationService.getMessage("auth.error.userFieldMissing")); } - // Step 6. Parse JSON into map and add start parameter. + // Step 6. Parse JSON into map. + // Note: Referral handling is done via bot registration endpoint, not through WebApp initData. Map result = new HashMap<>(); result.put("user", objectMapper.readValue(userJson, Map.class)); - result.put("start", start); + // "start" parameter is not included for WebApp - referrals handled by bot return result; @@ -84,7 +85,7 @@ public class TelegramAuthService { } catch (Exception ex) { log.error("Telegram initData validation failed: {}", ex.getMessage(), ex); - throw new UnauthorizedException("Invalid Telegram initData"); + throw new UnauthorizedException(localizationService.getMessage("auth.error.invalidInitData")); } } diff --git a/src/main/java/com/lottery/lottery/service/TelegramBotApiService.java b/src/main/java/com/lottery/lottery/service/TelegramBotApiService.java new file mode 100644 index 0000000..582ba09 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/TelegramBotApiService.java @@ -0,0 +1,161 @@ +package com.lottery.lottery.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.lottery.lottery.dto.TelegramApiResponse; +import com.lottery.lottery.dto.TelegramSendResult; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; + +import jakarta.annotation.PreDestroy; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Sends requests to Telegram Bot API. No client-side rate limiting; on 429 Too Many Requests + * from Telegram, schedules a single retry after retry_after seconds. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class TelegramBotApiService { + + private static final int MAX_RETRY_AFTER_SECONDS = 120; + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final RestTemplate restTemplate = new RestTemplate(); + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r, "telegram-api-retry"); + t.setDaemon(true); + return t; + }); + + /** Max 429 retries for broadcast (total attempts = 1 + this value). */ + private static final int BROADCAST_429_MAX_RETRIES = 2; + + /** + * POST for broadcast with 429 retry: on 429, reads retry_after from response, waits that long + * (capped, with jitter), then retries. Returns final result for audit (success + status code). + */ + public TelegramSendResult postForBroadcast(String url, HttpEntity entity) { + int attempt = 0; + int maxAttempts = 1 + BROADCAST_429_MAX_RETRIES; + while (true) { + try { + ResponseEntity response = restTemplate.postForEntity( + url, entity, TelegramApiResponse.class); + int code = response.getStatusCode().value(); + boolean ok = response.getBody() != null && Boolean.TRUE.equals(response.getBody().getOk()); + return new TelegramSendResult(ok && code == 200, code); + } catch (HttpClientErrorException e) { + if (e.getStatusCode().value() == 429 && attempt < maxAttempts - 1) { + int retryAfterSeconds = parseRetryAfter(e); + int waitSeconds = Math.min(Math.max(1, retryAfterSeconds), MAX_RETRY_AFTER_SECONDS); + double jitter = 0.9 + (Math.random() * 0.2); + long waitMs = (long) (waitSeconds * jitter * 1000); + log.warn("Telegram API 429 during broadcast, waiting {} ms then retry (attempt {}/{})", waitMs, attempt + 1, maxAttempts); + try { + Thread.sleep(waitMs); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return new TelegramSendResult(false, 429); + } + attempt++; + continue; + } + return new TelegramSendResult(false, e.getStatusCode().value()); + } catch (HttpServerErrorException e) { + return new TelegramSendResult(false, e.getStatusCode().value()); + } catch (Exception e) { + log.debug("Telegram send failed: {}", e.getMessage()); + return new TelegramSendResult(false, 0); + } + } + } + + /** + * Posts to Telegram API. On 429, schedules a single retry after retry_after seconds. + * + * @param url Telegram API URL + * @param entity Request entity (must be reusable for retry, e.g. no streams) + * @return Response body, or null if call failed and was scheduled for retry + */ + public ResponseEntity post(String url, HttpEntity entity) { + return post(url, entity, 0); + } + + private ResponseEntity post(String url, HttpEntity entity, int retryCount) { + try { + ResponseEntity response = restTemplate.postForEntity( + url, + entity, + TelegramApiResponse.class + ); + return response; + } catch (HttpClientErrorException e) { + if (e.getStatusCode().value() == 429 && retryCount == 0) { + int retryAfterSeconds = parseRetryAfter(e); + if (retryAfterSeconds > 0 && retryAfterSeconds <= MAX_RETRY_AFTER_SECONDS) { + log.warn("Telegram API 429 Too Many Requests, scheduling retry in {}s", retryAfterSeconds); + scheduler.schedule(() -> { + try { + post(url, entity, 1); + } catch (Exception ex) { + log.warn("Telegram API retry failed after 429 – outbound message lost, may affect registration welcome: {}", ex.getMessage()); + log.error("Telegram API retry failed", ex); + } + }, retryAfterSeconds, TimeUnit.SECONDS); + return null; + } + } + throw e; + } + } + + /** + * Parse retry_after from Telegram 429 response body (parameters.retry_after). + * Value may be in seconds or milliseconds (if > 120 we treat as ms and convert to seconds). + */ + private int parseRetryAfter(HttpClientErrorException e) { + try { + String body = e.getResponseBodyAsString(); + if (body == null || body.isEmpty()) { + return 60; + } + JsonNode root = objectMapper.readTree(body); + JsonNode params = root != null ? root.get("parameters") : null; + if (params != null && params.has("retry_after")) { + JsonNode ra = params.get("retry_after"); + if (!ra.isNumber()) return 60; + double value = ra.asDouble(); + if (value > 120) { + value = value / 1000.0; + } + return (int) Math.ceil(Math.max(1, value)); + } + } catch (Exception parseEx) { + log.debug("Could not parse retry_after from 429 response: {}", parseEx.getMessage()); + } + return 60; + } + + @PreDestroy + public void shutdown() { + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + scheduler.shutdownNow(); + } + } catch (InterruptedException e) { + scheduler.shutdownNow(); + Thread.currentThread().interrupt(); + } + } +} diff --git a/src/main/java/com/lottery/lottery/service/TelegramService.java b/src/main/java/com/lottery/lottery/service/TelegramService.java new file mode 100644 index 0000000..ee85146 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/TelegramService.java @@ -0,0 +1,106 @@ +package com.lottery.lottery.service; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.lottery.lottery.config.TelegramProperties; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Service for interacting with Telegram Bot API. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class TelegramService { + + private final TelegramProperties telegramProperties; + private final RestTemplate restTemplate = new RestTemplate(); + + /** + * Checks if a user is a member of a Telegram channel/chat. + * Requires channelCheckerBotToken to be configured. + * + * @param telegramUserId Telegram user ID + * @param chatId Chat ID (can be channel username like "@lottery_2026_test_channel" or numeric ID) + * @return true if user is a member (member, administrator, or creator), false otherwise + * @throws IllegalStateException if channelCheckerBotToken is not configured + */ + public boolean isUserMemberOfChat(Long telegramUserId, String chatId) { + String botToken = telegramProperties.getChannelCheckerBotToken(); + if (botToken == null || botToken.isEmpty()) { + log.error("Channel checker bot token is not configured"); + throw new IllegalStateException("Channel checker bot token is not configured. Please set TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN environment variable or configure it in the mounted secret file."); + } + + String url = "https://api.telegram.org/bot" + botToken + "/getChatMember"; + + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url) + .queryParam("chat_id", chatId) + .queryParam("user_id", telegramUserId); + + try { + ResponseEntity response = + restTemplate.getForEntity( + builder.toUriString(), + TelegramGetChatMemberResponse.class + ); + + if (response.getBody() == null || !Boolean.TRUE.equals(response.getBody().getOk())) { + log.warn("getChatMember returned not ok: userId={}, chatId={}", telegramUserId, chatId); + return false; + } + + String status = response.getBody().getResult().getStatus(); + + return switch (status) { + case "member", "administrator", "creator" -> true; + default -> false; // left, kicked, restricted + }; + + } catch (HttpClientErrorException.NotFound e) { + // user or chat not found + log.warn("User or chat not found: userId={}, chatId={}", telegramUserId, chatId); + return false; + } catch (HttpClientErrorException.BadRequest e) { + // Check if this is PARTICIPANT_ID_INVALID or "member not found" (user is not a member of the channel) + String responseBody = e.getResponseBodyAsString(); + if (responseBody != null && (responseBody.contains("PARTICIPANT_ID_INVALID") || + responseBody.contains("member not found"))) { + return false; + } + // Other 400 errors are real problems + log.error("Bad request when checking chat membership: userId={}, chatId={}, response={}", + telegramUserId, chatId, responseBody); + throw new IllegalStateException("Telegram API bad request", e); + } catch (Exception e) { + // timeout, 5xx, network, etc + log.error("Error checking chat membership: userId={}, chatId={}", telegramUserId, chatId, e); + throw new IllegalStateException("Telegram API error", e); + } + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class TelegramGetChatMemberResponse { + @JsonProperty("ok") + private Boolean ok; + + @JsonProperty("result") + private ChatMemberResult result; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ChatMemberResult { + @JsonProperty("status") + private String status; + } +} + diff --git a/src/main/java/com/lottery/lottery/service/TransactionService.java b/src/main/java/com/lottery/lottery/service/TransactionService.java new file mode 100644 index 0000000..b291e0d --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/TransactionService.java @@ -0,0 +1,234 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.TransactionDto; +import com.lottery.lottery.model.Transaction; +import com.lottery.lottery.repository.TransactionRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Service for managing transaction records. + * Creates transaction entries for all user balance changes. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class TransactionService { + + private final TransactionRepository transactionRepository; + private final LocalizationService localizationService; + + /** + * Creates a deposit transaction. + * + * @param userId User ID + * @param amount Amount in bigint format (positive) + */ + @Transactional + public void createDepositTransaction(Integer userId, Long amount) { + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(amount) + .type(Transaction.TransactionType.DEPOSIT) + .build(); + transactionRepository.save(transaction); + log.debug("Created deposit transaction: userId={}, amount={}", userId, amount); + } + + /** + * Creates a withdrawal transaction. + * + * @param userId User ID + * @param amount Amount in bigint format (positive, will be stored as negative) + */ + @Transactional + public void createWithdrawalTransaction(Integer userId, Long amount) { + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(-amount) // Store as negative + .type(Transaction.TransactionType.WITHDRAWAL) + .build(); + transactionRepository.save(transaction); + log.debug("Created withdrawal transaction: userId={}, amount={}", userId, amount); + } + + /** + * Creates a win transaction. + * + * @param userId User ID + * @param amount Amount in bigint format (positive, total payout) + * @param roundId Round ID + */ + @Transactional + public void createWinTransaction(Integer userId, Long amount, Long roundId) { + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(amount) + .type(Transaction.TransactionType.WIN) + .roundId(roundId) + .build(); + transactionRepository.save(transaction); + log.debug("Created win transaction: userId={}, amount={}, roundId={}", userId, amount, roundId); + } + + /** + * Creates a bet transaction. + * + * @param userId User ID + * @param amount Amount in bigint format (positive, will be stored as negative) + * @param roundId Round ID + */ + @Transactional + public void createBetTransaction(Integer userId, Long amount, Long roundId) { + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(-amount) // Store as negative + .type(Transaction.TransactionType.BET) + .roundId(roundId) + .build(); + transactionRepository.save(transaction); + log.debug("Created bet transaction: userId={}, amount={}, roundId={}", userId, amount, roundId); + } + + /** + * Creates a task bonus transaction. + * + * @param userId User ID + * @param amount Amount in bigint format (positive) + * @param taskId Task ID + */ + @Transactional + public void createTaskBonusTransaction(Integer userId, Long amount, Integer taskId) { + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(amount) + .type(Transaction.TransactionType.TASK_BONUS) + .taskId(taskId) + .build(); + transactionRepository.save(transaction); + log.debug("Created task bonus transaction: userId={}, amount={}, taskId={}", userId, amount, taskId); + } + + /** + * Creates a daily bonus transaction. + * + * @param userId User ID + * @param amount Amount in bigint format (positive) + */ + @Transactional + public void createDailyBonusTransaction(Integer userId, Long amount) { + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(amount) + .type(Transaction.TransactionType.DAILY_BONUS) + .taskId(null) // Daily bonus doesn't have taskId + .build(); + transactionRepository.save(transaction); + log.debug("Created daily bonus transaction: userId={}, amount={}", userId, amount); + } + + /** + * Creates a cancellation of withdrawal transaction. + * Used when admin cancels a payout - refunds tickets to user. + * + * @param userId User ID + * @param amount Amount in bigint format (positive, refund amount) + * @param createdAt Timestamp to use (from payout resolved_at) + */ + @Transactional + public void createCancellationOfWithdrawalTransaction(Integer userId, Long amount, Instant createdAt) { + Transaction transaction = Transaction.builder() + .userId(userId) + .amount(amount) // Positive amount (credit back to user) + .type(Transaction.TransactionType.CANCELLATION_OF_WITHDRAWAL) + .taskId(null) + .roundId(null) + .createdAt(createdAt) // Set custom timestamp (@PrePersist will check if null) + .build(); + transactionRepository.save(transaction); + log.info("Created cancellation of withdrawal transaction: userId={}, amount={}, createdAt={}", + userId, amount, createdAt); + } + + /** + * Gets transactions for a user with pagination. + * Returns 50 transactions per page, ordered by creation time descending (newest first). + * + * @param userId User ID + * @param page Page number (0-indexed) + * @param timezone Optional timezone (e.g., "Europe/London"). If null, uses UTC. + * @param languageCode User's language code for localization (e.g., "EN", "RU") + * @return Page of transactions + */ + @Transactional(readOnly = true) + public Page getUserTransactions(Integer userId, int page, String timezone, String languageCode) { + // Hardcoded page size: 50 transactions per page + Pageable pageable = PageRequest.of(page, 50); + + Page transactions = transactionRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable); + + // Determine timezone to use + ZoneId zoneId; + try { + zoneId = (timezone != null && !timezone.trim().isEmpty()) + ? ZoneId.of(timezone) + : ZoneId.of("UTC"); + } catch (Exception e) { + // Invalid timezone, fallback to UTC + zoneId = ZoneId.of("UTC"); + } + + // Get localized "at" word + String atWord = localizationService.getMessage("dateTime.at", languageCode); + if (atWord == null || atWord.isEmpty()) { + atWord = "at"; // Fallback to English + } + + // Create formatter with localized "at" word + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM '" + atWord + "' HH:mm") + .withZone(zoneId); + + return transactions.map(transaction -> { + // Format date + String date = formatter.format(transaction.getCreatedAt()); + + // Send enum value as string (e.g., "TASK_BONUS", "WIN") - frontend will handle localization + String typeEnumValue = transaction.getType().name(); + + // For DAILY_BONUS, don't include taskId (it should be null) + // For TASK_BONUS, include taskId + Integer taskIdToInclude = (transaction.getType() == Transaction.TransactionType.DAILY_BONUS) + ? null + : transaction.getTaskId(); + + return TransactionDto.builder() + .amount(transaction.getAmount()) + .date(date) + .type(typeEnumValue) // Send enum value, not localized string + .taskId(taskIdToInclude) + .roundId(transaction.getRoundId()) + .build(); + }); + } + + // Note: Transaction type localization is now handled in the frontend. + // Backend sends enum values (TASK_BONUS, WIN, etc.) and frontend translates them. + // This method is no longer used but kept for reference. + @Deprecated + private String mapTransactionTypeToDisplayName(Transaction.TransactionType type, String languageCode) { + // This method is deprecated - frontend handles all transaction type localization + return type.name(); + } +} + diff --git a/src/main/java/com/lottery/lottery/service/UserService.java b/src/main/java/com/lottery/lottery/service/UserService.java new file mode 100644 index 0000000..fcb35b0 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/UserService.java @@ -0,0 +1,478 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.ReferralDto; +import com.lottery.lottery.model.UserA; +import com.lottery.lottery.model.UserB; +import com.lottery.lottery.model.UserD; +import com.lottery.lottery.repository.UserARepository; +import com.lottery.lottery.repository.UserBRepository; +import com.lottery.lottery.repository.UserDRepository; +import com.lottery.lottery.util.IpUtils; +import com.lottery.lottery.util.TimeProvider; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; +import java.util.Optional; + +/** + * Service for user management with sharded tables. + * Handles registration, login, and referral system. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class UserService { + + private final UserARepository userARepository; + private final UserBRepository userBRepository; + private final AvatarService avatarService; + private final UserDRepository userDRepository; + private final CountryCodeService countryCodeService; + private final LocalizationService localizationService; + + /** + * Gets user balance. + */ + public Long getUserBalance(Integer userId) { + return userBRepository.findById(userId) + .map(UserB::getBalanceA) + .orElse(null); + } + + /** + * Gets or creates a user based on Telegram data. + * Updates user data on each login. + * Handles referral system if start parameter is present (only for bot registration). + * + * @param tgUserData Parsed Telegram data (contains "user" map and optional "start" string for bot registration) + * @param request HTTP request for IP extraction + * @return UserA entity + */ + @Transactional + public UserA getOrCreateUser(Map tgUserData, HttpServletRequest request) { + // Extract user data and start parameter (only used for bot registration, not WebApp) + @SuppressWarnings("unchecked") + Map tgUser = (Map) tgUserData.get("user"); + String start = (String) tgUserData.get("start"); + Long telegramId = ((Number) tgUser.get("id")).longValue(); + String firstName = (String) tgUser.get("first_name"); + String lastName = (String) tgUser.get("last_name"); + String username = (String) tgUser.get("username"); + Boolean isPremium = (Boolean) tgUser.get("is_premium"); + String languageCode = (String) tgUser.get("language_code"); + + // Build screen_name from first_name and last_name + String screenName = buildScreenName(firstName, lastName); + + // device_code: language_code from initData (uppercase), max 5 chars (DB column limit). + // Telegram may send BCP 47 codes like "en-US", "zh-Hans" which exceed VARCHAR(5). + String deviceCode = normalizeDeviceCode(languageCode); + + // Get client IP and convert to bytes + String clientIp = IpUtils.getClientIp(request); + byte[] ipBytes = IpUtils.ipToBytes(clientIp); + + // Determine if this is a bot registration (bot registration always has start parameter set, even if null) + // WebApp initData never has start parameter (it's always missing, not null) + boolean isBotRegistration = tgUserData.containsKey("start"); + + // Get country code from IP + // For bot registration, IP will be bot server's IP (incorrect), so use "XX" for new users + // For existing users, country_code is preserved anyway + String countryCode; + if (isBotRegistration) { + // Bot registration: can't determine user's real country from bot server IP + // Set to "XX" for new users (existing users preserve their original country_code) + countryCode = "XX"; + log.debug("Bot registration detected: using country_code=XX (cannot determine from bot server IP)"); + } else { + // WebApp registration: IP is user's real IP, so we can determine country + countryCode = countryCodeService.getCountryCode(clientIp); + } + + // Get current timestamp + long nowSeconds = TimeProvider.nowSeconds(); + + // Check if user exists + Optional existingUserOpt = userARepository.findByTelegramId(telegramId); + + if (existingUserOpt.isPresent()) { + // User exists + UserA userA = existingUserOpt.get(); + + // Use the isBotRegistration variable already determined above + // Bot registration = user opening referral link when already registered (no updates) + // WebApp login = actual login/update (update fields) + if (isBotRegistration) { + // Bot registration: User is already registered, just opening referral link + // Do NOT update any fields - just return the user as-is + log.debug("Existing user opening referral link: userId={}, telegramId={}", userA.getId(), telegramId); + return userA; + } else { + // WebApp login: User is actually logging in/updating, so update their data + log.debug("Updating user data on login: userId={}", userA.getId()); + + // Update avatar if Telegram file_id changed (only downloads if changed) + avatarService.updateAvatarIfNeeded(userA, telegramId, tgUser); + + // Update user data on login (including country_code from real IP) + updateUserOnLogin(userA, screenName, username, isPremium, countryCode, deviceCode, languageCode, ipBytes, nowSeconds); + return userA; + } + } else { + // New user - create in all 3 tables + log.info("New user registration: telegramId={}, referral={}", telegramId, start != null && !start.isEmpty() ? "yes" : "no"); + try { + return createNewUser(telegramId, screenName, username, isPremium, languageCode, countryCode, deviceCode, ipBytes, nowSeconds, start, tgUser); + } catch (Exception e) { + log.warn("New user creation failed for telegramId={}, possible duplicate or DB error: {}", telegramId, e.getMessage()); + throw e; + } + } + } + + // Supported languages for the application + private static final java.util.Set SUPPORTED_LANGUAGES = java.util.Set.of( + "EN", "RU", "DE", "IT", "NL", "PL", "FR", "ES", "ID", "TR" + ); + + /** + * Updates user data on login (when session is created via WebApp). + * Sets language_code only if it's null, empty, or 'XX' (not set yet). + * + * @param userA User to update + * @param screenName Updated screen name + * @param username Updated username + * @param isPremium Updated premium status + * @param countryCode Country code from user's real IP (WebApp only, not bot) + * @param deviceCode Device code (always updated) + * @param languageCode Language code from initData (only set if user's languageCode is null, empty, or 'XX') + * @param ipBytes IP address bytes + * @param nowSeconds Current timestamp + */ + private void updateUserOnLogin(UserA userA, String screenName, String username, Boolean isPremium, + String countryCode, String deviceCode, String languageCode, + byte[] ipBytes, long nowSeconds) { + userA.setScreenName(screenName); + userA.setTelegramName(username != null ? username : "-"); + userA.setIsPremium(isPremium != null && isPremium ? 1 : 0); + // Update country_code from user's real IP (WebApp login) + userA.setCountryCode(countryCode); + // Update device_code always + userA.setDeviceCode(deviceCode != null ? deviceCode.toUpperCase() : "XX"); + // Update language_code only if it's null, empty, or 'XX' (not set yet) + String currentLanguageCode = userA.getLanguageCode(); + if (currentLanguageCode == null || currentLanguageCode.isEmpty() || "XX".equals(currentLanguageCode)) { + String newLanguageCode = determineLanguageCode(languageCode); + userA.setLanguageCode(newLanguageCode); + log.debug("Set language_code on login: userId={}, languageCode={}", userA.getId(), newLanguageCode); + } else { + log.debug("Preserved existing language_code on login: userId={}, languageCode={}", userA.getId(), currentLanguageCode); + } + userA.setIp(ipBytes); + userA.setDateLogin((int) nowSeconds); + + userARepository.save(userA); + log.debug("Updated user data on login: userId={}", userA.getId()); + } + + /** + * Determines the language code to use. + * If the language from initData is supported, use it. Otherwise, default to EN. + */ + private String determineLanguageCode(String languageCodeFromInitData) { + if (languageCodeFromInitData == null || languageCodeFromInitData.isEmpty()) { + return "EN"; + } + String upperCode = languageCodeFromInitData.toUpperCase(); + // Check if it's a supported language + if (SUPPORTED_LANGUAGES.contains(upperCode)) { + return upperCode; + } + // Default to EN if not supported + return "EN"; + } + + /** + * Updates user's language code (called when user changes language in app header). + */ + @Transactional + public void updateLanguageCode(Integer userId, String languageCode) { + Optional userOpt = userARepository.findById(userId); + if (userOpt.isPresent()) { + UserA user = userOpt.get(); + user.setLanguageCode(languageCode != null && languageCode.length() == 2 ? languageCode.toUpperCase() : "XX"); + userARepository.save(user); + log.debug("Updated language_code for userId={}: {}", userId, user.getLanguageCode()); + } + } + + /** + * Creates a new user in all 3 tables with referral handling. + */ + private UserA createNewUser(Long telegramId, String screenName, String username, Boolean isPremium, + String languageCode, String countryCode, String deviceCode, + byte[] ipBytes, long nowSeconds, String start, Map tgUser) { + + // Determine language code (use supported language or default to EN) + String determinedLanguageCode = determineLanguageCode(languageCode); + + // Create UserA (avatar will be set after user is created and we have userId) + UserA userA = UserA.builder() + .screenName(screenName) + .telegramId(telegramId) + .telegramName(username != null ? username : "-") + .isPremium(isPremium != null && isPremium ? 1 : 0) + .languageCode(determinedLanguageCode) + .countryCode(countryCode) + .deviceCode(deviceCode != null ? deviceCode.toUpperCase() : "XX") + .ip(ipBytes) + .dateReg((int) nowSeconds) + .dateLogin((int) nowSeconds) + .banned(0) + .build(); + + userA = userARepository.save(userA); + Integer userId = userA.getId(); + + log.info("New user created: userId={}, telegramId={}", userId, telegramId); + + // Update avatar (downloads and saves, stores URL in DB with ?v=timestamp) + avatarService.updateAvatarIfNeeded(userA, telegramId, tgUser); + + // Create UserB with same ID + // Initial balance_a is 5,000,000 (5.00 tickets in bigint format) + UserB userB = UserB.builder() + .id(userId) + .balanceA(5_000_000L) + .balanceB(0L) + .depositTotal(0L) + .depositCount(0) + .withdrawTotal(0L) + .withdrawCount(0) + .build(); + userBRepository.save(userB); + + // Create UserD with referral handling + UserD userD = createUserDWithReferral(userId, screenName, start); + userDRepository.save(userD); + + return userA; + } + + /** + * Creates UserD entity with referral chain setup. + * IMPORTANT: This method should ONLY be called for NEW users during initial registration. + * Referral cannot be changed after user registration. + * + * @param userId New user's ID + * @param screenName User's screen name (from db_users_a) + * @param start Referral parameter (from bot registration, not WebApp) + */ + private UserD createUserDWithReferral(Integer userId, String screenName, String start) { + log.debug("Creating UserD with referral: userId={}, start={}", userId, start); + + // Defensive check: Ensure UserD doesn't already exist (should never happen, but safety check) + Optional existingUserD = userDRepository.findById(userId); + if (existingUserD.isPresent()) { + log.error("CRITICAL: Attempted to create UserD for existing userId={}. UserD already exists. Skipping UserD creation.", userId); + return existingUserD.get(); // Return existing UserD without modification + } + + UserD.UserDBuilder builder = UserD.builder() + .id(userId) + .screenName(screenName != null ? screenName : "-") + .refererId1(0) + .refererId2(0) + .refererId3(0) + .refererId4(0) + .refererId5(0) + .masterId(1) // Default master_id = 1 + .referals1(0) + .referals2(0) + .referals3(0) + .referals4(0) + .referals5(0) + .fromReferals1(0L) + .fromReferals2(0L) + .fromReferals3(0L) + .fromReferals4(0L) + .fromReferals5(0L) + .toReferer1(0L) + .toReferer2(0L) + .toReferer3(0L) + .toReferer4(0L) + .toReferer5(0L); + + if (start != null && !start.isEmpty()) { + try { + Integer refererId = Integer.parseInt(start); + + // Input validation: bounds check referral ID + if (refererId <= 0) { + log.warn("Invalid referral ID (non-positive) - userId={}, refererId={}", userId, refererId); + } else if (refererId > 2147483647) { + log.warn("Invalid referral ID (too large) - userId={}, refererId={}", userId, refererId); + } else if (refererId.equals(userId)) { + log.warn("Self-referral attempt blocked - userId={}", userId); + } else { + // Validate referer exists in UserA (main user table) + Optional refererUserAOpt = userARepository.findById(refererId); + if (refererUserAOpt.isEmpty()) { + log.warn("Invalid referer (not found) - userId={}, refererId={}", userId, refererId); + } else { + UserA refererUserA = refererUserAOpt.get(); + + // Check if referer is banned + if (refererUserA.getBanned() != null && refererUserA.getBanned() > 0) { + log.warn("Banned referer attempt - userId={}, refererId={}", userId, refererId); + } else { + // Referer is valid, check UserD + Optional refererUserDOpt = userDRepository.findById(refererId); + + if (refererUserDOpt.isPresent()) { + UserD refererUserD = refererUserDOpt.get(); + + // Set referral chain: shift referer's chain down by 1 level (3-level system) + builder.refererId1(refererId) + .masterId(refererUserD.getMasterId()) + .refererId2(refererUserD.getRefererId1()) + .refererId3(refererUserD.getRefererId2()); + + // Increment referal counts for all 3 levels up the chain + setupReferralChain(userId, refererId); + log.info("Referral chain set: userId={}, refererId={}", userId, refererId); + } else { + log.warn("Referer missing UserD record - userId={}, refererId={}", userId, refererId); + builder.refererId1(refererId); + } + } + } + } + } catch (NumberFormatException e) { + log.warn("Invalid referral format - userId={}, start='{}'", userId, start); + } + } + + UserD userD = builder.build(); + log.debug("Created UserD: userId={}, refererId1={}, refererId2={}, refererId3={}", + userId, userD.getRefererId1(), userD.getRefererId2(), userD.getRefererId3()); + + return userD; + } + + /** + * Sets up referral chain and increments referal counts for all 3 levels. + * Example: If user D registers with referer C, increments: + * - referals_1 for C + * - referals_2 for B (C's referer_id_1) + * - referals_3 for A (B's referer_id_1) + */ + private void setupReferralChain(Integer newUserId, Integer refererId) { + log.debug("Setting up referral chain: newUserId={}, refererId={}", newUserId, refererId); + + // Level 1: Direct referer + userDRepository.incrementReferals1(refererId); + + Optional level1Opt = userDRepository.findById(refererId); + if (level1Opt.isEmpty()) { + log.warn("Level 1 referer not found after increment: refererId={}", refererId); + return; + } + + UserD level1 = level1Opt.get(); + + // Level 2 + if (level1.getRefererId1() > 0) { + userDRepository.incrementReferals2(level1.getRefererId1()); + + Optional level2Opt = userDRepository.findById(level1.getRefererId1()); + if (level2Opt.isPresent()) { + UserD level2 = level2Opt.get(); + + // Level 3 + if (level2.getRefererId1() > 0) { + userDRepository.incrementReferals3(level2.getRefererId1()); + } + } + } + } + + /** Max length for device_code column (db_users_a). */ + private static final int DEVICE_CODE_MAX_LENGTH = 5; + + /** + * Normalizes Telegram language_code for storage in device_code (VARCHAR(5)). + * Uses primary part of BCP 47 codes (e.g. "en-US" -> "EN") and truncates to 5 chars. + */ + private static String normalizeDeviceCode(String languageCode) { + if (languageCode == null || languageCode.isEmpty()) { + return "XX"; + } + String primary = languageCode.contains("-") ? languageCode.split("-", 2)[0] : languageCode; + String normalized = primary.toUpperCase().trim(); + return normalized.length() > DEVICE_CODE_MAX_LENGTH ? normalized.substring(0, DEVICE_CODE_MAX_LENGTH) : normalized; + } + + /** + * Builds screen_name from first_name and last_name. + */ + private String buildScreenName(String firstName, String lastName) { + StringBuilder sb = new StringBuilder(); + if (firstName != null && !firstName.isEmpty()) { + sb.append(firstName); + } + if (lastName != null && !lastName.isEmpty()) { + if (sb.length() > 0) { + sb.append(" "); + } + sb.append(lastName); + } + String result = sb.toString().trim(); + return result.isEmpty() ? "-" : (result.length() > 75 ? result.substring(0, 75) : result); + } + + /** + * Gets user by ID. + */ + public Optional getUserById(Integer userId) { + return userARepository.findById(userId); + } + + /** + * Gets user by Telegram ID. + */ + public Optional getUserByTelegramId(Long telegramId) { + return userARepository.findByTelegramId(telegramId); + } + + /** + * Gets referrals for a specific level with pagination. + * Always returns 50 results per page. + * + * @param userId The user ID to get referrals for + * @param level The referral level (1, 2, or 3) + * @param page Page number (0-indexed) + * @return Page of referrals with name and commission + */ + public Page getReferrals(Integer userId, Integer level, Integer page) { + // Fixed page size of 50 to prevent database overload + Pageable pageable = PageRequest.of(page, 50); + + return switch (level) { + case 1 -> userDRepository.findReferralsLevel1(userId, pageable); + case 2 -> userDRepository.findReferralsLevel2(userId, pageable); + case 3 -> userDRepository.findReferralsLevel3(userId, pageable); + default -> throw new IllegalArgumentException( + localizationService.getMessage("user.error.referralLevelInvalid", String.valueOf(level))); + }; + } +} + diff --git a/src/main/java/com/lottery/lottery/service/WithdrawalStatusSyncService.java b/src/main/java/com/lottery/lottery/service/WithdrawalStatusSyncService.java new file mode 100644 index 0000000..d125134 --- /dev/null +++ b/src/main/java/com/lottery/lottery/service/WithdrawalStatusSyncService.java @@ -0,0 +1,127 @@ +package com.lottery.lottery.service; + +import com.lottery.lottery.dto.WithdrawalInfoApiResponse; +import com.lottery.lottery.model.Payout; +import com.lottery.lottery.repository.PayoutRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * Cron job: once per minute, fetches PROCESSING payouts (with payment_id), checks status + * via GET api/v1/withdrawals-info/{payment_id}, and updates DB (status, updated_at, and + * for COMPLETED/CANCELLED the same logic as admin complete/cancel). + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class WithdrawalStatusSyncService { + + private static final int BATCH_SIZE = 20; + + private final PayoutRepository payoutRepository; + private final CryptoWithdrawalService cryptoWithdrawalService; + private final PayoutService payoutService; + + @Scheduled(cron = "0 * * * * *") // Every minute at second 0 + @Transactional + public void syncWithdrawalStatuses() { + List payouts = payoutRepository.findByTypeAndStatusInAndPaymentIdIsNotNullOrderByUpdatedAtAsc( + Payout.PayoutType.CRYPTO, + Set.of(Payout.PayoutStatus.PROCESSING, Payout.PayoutStatus.WAITING), + PageRequest.of(0, BATCH_SIZE) + ); + log.info("Withdrawal sync cron: starting, {} CRYPTO payout(s) to process", payouts.size()); + if (payouts.isEmpty()) { + return; + } + Instant now = Instant.now(); + + for (Payout payout : payouts) { + Integer paymentId = payout.getPaymentId(); + try { + if (paymentId == null) { + log.warn("Withdrawal sync: skipping payout id={} (payment_id is null)", payout.getId()); + continue; + } + log.debug("Withdrawal sync: checking payout id={}, paymentId={}, status={}", payout.getId(), paymentId, payout.getStatus()); + Optional infoOpt = cryptoWithdrawalService.getWithdrawalInfo(paymentId); + if (infoOpt.isEmpty() || infoOpt.get().getResult() == null + || infoOpt.get().getResult().getPaymentList() == null + || infoOpt.get().getResult().getPaymentList().isEmpty()) { + log.warn("Withdrawal info empty or no payment_list: payoutId={}, paymentId={}", payout.getId(), paymentId); + payout.setUpdatedAt(now); + payoutRepository.save(payout); + continue; + } + WithdrawalInfoApiResponse.PaymentItem paymentItem = infoOpt.get().getResult().getPaymentList().get(0); + String apiStatus = paymentItem.getStatus(); + if (paymentItem.getTxhash() != null && !paymentItem.getTxhash().isBlank()) { + payout.setTxhash(paymentItem.getTxhash()); + } + Payout.PayoutStatus newStatus = mapApiStatusToPayoutStatus(apiStatus); + if (newStatus == null) { + log.warn("Unknown withdrawal API status: payoutId={}, paymentId={}, status={}", payout.getId(), paymentId, apiStatus); + payout.setUpdatedAt(now); + payoutRepository.save(payout); + continue; + } + + switch (newStatus) { + case PROCESSING: + payout.setUpdatedAt(now); + payoutRepository.save(payout); + log.debug("Withdrawal sync: payout id={} still PROCESSING, updated_at refreshed", payout.getId()); + break; + case WAITING: + payout.setStatus(Payout.PayoutStatus.WAITING); + payout.setUpdatedAt(now); + payoutRepository.save(payout); + log.debug("Withdrawal sync: payout id={}, paymentId={} -> WAITING", payout.getId(), paymentId); + break; + case COMPLETED: + payoutRepository.save(payout); // persist txhash if set + payoutService.markPayoutCompleted(payout.getId()); + log.debug("Withdrawal sync: payout id={}, paymentId={} -> COMPLETED", payout.getId(), paymentId); + break; + case CANCELLED: + payoutRepository.save(payout); // persist txhash if set + payoutService.markPayoutCancelled(payout.getId()); + log.debug("Withdrawal sync: payout id={}, paymentId={} -> CANCELLED", payout.getId(), paymentId); + break; + default: + payout.setUpdatedAt(now); + payoutRepository.save(payout); + } + } catch (Exception e) { + log.warn("Withdrawal sync error for payout id={}, paymentId={}: {}", payout.getId(), paymentId, e.getMessage(), e); + // Still update updated_at so we don't block the batch forever on one bad record + payout.setUpdatedAt(now); + payoutRepository.save(payout); + } + } + log.debug("Withdrawal sync cron: finished"); + } + + /** -1 PROCESSING, 0 WAITING, 1 COMPLETED, 2 CANCELLED */ + private static Payout.PayoutStatus mapApiStatusToPayoutStatus(String status) { + if (status == null) { + return null; + } + return switch (status.trim()) { + case "-1" -> Payout.PayoutStatus.PROCESSING; + case "0" -> Payout.PayoutStatus.WAITING; + case "1" -> Payout.PayoutStatus.COMPLETED; + case "2" -> Payout.PayoutStatus.CANCELLED; + default -> null; + }; + } +} diff --git a/src/main/java/com/honey/honey/util/IpUtils.java b/src/main/java/com/lottery/lottery/util/IpUtils.java similarity index 99% rename from src/main/java/com/honey/honey/util/IpUtils.java rename to src/main/java/com/lottery/lottery/util/IpUtils.java index 4dc06e9..8b71e1a 100644 --- a/src/main/java/com/honey/honey/util/IpUtils.java +++ b/src/main/java/com/lottery/lottery/util/IpUtils.java @@ -1,4 +1,4 @@ -package com.honey.honey.util; +package com.lottery.lottery.util; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/lottery/lottery/util/TelegramTokenRedactor.java b/src/main/java/com/lottery/lottery/util/TelegramTokenRedactor.java new file mode 100644 index 0000000..e7b3de2 --- /dev/null +++ b/src/main/java/com/lottery/lottery/util/TelegramTokenRedactor.java @@ -0,0 +1,26 @@ +package com.lottery.lottery.util; + +import java.util.regex.Pattern; + +/** + * Redacts Telegram Bot API token from strings before logging. + * Token appears in URLs like https://api.telegram.org/bot123456:AAHxxx.../methodName + */ +public final class TelegramTokenRedactor { + + private static final Pattern BOT_TOKEN_IN_URL = Pattern.compile("(/bot)([^/]+)(/)"); + + private TelegramTokenRedactor() { + } + + /** + * Replaces the bot token in Telegram API URLs with a redaction placeholder. + * Safe to call with null (returns null). + */ + public static String redact(String message) { + if (message == null || message.isEmpty()) { + return message; + } + return BOT_TOKEN_IN_URL.matcher(message).replaceAll("$1***REDACTED***$3"); + } +} diff --git a/src/main/java/com/honey/honey/util/TimeProvider.java b/src/main/java/com/lottery/lottery/util/TimeProvider.java similarity index 94% rename from src/main/java/com/honey/honey/util/TimeProvider.java rename to src/main/java/com/lottery/lottery/util/TimeProvider.java index 9c4fee3..d24e7e3 100644 --- a/src/main/java/com/honey/honey/util/TimeProvider.java +++ b/src/main/java/com/lottery/lottery/util/TimeProvider.java @@ -1,4 +1,4 @@ -package com.honey.honey.util; +package com.lottery.lottery.util; import java.time.Instant; @@ -25,3 +25,4 @@ public class TimeProvider { } } + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4f7ddbf..8dcc5aa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,17 +1,32 @@ server: port: 8080 + # Tomcat thread pool settings for high concurrency + tomcat: + threads: + max: 500 + min-spare: 50 + # Allow large headers (for Telegram auth tokens) + max-http-header-size: 20KB + # Graceful shutdown for zero-downtime deployments + shutdown: graceful spring: application: - name: honey-be + name: lottery-be + lifecycle: + timeout-per-shutdown-phase: 30s datasource: - url: ${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/honey_db} + url: ${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/lottery_db} username: ${SPRING_DATASOURCE_USERNAME:root} password: ${SPRING_DATASOURCE_PASSWORD:password} driver-class-name: com.mysql.cj.jdbc.Driver hikari: + # Connection pool settings for high concurrency + maximum-pool-size: 50 + minimum-idle: 20 connection-timeout: 30000 + idle-timeout: 600000 initialization-fail-timeout: -1 jpa: @@ -34,6 +49,11 @@ spring: telegram: bot-token: ${TELEGRAM_BOT_TOKEN} + # Bot token for checking channel membership (separate from main bot token) + channel-checker-bot-token: ${TELEGRAM_CHANNEL_CHECKER_BOT_TOKEN:} + # Channel ID for follow tasks (e.g., "@lottery_2026_test_channel" or numeric ID) + follow-task-channel-id: ${TELEGRAM_FOLLOW_TASK_CHANNEL_ID:@win_spin_news} + follow-task-channel-id-2: ${TELEGRAM_FOLLOW_TASK_CHANNEL_ID_2:@win_spin_withdrawals} app: session: @@ -46,18 +66,85 @@ app: # Maximum number of batches to process per cleanup run max-batches-per-run: ${APP_SESSION_CLEANUP_MAX_BATCHES:20} + data-cleanup: + # Batch cleanup configuration for game_round_participants and transactions + cleanup: + # Number of records to delete per batch + batch-size: ${APP_DATA_CLEANUP_BATCH_SIZE:5000} + # Maximum number of batches to process per cleanup run + max-batches-per-run: ${APP_DATA_CLEANUP_MAX_BATCHES:20} + # Sleep time in milliseconds between batches (to let database process WebSocket inserts) + batch-sleep-ms: ${APP_DATA_CLEANUP_BATCH_SLEEP_MS:500} + + avatar: + # Storage path for avatar files (relative to app root or absolute path) + # Railway: ephemeral filesystem (acceptable for testing) + # Inferno: should be mounted as Docker volume (e.g., /data/avatars) + storage-path: ${APP_AVATAR_STORAGE_PATH:./data/avatars} + # Enable Spring ResourceHandler for avatar serving + # Railway: Set ENABLE_SPRING_AVATAR_HANDLER=true (Spring Boot serves avatars) + # VPS: Leave unset or false (Nginx serves avatars via location ^~ /avatars/) + enable-spring-handler: ${ENABLE_SPRING_AVATAR_HANDLER:false} + # Public base URL for avatar URLs (e.g., https://api.example.com) + # If empty, will use server's base URL + public-base-url: ${APP_AVATAR_PUBLIC_BASE_URL:} + # Maximum avatar file size in bytes (default: 2MB) + max-size-bytes: ${APP_AVATAR_MAX_SIZE_BYTES:2097152} + # Maximum avatar dimension in pixels (default: 110x110) + max-dimension: ${APP_AVATAR_MAX_DIMENSION:110} + # Avatar URL cache TTL in minutes (default: 5 minutes) + cache-ttl-minutes: ${APP_AVATAR_CACHE_TTL_MINUTES:5} + + websocket: + # Allowed origins for WebSocket CORS (comma-separated) + # Default includes production domain and Telegram WebView domains + allowed-origins: ${APP_WEBSOCKET_ALLOWED_ORIGINS:https://win-spin.live,https://lottery-fe-production.up.railway.app,https://web.telegram.org,https://webk.telegram.org,https://webz.telegram.org} + + # Lottery bot scheduler: auto-joins bots from lottery_bot_configs into joinable rounds. Toggle via admin Feature Switches (lottery_bot_scheduler_enabled). + # Bet amount is decided in-process by persona + loss-streak and zone logic (no external API). + lottery-bot: + schedule-fixed-delay-ms: ${APP_LOTTERY_BOT_SCHEDULE_FIXED_DELAY_MS:5000} + + # Secret token for remote bet API (GET /api/remotebet/{token}?user_id=&room=&amount=). No auth; enable via Feature Switchers in admin. + remote-bet: + token: ${APP_REMOTE_BET_TOKEN:} + # Token in path for open user-check API: GET /api/check_user/{token}/{telegramId}. Set on VPS only. + check-user: + token: ${APP_CHECK_USER_TOKEN:} + # Token in path for 3rd party deposit webhook: POST /api/deposit_webhook/{token}. Body: user_id, usd_amount (bigint: 1_000_000 = 1 USD). Set on VPS only. + deposit-webhook: + token: ${APP_PASSIM_WEBHOOK_TOKEN:} + # Token in path for Telegram webhook: POST /api/telegram/webhook/{token}. Set on VPS only; use same value when registering webhook URL with Telegram. + telegram-webhook: + token: ${APP_TELEGRAM_WEBHOOK_TOKEN:} + # Token in path for notify broadcast: POST /api/notify_broadcast/{token} and POST /api/notify_broadcast/{token}/stop. Set on VPS only. + notify-broadcast: + token: ${APP_NOTIFY_BROADCAST_TOKEN:} + + # Crypto payment/payout API (deposit methods, etc.). Base URL and API key for https://spin-passim.tech/ + crypto-api: + base-url: ${APP_CRYPTO_API_BASE_URL:https://spin-passim.tech/} + api-key: ${APP_CRYPTO_API_KEY:} + + admin: + jwt: + # JWT secret key (MUST be at least 256 bits / 32 characters) + # Generate a secure random string for production + # Example: openssl rand -base64 32 + secret: ${APP_ADMIN_JWT_SECRET:change-this-to-a-secure-random-string-in-production-min-32-characters} + # JWT expiration time in milliseconds (default: 24 hours) + expiration: ${APP_ADMIN_JWT_EXPIRATION:86400000} + # GeoIP configuration # Set GEOIP_DB_PATH environment variable to use external file (recommended for production) # If not set, falls back to classpath:geoip/GeoLite2-Country.mmdb geoip: db-path: ${GEOIP_DB_PATH:} -logging: - level: - root: INFO - org.springframework.boot.context.config: DEBUG - org.springframework.core.env: DEBUG - com.honey: DEBUG +# Logging configuration moved to logback-spring.xml +# To use external logback-spring.xml on VPS, set system property: +# -Dlogging.config=/path/to/logback-spring.xml +# Or environment variable: LOGGING_CONFIG=/path/to/logback-spring.xml management: endpoints: diff --git a/src/main/resources/assets/bot_start.mp4 b/src/main/resources/assets/bot_start.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..d36c8dc993c461186ff3302f84be4fe22f3b2431 GIT binary patch literal 788556 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 z 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�r;30(k2AXXQ0);A@!unnM3 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/winspin_5.mp4 b/src/main/resources/assets/winspin_5.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..fd5bc5e60bfe85a1ec0b40fb92e9e37f782246e0 GIT binary patch literal 970941 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 z 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

nzt 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⪫=FPL4W>Q4Gu+ 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

IvYfH_wZiKRhNd^5TqPO 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

w_}q3vpdZrU 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%0DjV8QyAL)aD{5><__z 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 z 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�u#;(c}^#tmQ#M6;Fbg){Ev$b*N5NSX9Bh49KlBj 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/db/migration/V10__add_indexes_to_payouts.sql b/src/main/resources/db/migration/V10__add_indexes_to_payouts.sql new file mode 100644 index 0000000..2ba4464 --- /dev/null +++ b/src/main/resources/db/migration/V10__add_indexes_to_payouts.sql @@ -0,0 +1,7 @@ +-- Add indexes for faster queries on payouts table +-- Index for fetching user's payouts ordered by creation date +CREATE INDEX idx_payouts_user_created ON payouts(user_id, created_at DESC); + + + + diff --git a/src/main/resources/db/migration/V11__create_payments_table.sql b/src/main/resources/db/migration/V11__create_payments_table.sql new file mode 100644 index 0000000..4becc33 --- /dev/null +++ b/src/main/resources/db/migration/V11__create_payments_table.sql @@ -0,0 +1,22 @@ +-- Create payments table for Telegram Stars payments +CREATE TABLE IF NOT EXISTS payments ( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + order_id VARCHAR(255) NOT NULL UNIQUE COMMENT 'Unique order ID for Telegram invoice', + stars_amount INT NOT NULL COMMENT 'Amount in Stars', + tickets_amount BIGINT UNSIGNED NOT NULL COMMENT 'Tickets amount in bigint format (stars * 0.9 * 1,000,000)', + status VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING, COMPLETED, FAILED, CANCELLED', + telegram_payment_charge_id VARCHAR(255) NULL COMMENT 'Telegram payment charge ID', + telegram_provider_payment_charge_id VARCHAR(255) NULL COMMENT 'Telegram provider payment charge ID', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + completed_at TIMESTAMP NULL, + INDEX idx_user_id (user_id), + INDEX idx_order_id (order_id), + INDEX idx_status (status), + INDEX idx_created_at (created_at), + FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + + + diff --git a/src/main/resources/db/migration/V12__create_transactions_table.sql b/src/main/resources/db/migration/V12__create_transactions_table.sql new file mode 100644 index 0000000..5154dfb --- /dev/null +++ b/src/main/resources/db/migration/V12__create_transactions_table.sql @@ -0,0 +1,16 @@ +-- Create transactions table for transaction history +CREATE TABLE transactions ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + amount BIGINT NOT NULL COMMENT 'Amount in bigint format (positive for credits, negative for debits)', + type VARCHAR(50) NOT NULL COMMENT 'Transaction type: DEPOSIT, WITHDRAWAL, WIN, LOSS, TASK_BONUS', + task_id INT NULL COMMENT 'Task ID for TASK_BONUS type', + round_id BIGINT NULL COMMENT 'Round ID for WIN/LOSS type', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_user_id_created_at (user_id, created_at DESC), + INDEX idx_user_id_type (user_id, type), + FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + + diff --git a/src/main/resources/db/migration/V13__update_task_reward_type_to_tickets.sql b/src/main/resources/db/migration/V13__update_task_reward_type_to_tickets.sql new file mode 100644 index 0000000..919f283 --- /dev/null +++ b/src/main/resources/db/migration/V13__update_task_reward_type_to_tickets.sql @@ -0,0 +1,10 @@ +-- Update task reward_type from 'Stars' to 'Tickets' +-- Update all existing tasks +UPDATE tasks SET reward_type = 'Tickets' WHERE reward_type = 'Stars'; + +-- Update table default value and comment +ALTER TABLE tasks +MODIFY COLUMN reward_type VARCHAR(20) NOT NULL DEFAULT 'Tickets' COMMENT 'Tickets (all tasks use Tickets as reward type)'; + + + diff --git a/src/main/resources/db/migration/V14__update_other_task_title_to_tickets.sql b/src/main/resources/db/migration/V14__update_other_task_title_to_tickets.sql new file mode 100644 index 0000000..3444c93 --- /dev/null +++ b/src/main/resources/db/migration/V14__update_other_task_title_to_tickets.sql @@ -0,0 +1,9 @@ +-- Update "Other" task title and description from "$5" to Tickets format +-- requirement is 500000000 in bigint = 500 tickets +-- Title: "Top up Balance" (short for task list) +-- Description: "Top up Balance: 500 Tickets" (full for task modal) +UPDATE tasks +SET title = 'Top up Balance', + description = 'Top up Balance: 500 Tickets' +WHERE type = 'other' AND title LIKE '%$5%'; + diff --git a/src/main/resources/db/migration/V15__add_avatar_fields_to_users_a.sql b/src/main/resources/db/migration/V15__add_avatar_fields_to_users_a.sql new file mode 100644 index 0000000..1521371 --- /dev/null +++ b/src/main/resources/db/migration/V15__add_avatar_fields_to_users_a.sql @@ -0,0 +1,12 @@ +-- Add avatar_url and last_telegram_file_id columns to db_users_a +-- avatar_url: Stores the public URL with ?v=timestamp parameter for cache busting +-- last_telegram_file_id: Stores the Telegram file_id to avoid re-downloading unchanged avatars +ALTER TABLE `db_users_a` + ADD COLUMN `avatar_url` VARCHAR(500) DEFAULT NULL AFTER `banned`, + ADD COLUMN `last_telegram_file_id` VARCHAR(255) DEFAULT NULL AFTER `avatar_url`; + +-- Add index on avatar_url for faster lookups (optional, but helps if we query by avatar_url) +-- Note: We don't index last_telegram_file_id as it's only used during avatar update + + + diff --git a/src/main/resources/db/migration/V16__create_support_tickets_and_messages.sql b/src/main/resources/db/migration/V16__create_support_tickets_and_messages.sql new file mode 100644 index 0000000..aedb571 --- /dev/null +++ b/src/main/resources/db/migration/V16__create_support_tickets_and_messages.sql @@ -0,0 +1,33 @@ +-- Create support_tickets table +CREATE TABLE support_tickets ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + subject VARCHAR(100) NOT NULL, + status ENUM('OPENED', 'CLOSED') NOT NULL DEFAULT 'OPENED', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_user_id (user_id), + INDEX idx_status (status), + INDEX idx_user_status (user_id, status), + INDEX idx_created_at (created_at), + + FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create support_messages table +CREATE TABLE support_messages ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + ticket_id BIGINT NOT NULL, + user_id INT NOT NULL, + message VARCHAR(2000) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_ticket_id (ticket_id), + INDEX idx_user_id (user_id), + INDEX idx_ticket_created (ticket_id, created_at), + + FOREIGN KEY (ticket_id) REFERENCES support_tickets(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + diff --git a/src/main/resources/db/migration/V17__add_indexes_for_performance_and_cleanup.sql b/src/main/resources/db/migration/V17__add_indexes_for_performance_and_cleanup.sql new file mode 100644 index 0000000..9d87ab8 --- /dev/null +++ b/src/main/resources/db/migration/V17__add_indexes_for_performance_and_cleanup.sql @@ -0,0 +1,12 @@ +-- Add index on game_rounds for join query optimization +-- This helps with the query: SELECT p FROM GameRoundParticipant p WHERE p.userId = :userId +-- AND p.round.phase = 'RESOLUTION' AND p.round.resolvedAt IS NOT NULL ORDER BY p.round.resolvedAt DESC +CREATE INDEX idx_round_phase_resolved ON game_rounds (id, phase, resolved_at DESC); + +-- Add index on game_round_participants for cleanup by joined_at +CREATE INDEX idx_joined_at ON game_round_participants (joined_at); + +-- Add index on transactions for game history queries (filtering by WIN type) and cleanup by created_at +CREATE INDEX idx_type_created_at ON transactions (type, created_at); + + diff --git a/src/main/resources/db/migration/V18__remove_unused_indexes.sql b/src/main/resources/db/migration/V18__remove_unused_indexes.sql new file mode 100644 index 0000000..746407a --- /dev/null +++ b/src/main/resources/db/migration/V18__remove_unused_indexes.sql @@ -0,0 +1,9 @@ +-- Remove unused index on game_round_participants +-- This index was only used for cleanup, which is no longer performed on this table +-- Participants are now deleted immediately after rounds finish +DROP INDEX idx_joined_at ON game_round_participants; + +-- Update comment: idx_type_created_at is now used for game history queries (filtering by WIN type) +-- The cleanup query no longer filters by type, but the index is still useful for game history + + diff --git a/src/main/resources/db/migration/V19__add_daily_bonus_task.sql b/src/main/resources/db/migration/V19__add_daily_bonus_task.sql new file mode 100644 index 0000000..03cadb2 --- /dev/null +++ b/src/main/resources/db/migration/V19__add_daily_bonus_task.sql @@ -0,0 +1,8 @@ +-- Insert Daily Bonus task +-- reward_amount is in bigint format (1 ticket = 1000000) +-- requirement is 24 hours in milliseconds (86400000), but we'll use 0 as placeholder since we check claimed_at timestamp +-- The actual 24h check is done in TaskService.isTaskCompleted() for "daily" type +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) VALUES +('daily', 0, 1000000, 'Tickets', 1, 'Daily Bonus', 'Claim 1 free ticket every 24 hours'); + + diff --git a/src/main/resources/db/migration/V1__create_users_table.sql b/src/main/resources/db/migration/V1__create_users_table.sql deleted file mode 100644 index 7389d6c..0000000 --- a/src/main/resources/db/migration/V1__create_users_table.sql +++ /dev/null @@ -1,9 +0,0 @@ --- Create users table -CREATE TABLE IF NOT EXISTS users ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - telegram_id BIGINT NOT NULL UNIQUE, - username VARCHAR(255), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - INDEX idx_telegram_id (telegram_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - diff --git a/src/main/resources/db/migration/V3__replace_users_with_sharded_tables.sql b/src/main/resources/db/migration/V1__initial_schema.sql similarity index 60% rename from src/main/resources/db/migration/V3__replace_users_with_sharded_tables.sql rename to src/main/resources/db/migration/V1__initial_schema.sql index 6720f0c..7794759 100644 --- a/src/main/resources/db/migration/V3__replace_users_with_sharded_tables.sql +++ b/src/main/resources/db/migration/V1__initial_schema.sql @@ -1,27 +1,19 @@ --- Drop foreign key constraint from sessions table if it exists --- Find the foreign key name dynamically (MySQL auto-generates names like sessions_ibfk_1) -SET @fk_name = NULL; -SELECT CONSTRAINT_NAME INTO @fk_name -FROM information_schema.TABLE_CONSTRAINTS -WHERE CONSTRAINT_SCHEMA = DATABASE() -AND TABLE_NAME = 'sessions' -AND CONSTRAINT_TYPE = 'FOREIGN KEY' -LIMIT 1; - -SET @sql = IF(@fk_name IS NOT NULL, - CONCAT('ALTER TABLE sessions DROP FOREIGN KEY ', @fk_name), - 'DO 1'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- Drop old users table -DROP TABLE IF EXISTS users; +-- Create sessions table for Bearer token authentication +CREATE TABLE IF NOT EXISTS sessions ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + session_id_hash VARCHAR(255) NOT NULL UNIQUE, + user_id INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at TIMESTAMP NOT NULL, + INDEX idx_session_hash (session_id_hash), + INDEX idx_expires_at (expires_at), + INDEX idx_user_id (user_id), + INDEX idx_user_created (user_id, created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Create db_users_a table CREATE TABLE `db_users_a` ( - `id` int NOT NULL, + `id` int NOT NULL AUTO_INCREMENT, `screen_name` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '-', `telegram_id` bigint UNSIGNED DEFAULT NULL, `telegram_name` varchar(33) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '-', @@ -32,7 +24,11 @@ CREATE TABLE `db_users_a` ( `ip` varbinary(16) DEFAULT NULL, `date_reg` int NOT NULL DEFAULT '0', `date_login` int NOT NULL DEFAULT '0', - `banned` int NOT NULL DEFAULT '0' + `banned` int NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `telegram_id` (`telegram_id`), + KEY `telegram_name` (`telegram_name`), + KEY `ip` (`ip`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- Create db_users_b table @@ -43,7 +39,8 @@ CREATE TABLE `db_users_b` ( `deposit_total` bigint NOT NULL DEFAULT '0', `deposit_count` int NOT NULL DEFAULT '0', `withdraw_total` bigint NOT NULL DEFAULT '0', - `withdraw_count` int NOT NULL DEFAULT '0' + `withdraw_count` int NOT NULL DEFAULT '0', + KEY `id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- Create db_users_d table @@ -69,39 +66,21 @@ CREATE TABLE `db_users_d` ( `to_referer_2` bigint NOT NULL DEFAULT '0', `to_referer_3` bigint NOT NULL DEFAULT '0', `to_referer_4` bigint NOT NULL DEFAULT '0', - `to_referer_5` bigint NOT NULL DEFAULT '0' + `to_referer_5` bigint NOT NULL DEFAULT '0', + KEY `id` (`id`), + KEY `referer_id_1` (`referer_id_1`), + KEY `referer_id_2` (`referer_id_2`), + KEY `referer_id_3` (`referer_id_3`), + KEY `referer_id_4` (`referer_id_4`), + KEY `referer_id_5` (`referer_id_5`), + KEY `master_id` (`master_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; --- Add indexes for db_users_a -ALTER TABLE `db_users_a` - ADD PRIMARY KEY (`id`), - ADD UNIQUE KEY `telegram_id` (`telegram_id`), - ADD KEY `telegram_name` (`telegram_name`), - ADD KEY `ip` (`ip`); - --- Add indexes for db_users_b -ALTER TABLE `db_users_b` - ADD KEY `id` (`id`); - --- Add indexes for db_users_d -ALTER TABLE `db_users_d` - ADD KEY `id` (`id`), - ADD KEY `referer_id_1` (`referer_id_1`), - ADD KEY `referer_id_2` (`referer_id_2`), - ADD KEY `referer_id_3` (`referer_id_3`), - ADD KEY `referer_id_4` (`referer_id_4`), - ADD KEY `referer_id_5` (`referer_id_5`), - ADD KEY `master_id` (`master_id`); - --- Set auto increment for db_users_a -ALTER TABLE `db_users_a` - MODIFY `id` int NOT NULL AUTO_INCREMENT; - --- Update sessions table: change user_id from BIGINT to INT to match db_users_a.id -ALTER TABLE `sessions` - MODIFY `user_id` int NOT NULL; - -- Add foreign key constraint from sessions to db_users_a ALTER TABLE `sessions` ADD CONSTRAINT `fk_sessions_user_id` FOREIGN KEY (`user_id`) REFERENCES `db_users_a`(`id`) ON DELETE CASCADE; + + + + diff --git a/src/main/resources/db/migration/V20__create_user_daily_bonus_claims_table.sql b/src/main/resources/db/migration/V20__create_user_daily_bonus_claims_table.sql new file mode 100644 index 0000000..7cdcb86 --- /dev/null +++ b/src/main/resources/db/migration/V20__create_user_daily_bonus_claims_table.sql @@ -0,0 +1,14 @@ +-- Create user_daily_bonus_claims table for daily bonus claims +-- This table stores daily bonus claims with user information to avoid JOINs +CREATE TABLE IF NOT EXISTS `user_daily_bonus_claims` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `avatar_url` varchar(255) DEFAULT NULL, + `screen_name` varchar(75) NOT NULL DEFAULT '-', + `claimed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_claimed_at` (`claimed_at` DESC), + CONSTRAINT `fk_user_daily_bonus_claims_user` FOREIGN KEY (`user_id`) REFERENCES `db_users_a` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + diff --git a/src/main/resources/db/migration/V21__add_rounds_played_to_users_b.sql b/src/main/resources/db/migration/V21__add_rounds_played_to_users_b.sql new file mode 100644 index 0000000..5540fd8 --- /dev/null +++ b/src/main/resources/db/migration/V21__add_rounds_played_to_users_b.sql @@ -0,0 +1,6 @@ +-- Add rounds_played column to db_users_b table +-- This column tracks how many rounds each user has participated in +-- Used for third bet bonus logic instead of counting transactions +ALTER TABLE `db_users_b` + ADD COLUMN `rounds_played` int NOT NULL DEFAULT '0' AFTER `withdraw_count`; + diff --git a/src/main/resources/db/migration/V22__add_composite_index_payments_user_status.sql b/src/main/resources/db/migration/V22__add_composite_index_payments_user_status.sql new file mode 100644 index 0000000..4f91aa5 --- /dev/null +++ b/src/main/resources/db/migration/V22__add_composite_index_payments_user_status.sql @@ -0,0 +1,21 @@ +-- Add composite index for faster queries on payments table +-- Used for: SELECT SUM(stars_amount) WHERE user_id = ? AND status = 'COMPLETED' + +-- First, try to drop the index if it exists (for idempotency) +-- This will fail silently if the index doesn't exist, which is fine +SET @drop_sql = CONCAT('DROP INDEX idx_payments_user_status ON payments'); +SET @sql = IF( + (SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = DATABASE() + AND table_name = 'payments' + AND index_name = 'idx_payments_user_status') > 0, + @drop_sql, + 'SELECT 1' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- Now create the index +CREATE INDEX idx_payments_user_status ON payments(user_id, status); + diff --git a/src/main/resources/db/migration/V23__create_admins_table.sql b/src/main/resources/db/migration/V23__create_admins_table.sql new file mode 100644 index 0000000..78b8081 --- /dev/null +++ b/src/main/resources/db/migration/V23__create_admins_table.sql @@ -0,0 +1,13 @@ +-- Create separate admins table for internal staff +CREATE TABLE `admins` ( + `id` INT NOT NULL AUTO_INCREMENT, + `username` VARCHAR(50) NOT NULL UNIQUE, + `password_hash` VARCHAR(255) NOT NULL, + `role` VARCHAR(20) NOT NULL DEFAULT 'ROLE_ADMIN', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_username` (`username`), + KEY `idx_role` (`role`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + diff --git a/src/main/resources/db/migration/V24__add_admin_panel_indexes.sql b/src/main/resources/db/migration/V24__add_admin_panel_indexes.sql new file mode 100644 index 0000000..7c66f9c --- /dev/null +++ b/src/main/resources/db/migration/V24__add_admin_panel_indexes.sql @@ -0,0 +1,55 @@ +-- Add indexes for admin panel queries to improve performance +-- This migration adds indexes for filtering, searching, and dashboard statistics + +-- ============================================ +-- db_users_a indexes +-- ============================================ +-- Index for registration date filtering and counting (dashboard stats) +CREATE INDEX idx_users_date_reg ON db_users_a(date_reg); + +-- Index for login date filtering and counting active users (dashboard stats) +CREATE INDEX idx_users_date_login ON db_users_a(date_login); + +-- Index for banned status filtering +CREATE INDEX idx_users_banned ON db_users_a(banned); + +-- Index for country code filtering +CREATE INDEX idx_users_country_code ON db_users_a(country_code); + +-- Index for language code filtering +CREATE INDEX idx_users_language_code ON db_users_a(language_code); + +-- ============================================ +-- payments indexes +-- ============================================ +-- Composite index for dashboard queries: sumTicketsAmountByStatusAndCreatedAtAfter +-- This optimizes queries that filter by status and created_at (e.g., revenue stats) +CREATE INDEX idx_payments_status_created_at ON payments(status, created_at); + +-- ============================================ +-- payouts indexes +-- ============================================ +-- Composite index for dashboard queries: sumTotalByStatusAndCreatedAtAfter +-- This optimizes queries that filter by status and created_at (e.g., payout stats) +CREATE INDEX idx_payouts_status_created_at ON payouts(status, created_at); + +-- Index for payout type filtering +CREATE INDEX idx_payouts_type ON payouts(type); + +-- ============================================ +-- game_rounds indexes +-- ============================================ +-- Composite index for queries filtering by phase and resolved_at +-- This helps with queries like countByResolvedAtAfter when combined with phase filters +CREATE INDEX idx_game_rounds_phase_resolved_at ON game_rounds(phase, resolved_at); + +-- ============================================ +-- support_tickets indexes +-- ============================================ +-- Index for updated_at filtering (dashboard: tickets resolved today) +CREATE INDEX idx_support_tickets_updated_at ON support_tickets(updated_at); + +-- Composite index for common query pattern: status + updated_at +-- This helps with queries like countByStatusAndUpdatedAtAfter +CREATE INDEX idx_support_tickets_status_updated_at ON support_tickets(status, updated_at); + diff --git a/src/main/resources/db/migration/V25__add_user_id_to_admins.sql b/src/main/resources/db/migration/V25__add_user_id_to_admins.sql new file mode 100644 index 0000000..740bd32 --- /dev/null +++ b/src/main/resources/db/migration/V25__add_user_id_to_admins.sql @@ -0,0 +1,7 @@ +-- Add user_id column to admins table to link admin accounts to db_users_a +-- This allows admin messages in support tickets to use the admin's user_id +ALTER TABLE `admins` +ADD COLUMN `user_id` INT NULL AFTER `id`, +ADD CONSTRAINT `fk_admins_user_id` FOREIGN KEY (`user_id`) REFERENCES `db_users_a`(`id`) ON DELETE RESTRICT, +ADD INDEX `idx_user_id` (`user_id`); + diff --git a/src/main/resources/db/migration/V26__update_follow_tasks.sql b/src/main/resources/db/migration/V26__update_follow_tasks.sql new file mode 100644 index 0000000..8baad07 --- /dev/null +++ b/src/main/resources/db/migration/V26__update_follow_tasks.sql @@ -0,0 +1,17 @@ +-- Update existing follow task to use requirement=1 for News channel +-- Update title to "Follow our News channel" +UPDATE `tasks` +SET `title` = 'Follow our News channel', + `description` = 'Follow our News channel', + `requirement` = 1 +WHERE `type` = 'follow' AND `requirement` = 1; + +-- Add new follow task for Withdrawals channel (requirement=2) +-- reward_amount is in bigint format (5 Stars = 5000000) +-- Only insert if it doesn't already exist (check by type and requirement) +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) +SELECT 'follow', 2, 5000000, 'Stars', 2, 'Follow Proof of payment channel', 'Follow Proof of payment channel' +WHERE NOT EXISTS ( + SELECT 1 FROM `tasks` WHERE `type` = 'follow' AND `requirement` = 2 +); + diff --git a/src/main/resources/db/migration/V27__update_invite_30_friends_reward.sql b/src/main/resources/db/migration/V27__update_invite_30_friends_reward.sql new file mode 100644 index 0000000..2489bb7 --- /dev/null +++ b/src/main/resources/db/migration/V27__update_invite_30_friends_reward.sql @@ -0,0 +1,8 @@ +-- Update reward amount for "Invite 30 friends" task from 40 to 45 tickets +-- This changes the reward_amount from 40000000 (40 tickets) to 45000000 (45 tickets) +UPDATE `tasks` +SET `reward_amount` = 45000000 +WHERE `type` = 'referral' + AND `requirement` = 30 + AND `reward_amount` = 40000000; + diff --git a/src/main/resources/db/migration/V28__add_top_up_balance_tasks.sql b/src/main/resources/db/migration/V28__add_top_up_balance_tasks.sql new file mode 100644 index 0000000..dc43c21 --- /dev/null +++ b/src/main/resources/db/migration/V28__add_top_up_balance_tasks.sql @@ -0,0 +1,41 @@ +-- Convert existing 500 tickets task to 50 tickets task and add 3 new "Other" type tasks +-- These tasks are displayed in ascending order by requirement (display_order) +-- requirement is in bigint format (tickets * 1,000,000) +-- reward_amount is in bigint format (tickets * 1,000,000) + +-- Alter requirement column to BIGINT to support large values for "other" type tasks +-- The column was originally INT, but "other" tasks need BIGINT for deposit_total values +ALTER TABLE `tasks` MODIFY COLUMN `requirement` BIGINT NOT NULL COMMENT 'Number required (e.g., number of friends to invite, or deposit_total in bigint for other tasks)'; + +-- Convert existing 500 tickets task to 50 tickets task +-- Update requirement from 500000000 (500 tickets) to 50000000 (50 tickets) +-- Update reward from 100000000 (100 tickets) to 5000000 (5 tickets) +-- Update description and display_order +UPDATE `tasks` +SET `requirement` = 50000000, + `reward_amount` = 5000000, + `description` = 'Top up Balance: 50 Tickets', + `display_order` = 1 +WHERE `type` = 'other' AND `requirement` = 500000000; + +-- Task 2: 250 tickets requirement, 25 tickets reward +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) +SELECT 'other', 250000000, 25000000, 'Stars', 2, 'Top up Balance', 'Top up Balance: 250 Tickets' +WHERE NOT EXISTS ( + SELECT 1 FROM `tasks` WHERE `type` = 'other' AND `requirement` = 250000000 +); + +-- Task 3: 1000 tickets requirement, 100 tickets reward +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) +SELECT 'other', 1000000000, 100000000, 'Stars', 3, 'Top up Balance', 'Top up Balance: 1000 Tickets' +WHERE NOT EXISTS ( + SELECT 1 FROM `tasks` WHERE `type` = 'other' AND `requirement` = 1000000000 +); + +-- Task 4: 5000 tickets requirement, 500 tickets reward +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) +SELECT 'other', 5000000000, 500000000, 'Stars', 4, 'Top up Balance', 'Top up Balance: 5000 Tickets' +WHERE NOT EXISTS ( + SELECT 1 FROM `tasks` WHERE `type` = 'other' AND `requirement` = 5000000000 +); + diff --git a/src/main/resources/db/migration/V29__set_initial_balance_a.sql b/src/main/resources/db/migration/V29__set_initial_balance_a.sql new file mode 100644 index 0000000..4deefc4 --- /dev/null +++ b/src/main/resources/db/migration/V29__set_initial_balance_a.sql @@ -0,0 +1,5 @@ +-- Set initial balance_a default value to 3,000,000 (3.00 tickets) +-- This represents 3 tickets in bigint format (1 ticket = 1,000,000) +ALTER TABLE `db_users_b` +MODIFY COLUMN `balance_a` BIGINT UNSIGNED NOT NULL DEFAULT 3000000; + diff --git a/src/main/resources/db/migration/V2__create_game_tables.sql b/src/main/resources/db/migration/V2__create_game_tables.sql new file mode 100644 index 0000000..336337b --- /dev/null +++ b/src/main/resources/db/migration/V2__create_game_tables.sql @@ -0,0 +1,55 @@ +-- Create game_rooms table +CREATE TABLE IF NOT EXISTS game_rooms ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + room_number INT NOT NULL UNIQUE, + current_phase VARCHAR(20) NOT NULL DEFAULT 'WAITING', + countdown_end_at TIMESTAMP NULL, + total_tickets BIGINT UNSIGNED NOT NULL DEFAULT 0, + registered_players INT NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_phase (current_phase), + INDEX idx_countdown (countdown_end_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create game_rounds table (completed rounds history) +CREATE TABLE IF NOT EXISTS game_rounds ( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + room_id INT NOT NULL, + phase VARCHAR(20) NOT NULL, + total_tickets BIGINT UNSIGNED NOT NULL, + winner_user_id INT NULL, + winner_tickets BIGINT UNSIGNED NOT NULL DEFAULT 0, + commission BIGINT UNSIGNED NOT NULL DEFAULT 0, + payout BIGINT UNSIGNED NOT NULL DEFAULT 0, + started_at TIMESTAMP NOT NULL, + countdown_started_at TIMESTAMP NULL, + countdown_ended_at TIMESTAMP NULL, + resolved_at TIMESTAMP NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_room (room_id), + INDEX idx_winner (winner_user_id), + INDEX idx_resolved (resolved_at), + FOREIGN KEY (room_id) REFERENCES game_rooms(id) ON DELETE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create game_round_participants table (who joined which round) +CREATE TABLE IF NOT EXISTS game_round_participants ( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + round_id BIGINT NOT NULL, + user_id INT NOT NULL, + tickets BIGINT UNSIGNED NOT NULL, + joined_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_round (round_id), + INDEX idx_user (user_id), + INDEX idx_round_user (round_id, user_id), + FOREIGN KEY (round_id) REFERENCES game_rounds(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Insert default rooms (Room 1, 2, 3) +INSERT INTO game_rooms (room_number, current_phase, total_tickets, registered_players) +VALUES (1, 'WAITING', 0, 0), + (2, 'WAITING', 0, 0), + (3, 'WAITING', 0, 0); + diff --git a/src/main/resources/db/migration/V2__create_sessions_table.sql b/src/main/resources/db/migration/V2__create_sessions_table.sql deleted file mode 100644 index f8e20a9..0000000 --- a/src/main/resources/db/migration/V2__create_sessions_table.sql +++ /dev/null @@ -1,14 +0,0 @@ --- Create sessions table for Bearer token authentication -CREATE TABLE IF NOT EXISTS sessions ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - session_id_hash VARCHAR(255) NOT NULL UNIQUE, - user_id BIGINT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - expires_at TIMESTAMP NOT NULL, - INDEX idx_session_hash (session_id_hash), - INDEX idx_expires_at (expires_at), - INDEX idx_user_id (user_id), - INDEX idx_user_created (user_id, created_at), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - diff --git a/src/main/resources/db/migration/V30__create_quick_answers_table.sql b/src/main/resources/db/migration/V30__create_quick_answers_table.sql new file mode 100644 index 0000000..964a8f8 --- /dev/null +++ b/src/main/resources/db/migration/V30__create_quick_answers_table.sql @@ -0,0 +1,13 @@ +-- Create quick_answers table for admin quick response templates +CREATE TABLE `quick_answers` ( + `id` INT NOT NULL AUTO_INCREMENT, + `admin_id` INT NOT NULL, + `text` TEXT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_admin_id` (`admin_id`), + KEY `idx_admin_created` (`admin_id`, `created_at`), + CONSTRAINT `fk_quick_answers_admin` FOREIGN KEY (`admin_id`) REFERENCES `admins` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + diff --git a/src/main/resources/db/migration/V31__add_usd_amount_to_payments_and_payouts.sql b/src/main/resources/db/migration/V31__add_usd_amount_to_payments_and_payouts.sql new file mode 100644 index 0000000..cce4752 --- /dev/null +++ b/src/main/resources/db/migration/V31__add_usd_amount_to_payments_and_payouts.sql @@ -0,0 +1,7 @@ +-- Add usd_amount to payments (crypto): BIGINT, 1_000_000 in DB = 1 USD. Keep stars_amount for backward compatibility. +ALTER TABLE payments +ADD COLUMN usd_amount BIGINT UNSIGNED NULL COMMENT 'USD amount: 1_000_000 = 1 USD (crypto deposits)' AFTER stars_amount; + +-- Add usd_amount to payouts. Same scale: 1_000_000 = 1 USD. +ALTER TABLE payouts +ADD COLUMN usd_amount BIGINT UNSIGNED NULL COMMENT 'USD amount: 1_000_000 = 1 USD (crypto withdrawals)' AFTER stars_amount; diff --git a/src/main/resources/db/migration/V32__update_other_tasks_requirements_and_rewards.sql b/src/main/resources/db/migration/V32__update_other_tasks_requirements_and_rewards.sql new file mode 100644 index 0000000..80f7191 --- /dev/null +++ b/src/main/resources/db/migration/V32__update_other_tasks_requirements_and_rewards.sql @@ -0,0 +1,26 @@ +-- Update "other" type tasks: remove 50M task, change requirements and rewards for the rest +-- requirement and reward_amount are in bigint (tickets * 1_000_000) + +-- Remove the other task with requirement 50M (50 tickets) +DELETE FROM `tasks` WHERE `type` = 'other' AND `requirement` = 50000000; + +-- 250M -> 2000 tickets requirement, 200 tickets reward +UPDATE `tasks` +SET `requirement` = 2000000000, + `reward_amount` = 200000000, + `description` = 'Top up Balance: 2000 Tickets' +WHERE `type` = 'other' AND `requirement` = 250000000; + +-- 1000M -> 5000 tickets requirement, 500 tickets reward +UPDATE `tasks` +SET `requirement` = 5000000000, + `reward_amount` = 500000000, + `description` = 'Top up Balance: 5000 Tickets' +WHERE `type` = 'other' AND `requirement` = 1000000000; + +-- 5000M -> 10000 tickets requirement, 1000 tickets reward +UPDATE `tasks` +SET `requirement` = 10000000000, + `reward_amount` = 1000000000, + `description` = 'Top up Balance: 10000 Tickets' +WHERE `type` = 'other' AND `requirement` = 5000000000; diff --git a/src/main/resources/db/migration/V33__fix_other_task_duplicate_10000.sql b/src/main/resources/db/migration/V33__fix_other_task_duplicate_10000.sql new file mode 100644 index 0000000..2e16ee6 --- /dev/null +++ b/src/main/resources/db/migration/V33__fix_other_task_duplicate_10000.sql @@ -0,0 +1,9 @@ +-- Fix duplicate 10000-ticket "other" task: the row with display_order=3 that incorrectly +-- has requirement 10000 should be the 5000-ticket task (requirement/reward in bigint). +UPDATE `tasks` +SET `requirement` = 5000000000, + `reward_amount` = 500000000, + `description` = 'Top up Balance: 5000 Tickets' +WHERE `type` = 'other' + AND `display_order` = 3 + AND `requirement` = 10000000000; diff --git a/src/main/resources/db/migration/V34__add_referral_commission_indexes.sql b/src/main/resources/db/migration/V34__add_referral_commission_indexes.sql new file mode 100644 index 0000000..9d537e8 --- /dev/null +++ b/src/main/resources/db/migration/V34__add_referral_commission_indexes.sql @@ -0,0 +1,5 @@ +-- Indexes for referral list ordered by commission DESC (level 1, 2, 3). +-- Queries filter by referer_id_N and order by to_referer_N DESC. +CREATE INDEX idx_users_d_referer1_commission ON db_users_d (referer_id_1, to_referer_1 DESC); +CREATE INDEX idx_users_d_referer2_commission ON db_users_d (referer_id_2, to_referer_2 DESC); +CREATE INDEX idx_users_d_referer3_commission ON db_users_d (referer_id_3, to_referer_3 DESC); diff --git a/src/main/resources/db/migration/V35__add_total_win_after_deposit_to_users_b.sql b/src/main/resources/db/migration/V35__add_total_win_after_deposit_to_users_b.sql new file mode 100644 index 0000000..95111af --- /dev/null +++ b/src/main/resources/db/migration/V35__add_total_win_after_deposit_to_users_b.sql @@ -0,0 +1,4 @@ +-- Add total_win_after_deposit to db_users_b (bigint: 1 ticket = 1_000_000). +-- Reset to 0 on each deposit; incremented by round win amount when user wins; reduced when user creates a payout. +ALTER TABLE `db_users_b` + ADD COLUMN `total_win_after_deposit` BIGINT NOT NULL DEFAULT 0 AFTER `rounds_played`; diff --git a/src/main/resources/db/migration/V36__create_feature_switches.sql b/src/main/resources/db/migration/V36__create_feature_switches.sql new file mode 100644 index 0000000..9e01e7b --- /dev/null +++ b/src/main/resources/db/migration/V36__create_feature_switches.sql @@ -0,0 +1,10 @@ +-- Runtime feature toggles (e.g. remote bet endpoint). Can be changed from admin panel without restart. +CREATE TABLE `feature_switches` ( + `key` VARCHAR(64) NOT NULL, + `enabled` TINYINT(1) NOT NULL DEFAULT 0, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Insert default: remote bet endpoint disabled until explicitly enabled from admin +INSERT INTO `feature_switches` (`key`, `enabled`) VALUES ('remote_bet_enabled', 1); diff --git a/src/main/resources/db/migration/V37__create_crypto_deposit_tables.sql b/src/main/resources/db/migration/V37__create_crypto_deposit_tables.sql new file mode 100644 index 0000000..718544d --- /dev/null +++ b/src/main/resources/db/migration/V37__create_crypto_deposit_tables.sql @@ -0,0 +1,24 @@ +-- Crypto deposit methods from external API (spin-passim.tech). Synced on Store open. +-- Single row: hash for future cache; minimum_deposit = min of all methods' min_deposit_sum. +CREATE TABLE `crypto_deposit_config` ( + `id` INT NOT NULL DEFAULT 1, + `methods_hash` VARCHAR(255) NULL COMMENT 'Future: skip sync when unchanged', + `minimum_deposit` DECIMAL(10,2) NOT NULL DEFAULT 2.50, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT INTO `crypto_deposit_config` (`id`, `minimum_deposit`) VALUES (1, 2.50); + +-- One row per active deposit method (PID = icon filename, e.g. 235.png). +CREATE TABLE `crypto_deposit_methods` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `pid` INT NOT NULL COMMENT 'External API PID; equals icon filename without extension', + `name` VARCHAR(100) NOT NULL, + `network` VARCHAR(50) NOT NULL, + `example` VARCHAR(255) NULL, + `min_deposit_sum` DECIMAL(10,2) NOT NULL, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pid` (`pid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/main/resources/db/migration/V38__seed_crypto_deposit_methods.sql b/src/main/resources/db/migration/V38__seed_crypto_deposit_methods.sql new file mode 100644 index 0000000..dced465 --- /dev/null +++ b/src/main/resources/db/migration/V38__seed_crypto_deposit_methods.sql @@ -0,0 +1,40 @@ +-- Seed default crypto deposit methods and config hash (from external API snapshot). +-- minimum_deposit = 2.50 (min of all min_deposit_sum). Hash for future cron compare. +UPDATE `crypto_deposit_config` SET `methods_hash` = 'a86b5f20528bc9e385d4d12fb50f5e3d', `minimum_deposit` = 2.50 WHERE `id` = 1; + +INSERT INTO `crypto_deposit_methods` (`pid`, `name`, `network`, `example`, `min_deposit_sum`) VALUES +(235, 'TON', 'TON', 'UQAm3JwwV_wMgmJ05AzHqtlHAdkyJt58N-JHV2Uhf80hOEKD', 2.5), +(90, 'TRON', 'TRC20', 'TMQqX43PAMZPXPxX6Qj1fCyeiMWLgW35yF', 2.5), +(10, 'Bitcoin', 'BTC', '131qX9kauDpCGyn2MfAFwHcrrVk7JTLAYj', 2.5), +(100, 'USDC', 'ERC20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 40), +(20, 'Ethereum', 'ERC20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 40), +(71, 'USDT', 'TRC20', 'TMQqX43PAMZPXPxX6Qj1fCyeiMWLgW35yF', 2.5), +(30, 'LiteCoin', 'LTC', 'LRcJGhbbSkpSXAq3qMWibRmaxQzNAuQ1ZM', 2.5), +(130, 'BNB', 'BEP20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(244, 'NOT', 'TON', 'UQAm3JwwV_wMgmJ05AzHqtlHAdkyJt58N-JHV2Uhf80hOEKD', 2.5), +(245, 'DOGS', 'TON', 'UQAm3JwwV_wMgmJ05AzHqtlHAdkyJt58N-JHV2Uhf80hOEKD', 2.5), +(206, 'Shiba Inu', 'BEP20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(40, 'DOGE', 'DOGE', 'DSNgQwNKp4RzNwAbVhxokw1sadyq5kbD1n', 2.5), +(60, 'Solana', 'SOL', '86drvKWPo6mN2RauveLSu4TLkBUxMTxKsjpTNbjuHzA3', 2.5), +(120, 'XRP', 'XRP', 'rQN6FpoqGsSe1dHG665SnKmKesNQhnd4UB:12345', 2.5), +(73, 'USDT', 'SOL', '86drvKWPo6mN2RauveLSu4TLkBUxMTxKsjpTNbjuHzA3', 2.5), +(50, 'BCH', 'BCH', 'qq0pg56eg90m7rv6en7l0vv4gpudh8wf3swa0hqsu2', 2.5), +(70, 'USDT', 'ERC20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 40), +(72, 'USDT', 'BEP20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(75, 'USDT', 'TON', 'UQAm3JwwV_wMgmJ05AzHqtlHAdkyJt58N-JHV2Uhf80hOEKD', 2.5), +(76, 'USDT', 'ARBITRUM', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(201, 'Bitcoin', 'BEP20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(74, 'USDT', 'MATIC', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(77, 'USDT', 'OPTIMISM', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(78, 'USDT', 'Avalanche', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(102, 'USDC', 'MATIC', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(202, 'Ethereum', 'BEP20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(207, 'Shiba Inu', 'ERC20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 40), +(101, 'USDC', 'BEP20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(64, 'AVAX', 'Avalanche', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(61, 'MATIC', 'MATIC', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(242, 'BONK', 'SOL', '86drvKWPo6mN2RauveLSu4TLkBUxMTxKsjpTNbjuHzA3', 2.5), +(240, 'FLOKI', 'BEP20', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(62, 'ARB', 'ARBITRUM', '0x153aDC5BdF47D1f68F1ffdcEeaD7a458ca9CEd13', 2.5), +(208, 'DASH', 'DASH', 'Xg9j6CmK9DY822o8YsTtu183KEHYxiVdbr', 2.5) +ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `network` = VALUES(`network`), `example` = VALUES(`example`), `min_deposit_sum` = VALUES(`min_deposit_sum`), `updated_at` = CURRENT_TIMESTAMP; diff --git a/src/main/resources/db/migration/V3__rename_tickets_to_bet.sql b/src/main/resources/db/migration/V3__rename_tickets_to_bet.sql new file mode 100644 index 0000000..89c9e9d --- /dev/null +++ b/src/main/resources/db/migration/V3__rename_tickets_to_bet.sql @@ -0,0 +1,10 @@ +-- Rename tickets columns to bet for currency-agnostic naming +ALTER TABLE game_rooms CHANGE COLUMN total_tickets total_bet BIGINT UNSIGNED NOT NULL DEFAULT 0; +ALTER TABLE game_rounds CHANGE COLUMN total_tickets total_bet BIGINT UNSIGNED NOT NULL; +ALTER TABLE game_rounds CHANGE COLUMN winner_tickets winner_bet BIGINT UNSIGNED NOT NULL DEFAULT 0; +ALTER TABLE game_round_participants CHANGE COLUMN tickets bet BIGINT UNSIGNED NOT NULL; + + + + + diff --git a/src/main/resources/db/migration/V40__usd_amount_to_decimal.sql b/src/main/resources/db/migration/V40__usd_amount_to_decimal.sql new file mode 100644 index 0000000..8351349 --- /dev/null +++ b/src/main/resources/db/migration/V40__usd_amount_to_decimal.sql @@ -0,0 +1,13 @@ +-- Store USD as decimal (e.g. 1.25 USD = 1.25). Convert existing bigint (1_000_000 = 1 USD) to decimal. + +-- payments +ALTER TABLE payments ADD COLUMN usd_amount_new DECIMAL(20,2) NULL COMMENT 'USD amount as decimal' AFTER stars_amount; +UPDATE payments SET usd_amount_new = usd_amount / 1000000.0 WHERE usd_amount IS NOT NULL; +ALTER TABLE payments DROP COLUMN usd_amount; +ALTER TABLE payments CHANGE usd_amount_new usd_amount DECIMAL(20,2) NULL COMMENT 'USD amount (e.g. 1.25)'; + +-- payouts +ALTER TABLE payouts ADD COLUMN usd_amount_new DECIMAL(20,2) NULL COMMENT 'USD amount as decimal' AFTER stars_amount; +UPDATE payouts SET usd_amount_new = usd_amount / 1000000.0 WHERE usd_amount IS NOT NULL; +ALTER TABLE payouts DROP COLUMN usd_amount; +ALTER TABLE payouts CHANGE usd_amount_new usd_amount DECIMAL(20,2) NULL COMMENT 'USD amount (e.g. 1.25)'; diff --git a/src/main/resources/db/migration/V41__create_crypto_withdrawal_tables.sql b/src/main/resources/db/migration/V41__create_crypto_withdrawal_tables.sql new file mode 100644 index 0000000..bfa3ca3 --- /dev/null +++ b/src/main/resources/db/migration/V41__create_crypto_withdrawal_tables.sql @@ -0,0 +1,22 @@ +-- Crypto withdrawal methods from external API (GET /api/v1/withdrawal-methods). Synced every 30 min. +-- Methods table overwritten on each sync (no hash check). min_withdrawal is per method. +-- One row per active withdrawal method (pid = icon_id from API, used for icon filename e.g. 30.png). +CREATE TABLE `crypto_withdrawal_methods` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `pid` INT NOT NULL COMMENT 'Icon ID from API; equals icon filename without extension', + `name` VARCHAR(50) NOT NULL COMMENT 'Display name (ticker from API, e.g. TON, LTC)', + `network` VARCHAR(100) NOT NULL COMMENT 'Network name from API (e.g. Toncoin, Litecoin)', + `way_id` INT NOT NULL COMMENT 'External API way_id', + `min_withdrawal` DECIMAL(10,2) NOT NULL DEFAULT 0.10, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pid` (`pid`), + KEY `ix_way_id` (`way_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Seed initial methods (LTC, BNB, TRX, TON) +INSERT INTO `crypto_withdrawal_methods` (`pid`, `name`, `network`, `way_id`, `min_withdrawal`) VALUES +(30, 'LTC', 'Litecoin', 1, 0.10), +(130, 'BNB', 'BNB', 2, 0.10), +(90, 'TRX', 'TRON', 3, 0.10), +(235, 'TON', 'Toncoin', 4, 0.10); diff --git a/src/main/resources/db/migration/V42__withdrawal_methods_pid_and_icon_id.sql b/src/main/resources/db/migration/V42__withdrawal_methods_pid_and_icon_id.sql new file mode 100644 index 0000000..517cf7a --- /dev/null +++ b/src/main/resources/db/migration/V42__withdrawal_methods_pid_and_icon_id.sql @@ -0,0 +1,16 @@ +-- Withdrawal methods API now returns pid (method id) and icon_id (for icons). +-- Map: api pid -> pid, api icon_id -> icon_id. Remove way_id. +ALTER TABLE `crypto_withdrawal_methods` + ADD COLUMN `icon_id` VARCHAR(20) NOT NULL DEFAULT '' COMMENT 'Icon ID from API for icon filename' AFTER `network`, + DROP INDEX `ix_way_id`, + DROP COLUMN `way_id`; + +-- Replace V41 seed rows with new structure (pid = method id, icon_id = icon filename) +DELETE FROM `crypto_withdrawal_methods`; + +-- Seed initial methods (pid from API, icon_id for icon filename) +INSERT INTO `crypto_withdrawal_methods` (`pid`, `name`, `network`, `icon_id`, `min_withdrawal`) VALUES +(1, 'LTC', 'Litecoin', '30', 0.10), +(2, 'BNB', 'BNB', '130', 0.10), +(3, 'TRX', 'TRON', '90', 0.10), +(4, 'TON', 'Toncoin', '235', 0.10); diff --git a/src/main/resources/db/migration/V43__payouts_crypto_columns.sql b/src/main/resources/db/migration/V43__payouts_crypto_columns.sql new file mode 100644 index 0000000..a8aa97b --- /dev/null +++ b/src/main/resources/db/migration/V43__payouts_crypto_columns.sql @@ -0,0 +1,8 @@ +-- Crypto withdrawal payouts: store ticker and amounts from crypto API. +-- type 'CRYPTO' is used for such payouts (no enum change in DB, VARCHAR already). +ALTER TABLE payouts + ADD COLUMN crypto_name VARCHAR(20) NULL COMMENT 'Ticker from crypto API (e.g. TRX, TON)' AFTER gift_name, + ADD COLUMN amount_coins VARCHAR(50) NULL COMMENT 'Withdrawal amount in coins from API' AFTER usd_amount, + ADD COLUMN commission_coins VARCHAR(50) NULL COMMENT 'Commission in coins from API' AFTER amount_coins, + ADD COLUMN amount_to_send VARCHAR(50) NULL COMMENT 'Final amount to send from API' AFTER commission_coins, + ADD COLUMN wallet VARCHAR(120) NULL COMMENT 'Wallet address for CRYPTO type' AFTER username; diff --git a/src/main/resources/db/migration/V44__payouts_payment_id.sql b/src/main/resources/db/migration/V44__payouts_payment_id.sql new file mode 100644 index 0000000..dda5806 --- /dev/null +++ b/src/main/resources/db/migration/V44__payouts_payment_id.sql @@ -0,0 +1,12 @@ +-- Store crypto API payment_id from POST api/v1/withdrawal response (result.payment.payment_id). +ALTER TABLE payouts + ADD COLUMN payment_id INT NULL COMMENT 'Crypto API payment id from withdrawal response' AFTER amount_to_send; + +-- Extend payout status with WAITING. +ALTER TABLE payouts + MODIFY COLUMN status VARCHAR(20) NOT NULL DEFAULT 'PROCESSING' COMMENT 'PROCESSING, COMPLETED, CANCELLED, WAITING'; + +-- Add updated_at; initially equal to created_at. +ALTER TABLE payouts + ADD COLUMN updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at; +UPDATE payouts SET updated_at = created_at; diff --git a/src/main/resources/db/migration/V45__payouts_crypto_indexes.sql b/src/main/resources/db/migration/V45__payouts_crypto_indexes.sql new file mode 100644 index 0000000..47a8376 --- /dev/null +++ b/src/main/resources/db/migration/V45__payouts_crypto_indexes.sql @@ -0,0 +1,5 @@ +-- Indexes for crypto payout logic: +-- 1) existsByUserIdAndStatus(userId, status) - at most one PROCESSING per user check +-- 2) findByStatusInAndPaymentIdIsNotNullOrderByUpdatedAtAsc - cron sync batch (status + updated_at) +CREATE INDEX idx_payouts_user_status ON payouts(user_id, status); +CREATE INDEX idx_payouts_status_updated_at ON payouts(status, updated_at); diff --git a/src/main/resources/db/migration/V46__payouts_type_status_updated_at_index.sql b/src/main/resources/db/migration/V46__payouts_type_status_updated_at_index.sql new file mode 100644 index 0000000..a9a0bd6 --- /dev/null +++ b/src/main/resources/db/migration/V46__payouts_type_status_updated_at_index.sql @@ -0,0 +1,3 @@ +-- Index for withdrawal sync cron: findByTypeAndStatusInAndPaymentIdIsNotNullOrderByUpdatedAtAsc(CRYPTO, PROCESSING|WAITING). +-- Covers type + status filter and ORDER BY updated_at ASC. +CREATE INDEX idx_payouts_type_status_updated_at ON payouts(type, status, updated_at); diff --git a/src/main/resources/db/migration/V47__payouts_drop_redundant_index.sql b/src/main/resources/db/migration/V47__payouts_drop_redundant_index.sql new file mode 100644 index 0000000..79c3a62 --- /dev/null +++ b/src/main/resources/db/migration/V47__payouts_drop_redundant_index.sql @@ -0,0 +1,3 @@ +-- Drop redundant index: cron now uses findByTypeAndStatusIn... with idx_payouts_type_status_updated_at (type, status, updated_at). +-- No query uses (status, updated_at) without type anymore. +DROP INDEX idx_payouts_status_updated_at ON payouts; diff --git a/src/main/resources/db/migration/V48__feature_switches_payment_payout.sql b/src/main/resources/db/migration/V48__feature_switches_payment_payout.sql new file mode 100644 index 0000000..643978e --- /dev/null +++ b/src/main/resources/db/migration/V48__feature_switches_payment_payout.sql @@ -0,0 +1,4 @@ +-- Feature switchers for payment (deposits) and payout (withdrawals). Enabled by default. +INSERT INTO `feature_switches` (`key`, `enabled`) VALUES + ('payment_enabled', 1), + ('payout_enabled', 1); diff --git a/src/main/resources/db/migration/V49__update_tasks_rewards_and_add_deposit_tasks.sql b/src/main/resources/db/migration/V49__update_tasks_rewards_and_add_deposit_tasks.sql new file mode 100644 index 0000000..638f469 --- /dev/null +++ b/src/main/resources/db/migration/V49__update_tasks_rewards_and_add_deposit_tasks.sql @@ -0,0 +1,43 @@ +-- Update task rewards and add new deposit (other) tasks. +-- requirement and reward_amount are in bigint (tickets * 1_000_000). + +-- ========== FOLLOW: change 5 tickets reward to 7 tickets ========== +UPDATE `tasks` SET `reward_amount` = 7000000 WHERE `type` = 'follow' AND `reward_amount` = 5000000; + +-- ========== OTHER (deposit): update existing rewards ========== +-- 2000 tickets: reward 200 -> 100 +UPDATE `tasks` SET `reward_amount` = 100000000, `description` = 'Top up Balance: 2000 Tickets' +WHERE `type` = 'other' AND `requirement` = 2000000000; + +-- 5000 tickets: reward 500 -> 250 +UPDATE `tasks` SET `reward_amount` = 250000000, `description` = 'Top up Balance: 5000 Tickets' +WHERE `type` = 'other' AND `requirement` = 5000000000; + +-- 10000 tickets: reward 1000 -> 500 +UPDATE `tasks` SET `reward_amount` = 500000000, `description` = 'Top up Balance: 10000 Tickets' +WHERE `type` = 'other' AND `requirement` = 10000000000; + +-- ========== OTHER: add new deposit tasks (50000, 150000, 500000 tickets) ========== +-- display_order: existing are 1..4 (50, 250, 2000, 5000, 10000 from V28/V32). Get max display_order for other type then add 5,6,7. +-- 50000 tickets -> 2500 reward; 150000 -> 7500; 500000 -> 25000 +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) +SELECT 'other', 50000000000, 2500000000, 'Tickets', 5, 'Top up Balance', 'Top up Balance: 50000 Tickets' +WHERE NOT EXISTS (SELECT 1 FROM `tasks` WHERE `type` = 'other' AND `requirement` = 50000000000); + +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) +SELECT 'other', 150000000000, 7500000000, 'Tickets', 6, 'Top up Balance', 'Top up Balance: 150000 Tickets' +WHERE NOT EXISTS (SELECT 1 FROM `tasks` WHERE `type` = 'other' AND `requirement` = 150000000000); + +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) +SELECT 'other', 500000000000, 25000000000, 'Tickets', 7, 'Top up Balance', 'Top up Balance: 500000 Tickets' +WHERE NOT EXISTS (SELECT 1 FROM `tasks` WHERE `type` = 'other' AND `requirement` = 500000000000); + +-- ========== REFERRAL: update rewards (requirement -> new reward in tickets) ========== +-- 1 -> 5; 3 -> 15; 7 -> 35; 15 -> 75; 30 -> 110; 50 -> 150; 100 -> 375 +UPDATE `tasks` SET `reward_amount` = 5000000 WHERE `type` = 'referral' AND `requirement` = 1; +UPDATE `tasks` SET `reward_amount` = 15000000 WHERE `type` = 'referral' AND `requirement` = 3; +UPDATE `tasks` SET `reward_amount` = 35000000 WHERE `type` = 'referral' AND `requirement` = 7; +UPDATE `tasks` SET `reward_amount` = 75000000 WHERE `type` = 'referral' AND `requirement` = 15; +UPDATE `tasks` SET `reward_amount` = 110000000 WHERE `type` = 'referral' AND `requirement` = 30; +UPDATE `tasks` SET `reward_amount` = 150000000 WHERE `type` = 'referral' AND `requirement` = 50; +UPDATE `tasks` SET `reward_amount` = 375000000 WHERE `type` = 'referral' AND `requirement` = 100; diff --git a/src/main/resources/db/migration/V4__add_composite_index_for_completed_rounds.sql b/src/main/resources/db/migration/V4__add_composite_index_for_completed_rounds.sql new file mode 100644 index 0000000..da69639 --- /dev/null +++ b/src/main/resources/db/migration/V4__add_composite_index_for_completed_rounds.sql @@ -0,0 +1,10 @@ +-- Add composite index for optimized querying of completed rounds with winners +-- This index supports the query: WHERE room_id = X AND phase = 'RESOLUTION' AND resolved_at IS NOT NULL AND winner_user_id IS NOT NULL ORDER BY resolved_at DESC +-- Index order: +-- 1. room_id (exact match, most selective WHERE filter) +-- 2. phase (exact match WHERE filter) +-- 3. resolved_at (ORDER BY column - placed after equality filters for efficient sorting) +-- 4. winner_user_id (IS NOT NULL filter - MySQL can still use index efficiently) +-- This allows MySQL to efficiently filter by room_id and phase, then sort by resolved_at using the index +CREATE INDEX idx_room_phase_resolved_winner ON game_rounds (room_id, phase, resolved_at, winner_user_id); + diff --git a/src/main/resources/db/migration/V50__set_initial_balance_a_5_tickets.sql b/src/main/resources/db/migration/V50__set_initial_balance_a_5_tickets.sql new file mode 100644 index 0000000..8094123 --- /dev/null +++ b/src/main/resources/db/migration/V50__set_initial_balance_a_5_tickets.sql @@ -0,0 +1,4 @@ +-- Set initial balance_a default value to 5,000,000 (5.00 tickets) +-- New users get 5 tickets on registration (1 ticket = 1,000,000 in bigint) +ALTER TABLE `db_users_b` +MODIFY COLUMN `balance_a` BIGINT UNSIGNED NOT NULL DEFAULT 5000000; diff --git a/src/main/resources/db/migration/V51__feature_switches_referral_tasks_50_100.sql b/src/main/resources/db/migration/V51__feature_switches_referral_tasks_50_100.sql new file mode 100644 index 0000000..3ca5618 --- /dev/null +++ b/src/main/resources/db/migration/V51__feature_switches_referral_tasks_50_100.sql @@ -0,0 +1,5 @@ +-- Toggle "Invite 50 friends" and "Invite 100 friends" referral tasks. When disabled (0), tasks are hidden and cannot be claimed. +INSERT INTO `feature_switches` (`key`, `enabled`) VALUES + ('task_referral_50_enabled', 0), + ('task_referral_100_enabled', 0) +ON DUPLICATE KEY UPDATE `key` = VALUES(`key`); diff --git a/src/main/resources/db/migration/V52__bot_config_tables.sql b/src/main/resources/db/migration/V52__bot_config_tables.sql new file mode 100644 index 0000000..673d01b --- /dev/null +++ b/src/main/resources/db/migration/V52__bot_config_tables.sql @@ -0,0 +1,14 @@ +-- Safe bot users: when balance < threshold they get 100% win rate at resolution (display unchanged) +CREATE TABLE IF NOT EXISTS safe_bot_users ( + user_id INT NOT NULL PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Flexible bot config: user has fixed win rate (0-1) regardless of bet +CREATE TABLE IF NOT EXISTS flexible_bot_configs ( + user_id INT NOT NULL PRIMARY KEY, + win_rate DECIMAL(5,4) NOT NULL COMMENT '0.0000-1.0000', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT chk_win_rate CHECK (win_rate >= 0 AND win_rate <= 1) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/main/resources/db/migration/V53__admin_users_list_sort_indexes.sql b/src/main/resources/db/migration/V53__admin_users_list_sort_indexes.sql new file mode 100644 index 0000000..f2b4355 --- /dev/null +++ b/src/main/resources/db/migration/V53__admin_users_list_sort_indexes.sql @@ -0,0 +1,15 @@ +-- Indexes for admin users list sorting (Balance, Profit, Deposits, Withdraws, Rounds, Referrals) +-- db_users_b: balance_a, deposit_total, withdraw_total, rounds_played +CREATE INDEX idx_users_b_balance_a ON db_users_b(balance_a); +CREATE INDEX idx_users_b_deposit_total ON db_users_b(deposit_total); +CREATE INDEX idx_users_b_withdraw_total ON db_users_b(withdraw_total); +CREATE INDEX idx_users_b_rounds_played ON db_users_b(rounds_played); + +-- db_users_d: for referral count (sum of referals_1..5) we filter by referer_id_N; indexes already exist (V34) +-- For sorting by total referral count we could use a composite; referer_id_1 is used for "referrals of user X" +CREATE INDEX idx_users_d_referals1 ON db_users_d(referals_1); +CREATE INDEX idx_users_d_referer_id_1 ON db_users_d(referer_id_1); +CREATE INDEX idx_users_d_referer_id_2 ON db_users_d(referer_id_2); +CREATE INDEX idx_users_d_referer_id_3 ON db_users_d(referer_id_3); +CREATE INDEX idx_users_d_referer_id_4 ON db_users_d(referer_id_4); +CREATE INDEX idx_users_d_referer_id_5 ON db_users_d(referer_id_5); diff --git a/src/main/resources/db/migration/V54__lottery_bot_configs.sql b/src/main/resources/db/migration/V54__lottery_bot_configs.sql new file mode 100644 index 0000000..ed8e505 --- /dev/null +++ b/src/main/resources/db/migration/V54__lottery_bot_configs.sql @@ -0,0 +1,20 @@ +-- Bot behaviour config: links a real user (db_users_a/b) to play as a bot with time windows, rooms, bet range. +-- One config per user; user_id must exist in db_users_a. +CREATE TABLE IF NOT EXISTS lottery_bot_configs ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + room_1 TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Can play room 1', + room_2 TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Can play room 2', + room_3 TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Can play room 3', + time_utc_start TIME NOT NULL COMMENT 'Start of active window (UTC)', + time_utc_end TIME NOT NULL COMMENT 'End of active window (UTC)', + bet_min BIGINT NOT NULL COMMENT 'Min bet in bigint (1 ticket = 1000000)', + bet_max BIGINT NOT NULL COMMENT 'Max bet in bigint', + persona VARCHAR(20) NOT NULL DEFAULT 'balanced' COMMENT 'conservative, aggressive, balanced', + active TINYINT(1) NOT NULL DEFAULT 1, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY uk_lottery_bot_configs_user_id (user_id), + KEY idx_lottery_bot_configs_active_rooms (active, room_1, room_2, room_3), + CONSTRAINT fk_lottery_bot_configs_user FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/main/resources/db/migration/V55__feature_switch_lottery_bot_scheduler.sql b/src/main/resources/db/migration/V55__feature_switch_lottery_bot_scheduler.sql new file mode 100644 index 0000000..a940824 --- /dev/null +++ b/src/main/resources/db/migration/V55__feature_switch_lottery_bot_scheduler.sql @@ -0,0 +1,4 @@ +-- Toggle for lottery bot scheduler (auto-join bots from lottery_bot_configs into joinable rounds). When disabled (0), scheduler skips registration. +INSERT INTO `feature_switches` (`key`, `enabled`) VALUES + ('lottery_bot_scheduler_enabled', 1) +ON DUPLICATE KEY UPDATE `key` = VALUES(`key`); diff --git a/src/main/resources/db/migration/V56__create_promotions_tables.sql b/src/main/resources/db/migration/V56__create_promotions_tables.sql new file mode 100644 index 0000000..32d004f --- /dev/null +++ b/src/main/resources/db/migration/V56__create_promotions_tables.sql @@ -0,0 +1,39 @@ +-- Promotions: each has type, time range, status +CREATE TABLE IF NOT EXISTS promotions ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + type VARCHAR(32) NOT NULL COMMENT 'e.g. NET_WIN', + start_time TIMESTAMP NOT NULL, + end_time TIMESTAMP NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'PLANNED' COMMENT 'ACTIVE, INACTIVE, FINISHED, PLANNED', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + KEY idx_promotions_type_status (type, status), + KEY idx_promotions_times (start_time, end_time), + KEY idx_promotions_status (status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Prizes per place for a promotion +CREATE TABLE IF NOT EXISTS promotions_rewards ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + promo_id INT NOT NULL, + place INT NOT NULL COMMENT '1 = first place, 2 = second, etc.', + reward BIGINT NOT NULL COMMENT 'Tickets in bigint (1 ticket = 1000000)', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY uk_promotions_rewards_promo_place (promo_id, place), + KEY idx_promotions_rewards_promo (promo_id), + CONSTRAINT fk_promotions_rewards_promo FOREIGN KEY (promo_id) REFERENCES promotions(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- User progress per promotion (one row per user per promo) +CREATE TABLE IF NOT EXISTS promotions_users ( + promo_id INT NOT NULL, + user_id INT NOT NULL, + points DECIMAL(20,2) NOT NULL DEFAULT 0 COMMENT 'Points as ticket count, 2 decimal places (e.g. 100.25)', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (promo_id, user_id), + KEY idx_promotions_users_promo_points (promo_id, points DESC), + KEY idx_promotions_users_user (user_id), + CONSTRAINT fk_promotions_users_promo FOREIGN KEY (promo_id) REFERENCES promotions(id) ON DELETE CASCADE, + CONSTRAINT fk_promotions_users_user FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/main/resources/db/migration/V57__seed_first_net_win_promotion.sql b/src/main/resources/db/migration/V57__seed_first_net_win_promotion.sql new file mode 100644 index 0000000..ae0febe --- /dev/null +++ b/src/main/resources/db/migration/V57__seed_first_net_win_promotion.sql @@ -0,0 +1,20 @@ +-- First NET_WIN promotion: 26.02.2026 12:00 UTC -> 01.03.2026 12:00 UTC +INSERT INTO promotions (type, start_time, end_time, status) VALUES +('NET_WIN', '2026-02-26 12:00:00', '2026-03-01 12:00:00', 'PLANNED'); + +-- Rewards: 1 ticket = 1,000,000 in bigint +-- place 1: 50,000 tickets = 50000000000 +-- place 2: 30,000 = 30000000000, 3: 20,000 = 20000000000, 4: 15,000 = 15000000000, 5: 10,000 = 10000000000 +-- places 6-10: 5,000 each = 5000000000 +SET @promo_id = LAST_INSERT_ID(); +INSERT INTO promotions_rewards (promo_id, place, reward) VALUES +(@promo_id, 1, 50000000000), +(@promo_id, 2, 30000000000), +(@promo_id, 3, 20000000000), +(@promo_id, 4, 15000000000), +(@promo_id, 5, 10000000000), +(@promo_id, 6, 5000000000), +(@promo_id, 7, 5000000000), +(@promo_id, 8, 5000000000), +(@promo_id, 9, 5000000000), +(@promo_id, 10, 5000000000); diff --git a/src/main/resources/db/migration/V58__add_promotions_total_reward.sql b/src/main/resources/db/migration/V58__add_promotions_total_reward.sql new file mode 100644 index 0000000..a1ba390 --- /dev/null +++ b/src/main/resources/db/migration/V58__add_promotions_total_reward.sql @@ -0,0 +1,9 @@ +-- total_reward in tickets (BIGINT: 1 ticket = 1_000_000) +ALTER TABLE promotions +ADD COLUMN total_reward BIGINT NULL DEFAULT NULL COMMENT 'Total prize fund in bigint (1 ticket = 1000000)' AFTER status; + +-- First promo: 150 000 tickets = 150_000_000_000 +UPDATE promotions SET total_reward = 150000000000 WHERE id = 1; + +-- Index for filtering by status (already have idx_promotions_status) +-- total_reward is for display only, no extra index needed diff --git a/src/main/resources/db/migration/V59__create_notifications_audit.sql b/src/main/resources/db/migration/V59__create_notifications_audit.sql new file mode 100644 index 0000000..f1dc375 --- /dev/null +++ b/src/main/resources/db/migration/V59__create_notifications_audit.sql @@ -0,0 +1,10 @@ +-- Audit of notification broadcast sends: one row per user send attempt. +CREATE TABLE notifications_audit ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT 'Internal user id from db_users_a', + status VARCHAR(20) NOT NULL COMMENT 'SUCCESS or FAILED', + telegram_status_code INT NULL COMMENT 'HTTP status from Telegram API response (e.g. 200, 403)', + created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + INDEX idx_notifications_audit_user_id (user_id), + INDEX idx_notifications_audit_created_at (created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/main/resources/db/migration/V5__add_screen_name_to_users_d.sql b/src/main/resources/db/migration/V5__add_screen_name_to_users_d.sql new file mode 100644 index 0000000..6b69c0b --- /dev/null +++ b/src/main/resources/db/migration/V5__add_screen_name_to_users_d.sql @@ -0,0 +1,12 @@ +-- Add screen_name column to db_users_d table +ALTER TABLE `db_users_d` +ADD COLUMN `screen_name` VARCHAR(75) NOT NULL DEFAULT '-' AFTER `id`; + +-- Update existing records to copy screen_name from db_users_a +UPDATE `db_users_d` ud +INNER JOIN `db_users_a` ua ON ud.id = ua.id +SET ud.screen_name = ua.screen_name; + + + + diff --git a/src/main/resources/db/migration/V60__update_promotion_1_rewards.sql b/src/main/resources/db/migration/V60__update_promotion_1_rewards.sql new file mode 100644 index 0000000..1389e00 --- /dev/null +++ b/src/main/resources/db/migration/V60__update_promotion_1_rewards.sql @@ -0,0 +1,15 @@ +-- Update promotion id=1 rewards (place -> tickets; stored as tickets * 1_000_000) +-- 1->300k, 2->200k, 3->150k, 4->100k, 5->80k, 6->55k, 7->40k, 8->30k, 9->25k, 10->20k +UPDATE promotions_rewards SET reward = 300000000000 WHERE promo_id = 1 AND place = 1; +UPDATE promotions_rewards SET reward = 200000000000 WHERE promo_id = 1 AND place = 2; +UPDATE promotions_rewards SET reward = 150000000000 WHERE promo_id = 1 AND place = 3; +UPDATE promotions_rewards SET reward = 100000000000 WHERE promo_id = 1 AND place = 4; +UPDATE promotions_rewards SET reward = 80000000000 WHERE promo_id = 1 AND place = 5; +UPDATE promotions_rewards SET reward = 55000000000 WHERE promo_id = 1 AND place = 6; +UPDATE promotions_rewards SET reward = 40000000000 WHERE promo_id = 1 AND place = 7; +UPDATE promotions_rewards SET reward = 30000000000 WHERE promo_id = 1 AND place = 8; +UPDATE promotions_rewards SET reward = 25000000000 WHERE promo_id = 1 AND place = 9; +UPDATE promotions_rewards SET reward = 20000000000 WHERE promo_id = 1 AND place = 10; + +-- Total prize fund: 1_000_000 tickets = 1_000_000_000_000 +UPDATE promotions SET total_reward = 1000000000000 WHERE id = 1; diff --git a/src/main/resources/db/migration/V62__notifications_audit_user_created_index.sql b/src/main/resources/db/migration/V62__notifications_audit_user_created_index.sql new file mode 100644 index 0000000..a37cf54 --- /dev/null +++ b/src/main/resources/db/migration/V62__notifications_audit_user_created_index.sql @@ -0,0 +1,3 @@ +-- Composite index for "latest notification audit per user" (ignoreBlocked: skip users whose last send was FAILED). +-- findTopByUserIdOrderByCreatedAtDesc(user_id) uses this for efficient lookup. +CREATE INDEX idx_notifications_audit_user_created ON notifications_audit (user_id, created_at DESC); diff --git a/src/main/resources/db/migration/V63__system_settings_bot_max_participants.sql b/src/main/resources/db/migration/V63__system_settings_bot_max_participants.sql new file mode 100644 index 0000000..1e8d948 --- /dev/null +++ b/src/main/resources/db/migration/V63__system_settings_bot_max_participants.sql @@ -0,0 +1,9 @@ +-- Configurations: key-value store for app-wide settings (e.g. lottery bot scheduler). +CREATE TABLE IF NOT EXISTS configurations ( + `key` VARCHAR(128) NOT NULL PRIMARY KEY, + value VARCHAR(512) NOT NULL DEFAULT '' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Bots may join a round only when participant count <= this value (default 1 = join when 0 or 1 participant). +INSERT INTO configurations (`key`, value) VALUES ('lottery_bot_max_participants_before_join', '1') +ON DUPLICATE KEY UPDATE `key` = VALUES(`key`); diff --git a/src/main/resources/db/migration/V64__game_rounds_room_phase_started_at_index.sql b/src/main/resources/db/migration/V64__game_rounds_room_phase_started_at_index.sql new file mode 100644 index 0000000..e65ac00 --- /dev/null +++ b/src/main/resources/db/migration/V64__game_rounds_room_phase_started_at_index.sql @@ -0,0 +1,4 @@ +-- Index for admin online-users and other flows that fetch "most recent active round" per room. +-- Query: WHERE room_id = ? AND phase IN (...) ORDER BY started_at DESC LIMIT 1 +-- Covers filter by room_id and phase, and sort by started_at without filesort. +CREATE INDEX idx_game_rounds_room_phase_started_at ON game_rounds (room_id, phase, started_at DESC); diff --git a/src/main/resources/db/migration/V65__feature_switch_manual_pay_for_all_payouts.sql b/src/main/resources/db/migration/V65__feature_switch_manual_pay_for_all_payouts.sql new file mode 100644 index 0000000..544ae6a --- /dev/null +++ b/src/main/resources/db/migration/V65__feature_switch_manual_pay_for_all_payouts.sql @@ -0,0 +1,4 @@ +-- When enabled (1), send manual_pay=1 for all crypto payouts. When disabled (0), send manual_pay=1 only for users who completed 50 or 100 referrals (first withdrawal). Default on. +INSERT INTO `feature_switches` (`key`, `enabled`) VALUES + ('manual_pay_for_all_payouts', 1) +ON DUPLICATE KEY UPDATE `key` = VALUES(`key`); diff --git a/src/main/resources/db/migration/V66__payouts_add_txhash.sql b/src/main/resources/db/migration/V66__payouts_add_txhash.sql new file mode 100644 index 0000000..c0fe7e8 --- /dev/null +++ b/src/main/resources/db/migration/V66__payouts_add_txhash.sql @@ -0,0 +1,2 @@ +-- Store transaction hash from crypto withdrawal API (WithdrawalInfoApiResponse.PaymentItem.txhash) +ALTER TABLE `payouts` ADD COLUMN `txhash` VARCHAR(255) NULL AFTER `payment_id`; diff --git a/src/main/resources/db/migration/V67__admin_user_deposits_withdrawals_indexes.sql b/src/main/resources/db/migration/V67__admin_user_deposits_withdrawals_indexes.sql new file mode 100644 index 0000000..22b65fd --- /dev/null +++ b/src/main/resources/db/migration/V67__admin_user_deposits_withdrawals_indexes.sql @@ -0,0 +1,9 @@ +-- Indexes for admin User Detail tabs: Deposits and Withdrawals. +-- Deposits: GET /admin/users/{id}/payments — WHERE user_id = ? ORDER BY created_at DESC (default). +-- Withdrawals: GET /admin/users/{id}/payouts — WHERE user_id = ? AND type = 'CRYPTO' ORDER BY created_at DESC (default). + +-- payments: optimize "list payments by user" with default sort by created_at +CREATE INDEX idx_payments_user_created_at ON payments(user_id, created_at); + +-- payouts: optimize "list CRYPTO payouts by user" with default sort by created_at +CREATE INDEX idx_payouts_user_type_created_at ON payouts(user_id, type, created_at); diff --git a/src/main/resources/db/migration/V68__payouts_user_type_crypto_name_index.sql b/src/main/resources/db/migration/V68__payouts_user_type_crypto_name_index.sql new file mode 100644 index 0000000..5da923b --- /dev/null +++ b/src/main/resources/db/migration/V68__payouts_user_type_crypto_name_index.sql @@ -0,0 +1,2 @@ +-- Withdrawals tab: sort by Ticker (crypto_name). Query: WHERE user_id = ? AND type = 'CRYPTO' ORDER BY crypto_name. +CREATE INDEX idx_payouts_user_type_crypto_name ON payouts(user_id, type, crypto_name); diff --git a/src/main/resources/db/migration/V69__users_b_withdrawals_disabled.sql b/src/main/resources/db/migration/V69__users_b_withdrawals_disabled.sql new file mode 100644 index 0000000..f8c1cde --- /dev/null +++ b/src/main/resources/db/migration/V69__users_b_withdrawals_disabled.sql @@ -0,0 +1,2 @@ +-- Per-user withdrawal restriction. When 1, the user cannot create any payout request (STARS, GIFT, CRYPTO). +ALTER TABLE `db_users_b` ADD COLUMN `withdrawals_disabled` TINYINT(1) NOT NULL DEFAULT 0 AFTER `total_win_after_deposit`; diff --git a/src/main/resources/db/migration/V6__create_tasks_tables.sql b/src/main/resources/db/migration/V6__create_tasks_tables.sql new file mode 100644 index 0000000..d2725fe --- /dev/null +++ b/src/main/resources/db/migration/V6__create_tasks_tables.sql @@ -0,0 +1,41 @@ +-- Create tasks table +CREATE TABLE `tasks` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` varchar(20) NOT NULL COMMENT 'referral, follow, other', + `requirement` int NOT NULL COMMENT 'Number required (e.g., number of friends to invite)', + `reward_amount` bigint NOT NULL COMMENT 'Reward amount in bigint format', + `reward_type` varchar(20) NOT NULL DEFAULT 'Stars' COMMENT 'Stars, Power, etc.', + `display_order` int NOT NULL DEFAULT 0 COMMENT 'Order for display', + `title` varchar(255) NOT NULL COMMENT 'Task title (e.g., "Invite 1 friend")', + `description` text COMMENT 'Task description', + PRIMARY KEY (`id`), + KEY `idx_type` (`type`), + KEY `idx_display_order` (`display_order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- Create user_task_claims table to track claimed tasks +-- If record exists, it means the task was claimed and reward was given +CREATE TABLE `user_task_claims` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `task_id` int NOT NULL, + `claimed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_user_task` (`user_id`, `task_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_task_id` (`task_id`), + CONSTRAINT `fk_user_task_claims_user` FOREIGN KEY (`user_id`) REFERENCES `db_users_a` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_task_claims_task` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- Insert default referral tasks +-- reward_amount is in bigint format (e.g., 2 Stars = 2000000, 5 Stars = 5000000) +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) VALUES +('referral', 1, 2000000, 'Stars', 1, 'Invite 1 friend', 'Invite 1 friend using your unique referral link'), +('referral', 3, 5000000, 'Stars', 2, 'Invite 3 friends', 'Invite 3 friends using your unique referral link'), +('referral', 7, 15000000, 'Stars', 3, 'Invite 7 friends', 'Invite 7 friends using your unique referral link'), +('referral', 15, 25000000, 'Stars', 4, 'Invite 15 friends', 'Invite 15 friends using your unique referral link'), +('referral', 30, 40000000, 'Stars', 5, 'Invite 30 friends', 'Invite 30 friends using your unique referral link'), +('referral', 50, 60000000, 'Stars', 6, 'Invite 50 friends', 'Invite 50 friends using your unique referral link'), +('referral', 100, 150000000, 'Stars', 7, 'Invite 100 friends', 'Invite 100 friends using your unique referral link'); + diff --git a/src/main/resources/db/migration/V70__seed_net_win_max_bet_and_ref_count_promotions.sql b/src/main/resources/db/migration/V70__seed_net_win_max_bet_and_ref_count_promotions.sql new file mode 100644 index 0000000..6e47c93 --- /dev/null +++ b/src/main/resources/db/migration/V70__seed_net_win_max_bet_and_ref_count_promotions.sql @@ -0,0 +1,33 @@ +-- Two new promotions: NET_WIN_MAX_BET and REF_COUNT, start 4 March 2026 14:00 UTC, same rewards as promotion 1 (V60) +-- End time: 11 March 2026 14:00 UTC (1 week) +INSERT INTO promotions (type, start_time, end_time, status, total_reward) VALUES +('NET_WIN_MAX_BET', '2026-03-04 14:00:00', '2026-03-11 14:00:00', 'PLANNED', 1000000000000), +('REF_COUNT', '2026-03-04 14:00:00', '2026-03-11 14:00:00', 'PLANNED', 1000000000000); + +-- NET_WIN_MAX_BET rewards (same as promotion 1) +SET @promo_max_bet = (SELECT id FROM promotions WHERE type = 'NET_WIN_MAX_BET' AND start_time = '2026-03-04 14:00:00' LIMIT 1); +INSERT INTO promotions_rewards (promo_id, place, reward) VALUES +(@promo_max_bet, 1, 300000000000), +(@promo_max_bet, 2, 200000000000), +(@promo_max_bet, 3, 150000000000), +(@promo_max_bet, 4, 100000000000), +(@promo_max_bet, 5, 80000000000), +(@promo_max_bet, 6, 55000000000), +(@promo_max_bet, 7, 40000000000), +(@promo_max_bet, 8, 30000000000), +(@promo_max_bet, 9, 25000000000), +(@promo_max_bet, 10, 20000000000); + +-- REF_COUNT rewards (same structure) +SET @promo_ref = (SELECT id FROM promotions WHERE type = 'REF_COUNT' AND start_time = '2026-03-04 14:00:00' LIMIT 1); +INSERT INTO promotions_rewards (promo_id, place, reward) VALUES +(@promo_ref, 1, 300000000000), +(@promo_ref, 2, 200000000000), +(@promo_ref, 3, 150000000000), +(@promo_ref, 4, 100000000000), +(@promo_ref, 5, 80000000000), +(@promo_ref, 6, 55000000000), +(@promo_ref, 7, 40000000000), +(@promo_ref, 8, 30000000000), +(@promo_ref, 9, 25000000000), +(@promo_ref, 10, 20000000000); diff --git a/src/main/resources/db/migration/V7__add_follow_and_other_tasks.sql b/src/main/resources/db/migration/V7__add_follow_and_other_tasks.sql new file mode 100644 index 0000000..43d1e33 --- /dev/null +++ b/src/main/resources/db/migration/V7__add_follow_and_other_tasks.sql @@ -0,0 +1,14 @@ +-- Insert Follow task +-- reward_amount is in bigint format (5 Stars = 5000000) +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) VALUES +('follow', 1, 5000000, 'Stars', 1, 'Follow our News channel', 'Follow our News channel'); + +-- Insert Other task +-- reward_amount is in bigint format (100 Stars = 100000000) +-- requirement is deposit_total in bigint format (500000000 = 500 USD in bigint) +INSERT INTO `tasks` (`type`, `requirement`, `reward_amount`, `reward_type`, `display_order`, `title`, `description`) VALUES +('other', 500000000, 100000000, 'Stars', 1, 'Top Up Balance: $5', 'Top Up Balance: $5'); + + + + diff --git a/src/main/resources/db/migration/V8__create_payouts_table.sql b/src/main/resources/db/migration/V8__create_payouts_table.sql new file mode 100644 index 0000000..a394738 --- /dev/null +++ b/src/main/resources/db/migration/V8__create_payouts_table.sql @@ -0,0 +1,18 @@ +-- Create payouts table +CREATE TABLE IF NOT EXISTS payouts ( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + username VARCHAR(255) NOT NULL, + type VARCHAR(20) NOT NULL COMMENT 'STARS, GIFT', + gift_name VARCHAR(50) NULL COMMENT 'Gift name for GIFT type (HEART, BEAR, etc)', + total BIGINT UNSIGNED NOT NULL COMMENT 'Tickets amount in bigint format', + stars_amount INT NOT NULL COMMENT 'Stars amount', + status VARCHAR(20) NOT NULL DEFAULT 'PROCESSING' COMMENT 'PROCESSING, COMPLETED, CANCELLED', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + resolved_at TIMESTAMP NULL, + INDEX idx_user_id (user_id), + INDEX idx_status (status), + INDEX idx_created_at (created_at), + FOREIGN KEY (user_id) REFERENCES db_users_a(id) ON DELETE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + diff --git a/src/main/resources/db/migration/V9__add_quantity_to_payouts.sql b/src/main/resources/db/migration/V9__add_quantity_to_payouts.sql new file mode 100644 index 0000000..f3e1e88 --- /dev/null +++ b/src/main/resources/db/migration/V9__add_quantity_to_payouts.sql @@ -0,0 +1,7 @@ +-- Add quantity column to payouts table +ALTER TABLE payouts +ADD COLUMN quantity INT NOT NULL DEFAULT 1 COMMENT 'Quantity of gifts/stars (1-100)' AFTER stars_amount; + + + + diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..412d5e9 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOG_DIR}/${APP_NAME}.log + + + + + ${LOG_DIR}/${APP_NAME}-%d{yyyy-MM-dd}.%i.log + + + 50MB + + + 14 + days + + + 10GB + + + true + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + 0 + + + 256 + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 0000000..f7be116 --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1,201 @@ +# Default English messages +# This file contains all user-facing messages in the application + +# Telegram Bot Messages +bot.button.startSpinning=Start Game +bot.button.startSpinningInline=↘️ START GAME ↙️ +bot.button.usersPayouts=Users payouts +bot.button.infoChannel=Info channel +bot.button.openChannel=Open Channel +bot.button.goToChannel=Go to the channel +bot.welcome.firstMessage=Try your luck with Win Spin! +bot.welcome.message=Start betting, win and withdraw straight to your wallet.\n\n👉 To get started, watch the video above. +bot.message.startSpinning=Use this button to open the Win Spin app: +bot.message.usersPayouts=🔒 Real-time updates.\n\nAll withdrawals are shown in the channel. You can check everything yourself: +bot.message.infoChannel=📢 Stay informed with all the latest project updates — right here on our channel +bot.message.paySupport=Hello! In order to create a support request regarding your payment, please contact @winspinpaysupport.\n\nPlease note: Refunds are only granted if the purchased tickets have not yet been used in game rounds. +bot.message.unrecognized=We couldn't recognize that. Please use one of the buttons below. + +# Common +common.error.unknown=An unexpected error occurred +common.error.validation=Validation error +common.success=Success + +# Feature switches (payment / payout disabled) +feature.depositsUnavailable=Deposits are temporarily unavailable. We apologise for the inconvenience. Please try again later. +feature.payoutsUnavailable=Withdrawals are temporarily unavailable. We apologise for the inconvenience. Please try again later. + +# Authentication +auth.error.invalid=Invalid authentication +auth.error.expired=Session expired +auth.error.required=Authentication required +auth.error.accessRestricted=Application access restricted. +auth.error.initDataRequired=initData is required +auth.error.initDataMissing=Telegram initData is missing +auth.error.missingHash=Missing Telegram hash +auth.error.invalidSignature=Invalid Telegram signature +auth.error.userFieldMissing=initData does not contain 'user' field +auth.error.invalidInitData=Invalid Telegram initData + +# User +user.error.notFound=User not found +user.error.banned=User is banned +user.language.updated=Language updated successfully + +# Game +game.error.roomNotFound=Room not found. Please try again. +game.error.invalidBetAmount=Bet amount must be between {0} and {1} +game.error.insufficientBalance=Insufficient balance +game.error.rateLimit=Too many requests. Please wait a moment before trying again. +game.error.roundNotActive=Round is not active +game.error.maxBetExceeded=You have exceeded the maximum bet limit of {0} for this room. Your current total bet is {1}, so you can bet up to {2} more. +game.error.betMustBePositive=Bet amount must be a positive integer. +game.error.roomNumberInvalid=Room number must be between 1 and 3. + +# Task +task.error.notFound=Task not found +task.error.notCompleted=Task not completed! +task.error.alreadyClaimed=Task already claimed +task.success.claimed=Task claimed successfully +task.claimed=Claimed + +# Transaction +transaction.error.notFound=Transaction not found + +# Payout +payout.error.withdrawalsRestrictedForAccount=We're sorry, but withdrawals have been restricted for your account. Please contact Support for assistance. +payout.error.depositRequiredToWithdraw=Due to the increasing incidence of fraud, at least one deposit is required to create a withdrawal request. +payout.error.withdrawExceedsWinAfterDeposit=Due to increased fraud, withdrawal requests cannot exceed your total winnings since your last deposit. You can currently withdraw a maximum of {0} tickets. +payout.error.invalidAmount=Invalid payout amount +payout.error.minimumNotMet=Minimum payout amount not met +payout.error.insufficientBalance=Insufficient balance for payout +payout.error.quantityMustBeOne=Quantity must be 1 for this payout type +payout.error.invalidPayoutType=Invalid payout type +payout.error.starsAmountNonNegative=Amount must be a non-negative integer +payout.error.minimumStarsRequired=Minimum amount required +payout.error.starsAmountNotAllowed=Amount must be one of the allowed options +payout.error.starsAmountAndTotalRequired=Amount and total are required +payout.error.totalMustEqualStars=Total must match the expected value. Expected: {0}, provided: {1} +payout.error.giftNameRequired=Gift name is required for GIFT type payout +payout.error.invalidGiftName=Invalid gift name: {0} +payout.error.starsAmountNotDefined=Amount not defined for gift: {0} +payout.error.totalNotDefined=Total not defined for gift: {0} +payout.error.giftNameTotalQuantityRequired=Gift name, total, and quantity are required +payout.error.totalForGiftMismatch=Total for gift {0} (quantity {1}) must be {2}, but provided: {3} +payout.error.quantityRequired=Quantity is required +payout.error.quantityRange=Quantity must be between 1 and 100 +payout.error.usernameRequired=Username is required +payout.error.usernamePattern=Username must start with @ followed by at least one English letter +payout.error.ticketsAmountNonNegative=Tickets amount must be a non-negative number +payout.error.insufficientBalanceDetailed=Insufficient balance. Available: {0}, Required: {1} +payout.error.notFound=Payout not found: {0} +payout.error.onlyProcessingCanComplete=Only PROCESSING payouts can be completed. Current status: {0} +payout.error.onlyProcessingCanCancel=Only PROCESSING payouts can be cancelled. Current status: {0} +payout.error.withdrawalInProgress=You already have a withdrawal in progress. Please wait for it to complete. +payout.error.withdrawalAmountMaxTwoDecimals=Withdrawal amount must have at most 2 decimal places (e.g. 125.25). Values like 125.125 are not supported. +payout.success.requested=Payout request submitted successfully + +# Withdraw (crypto) +withdraw.error.walletInvalidFormat=The wallet address format is incorrect. Please check and try again. +withdraw.error.tryLater=There is an issue with withdrawals at the moment. Please try again a bit later. + +# Payment +payment.error.invalidAmount=Invalid payment amount +payment.error.minStars=Amount must be at least {0} +payment.error.maxStars=Amount cannot exceed {0} +payment.error.usdRange=USD amount must be between {0} and {1} +payment.error.usdMaxTwoDecimals=USD amount must have at most 2 decimal places (e.g. 2.45). +payment.error.legacyNotSupported=This payment method is no longer supported. Please use available methods to purchase tickets. +payment.error.botTokenNotConfigured=Bot token is not configured +payment.error.failedToCreateInvoice=Failed to create invoice link +payment.error.paymentNotFound=Payment not found for order ID: {0} +payment.error.userNotFound=User not found for Telegram ID: {0} +payment.error.userIdMismatch=User ID mismatch. Expected: {0}, but got: {1} +payment.error.starsAmountMismatch=Amount mismatch. Expected: {0}, but got: {1} +payment.error.failed=Payment failed. Please try again. +payment.error.statusUnknown=Payment status unknown. Please check your balance. +payment.error.invalidPid=Invalid payment method selected. +payment.error.depositAddressFailed=Failed to get deposit address. Please try again. +payment.success.purchased=Successfully purchased {0} tickets! + +# Support +support.error.ticketNotFound=Ticket not found +support.error.messageFailed=Failed to send message +support.error.closeFailed=Failed to close ticket +support.error.maxTicketsReached=You have reached the maximum limit of {0} open tickets. Please close some tickets before creating a new one. +support.error.ticketClosed=Cannot add message to a closed ticket. Please create a new ticket. +support.error.maxMessagesReached=You have reached the maximum limit of {0} messages per ticket. Please create a new ticket. +support.error.rateLimitWait=Please wait {0} second(s) before sending another message. +support.error.ticketAlreadyClosed=Ticket is already closed. + +# Validation +validation.error.required={0} is required +validation.error.positive={0} must be positive +validation.error.range={0} must be between {1} and {2} + +# Game Room (additional messages) +game.error.roundNotFound=Game round not found. Please try again. +game.error.participantNotFound=Participant not found. Please try again. +game.error.rateLimitWait=Please wait before placing another bet. Rate limit: 1 bet per 1 second. +game.error.roomNotJoinable=Room is not joinable at this time +game.error.invalidRequest=Invalid request. Request body is required. + +# User Service +user.error.referralLevelInvalid=Invalid referral level: {0}. Must be 1, 2, or 3. +user.error.depositAmountInvalid=Deposit amount must be positive +user.error.balanceNotFound=User balance not found + +# Task Controller +task.message.claimed=Task claimed successfully +task.message.notCompleted=Task not completed! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Invite 1 friend +task.title.inviteFriends.3=Invite 3 friends +task.title.inviteFriends.7=Invite 7 friends +task.title.inviteFriends.15=Invite 15 friends +task.title.inviteFriends.30=Invite 30 friends +task.title.inviteFriends.50=Invite 50 friends +task.title.inviteFriends.100=Invite 100 friends +task.title.followChannel=Follow our News channel +task.title.followChannelWithdrawals=Follow Proof of payment channel +task.title.dailyBonus=Daily Bonus +task.title.deposit=Deposit {0} tickets +task.description.inviteFriends.1=Invite 1 friend using your unique referral link +task.description.inviteFriends.3=Invite 3 friends using your unique referral link +task.description.inviteFriends.7=Invite 7 friends using your unique referral link +task.description.inviteFriends.15=Invite 15 friends using your unique referral link +task.description.inviteFriends.30=Invite 30 friends using your unique referral link +task.description.inviteFriends.50=Invite 50 friends using your unique referral link +task.description.inviteFriends.100=Invite 100 friends using your unique referral link +task.description.followChannel=Follow our News channel +task.description.followChannelWithdrawals=Follow Proof of payment channel +task.description.dailyBonus=Claim your daily free ticket! +task.description.deposit=Deposit {0} tickets to your account + +# Task Reward Text (localized with proper grammar) +# Referral tasks - separate keys for each requirement +task.reward.tickets.1=+5 Tickets +task.reward.tickets.3=+15 Tickets +task.reward.tickets.7=+35 Tickets +task.reward.tickets.15=+75 Tickets +task.reward.tickets.30=+110 Tickets +task.reward.tickets.50=+150 Tickets +task.reward.tickets.100=+375 Tickets +task.reward.tickets.follow=+7 Tickets +task.reward.tickets.daily=+1 Ticket +task.reward.tickets.other.50=+5 Tickets +task.reward.tickets.other.250=+25 Tickets +task.reward.tickets.other.1000=+100 Tickets +task.reward.tickets.other.2000=+100 Tickets +task.reward.tickets.other.5000=+250 Tickets +task.reward.tickets.other.10000=+500 Tickets +task.reward.tickets.other.500=+100 Tickets +task.reward.tickets.other.50000=+2500 Tickets +task.reward.tickets.other.150000=+7500 Tickets +task.reward.tickets.other.500000=+25000 Tickets + +# Date/Time formatting +dateTime.at=at + diff --git a/src/main/resources/messages_de.properties b/src/main/resources/messages_de.properties new file mode 100644 index 0000000..7a6f590 --- /dev/null +++ b/src/main/resources/messages_de.properties @@ -0,0 +1,144 @@ +# German messages +common.error.unknown=Ein unerwarteter Fehler ist aufgetreten +common.error.validation=Validierungsfehler +common.success=Erfolg + +feature.depositsUnavailable=Einzahlungen sind vorübergehend nicht verfügbar. Wir entschuldigen uns für die Unannehmlichkeiten. Bitte versuchen Sie es später erneut. +feature.payoutsUnavailable=Auszahlungen sind vorübergehend nicht verfügbar. Wir entschuldigen uns für die Unannehmlichkeiten. Bitte versuchen Sie es später erneut. + +auth.error.invalid=Ungültige Authentifizierung +auth.error.expired=Sitzung abgelaufen +auth.error.required=Authentifizierung erforderlich +auth.error.accessRestricted=Zugriff auf die Anwendung eingeschränkt. + +user.error.notFound=Benutzer nicht gefunden +user.error.banned=Benutzer ist gesperrt +user.language.updated=Sprache erfolgreich aktualisiert + +game.error.roomNotFound=Raum nicht gefunden. Bitte versuchen Sie es erneut. +game.error.invalidBetAmount=Der Einsatzbetrag muss zwischen {0} und {1} liegen +game.error.insufficientBalance=Unzureichendes Guthaben +game.error.rateLimit=Zu viele Anfragen. Bitte warten Sie einen Moment, bevor Sie es erneut versuchen. +game.error.roundNotActive=Runde ist nicht aktiv +game.error.maxBetExceeded=Sie haben das maximale Einsatzlimit von {0} für diesen Raum überschritten. Ihr aktueller Gesamteinsatz beträgt {1}, daher können Sie noch bis zu {2} mehr setzen. +game.error.betMustBePositive=Der Einsatzbetrag muss eine positive ganze Zahl sein. +game.error.roomNumberInvalid=Die Raumnummer muss zwischen 1 und 3 liegen. + +task.error.notFound=Aufgabe nicht gefunden +task.error.notCompleted=Aufgabe nicht abgeschlossen! +task.error.alreadyClaimed=Aufgabe bereits erhalten +task.success.claimed=Aufgabe erfolgreich erhalten +task.claimed=Eingelöst + +transaction.error.notFound=Transaktion nicht gefunden + +payout.error.withdrawalsRestrictedForAccount=Wir bedauern, aber Auszahlungen wurden für Ihr Konto eingeschränkt. Bitte kontaktieren Sie den Support. +payout.error.depositRequiredToWithdraw=Aufgrund des zunehmenden Betrugs ist mindestens eine Einzahlung erforderlich, um eine Auszahlungsanfrage zu erstellen. +payout.error.withdrawExceedsWinAfterDeposit=Aufgrund des zunehmenden Betrugs dürfen Auszahlungsanfragen Ihre Gesamtgewinne seit Ihrer letzten Einzahlung nicht überschreiten. Sie können derzeit maximal {0} Tickets abheben. +payout.error.invalidAmount=Ungültiger Auszahlungsbetrag +payout.error.minimumNotMet=Mindestauszahlungsbetrag nicht erreicht +payout.error.insufficientBalance=Unzureichendes Guthaben für Auszahlung +payout.error.withdrawalInProgress=Sie haben bereits eine Auszahlung in Bearbeitung. Bitte warten Sie auf den Abschluss. +payout.success.requested=Auszahlungsanfrage erfolgreich übermittelt + +withdraw.error.walletInvalidFormat=Das Format der Wallet-Adresse ist falsch. Bitte überprüfen Sie und versuchen Sie es erneut. +withdraw.error.tryLater=Derzeit gibt es ein Problem mit Auszahlungen. Bitte versuchen Sie es etwas später erneut. + +payment.error.invalidAmount=Ungültiger Zahlungsbetrag +payment.error.minStars=Der Betrag muss mindestens {0} betragen +payment.error.maxStars=Der Betrag darf {0} nicht überschreiten +payment.error.usdMaxTwoDecimals=Der USD-Betrag darf höchstens 2 Nachkommastellen haben (z. B. 2,45). +payment.error.legacyNotSupported=Diese Zahlungsmethode wird nicht mehr unterstützt. Bitte verwenden Sie die verfügbaren Methoden, um Tickets zu kaufen. +payment.error.failed=Zahlung fehlgeschlagen. Bitte versuchen Sie es erneut. +payment.error.statusUnknown=Zahlungsstatus unbekannt. Bitte überprüfen Sie Ihr Guthaben. +payment.success.purchased=Erfolgreich {0} Tickets gekauft! + +support.error.ticketNotFound=Ticket nicht gefunden +support.error.messageFailed=Nachricht konnte nicht gesendet werden +support.error.closeFailed=Ticket konnte nicht geschlossen werden +support.error.rateLimitWait=Bitte warten Sie {0} Sekunde(n), bevor Sie eine weitere Nachricht senden. + +validation.error.required={0} ist erforderlich +validation.error.positive={0} muss positiv sein +validation.error.range={0} muss zwischen {1} und {2} liegen + +# Game Room (additional messages) +game.error.roundNotFound=Spielrunde nicht gefunden. Bitte versuchen Sie es erneut. +game.error.participantNotFound=Teilnehmer nicht gefunden. Bitte versuchen Sie es erneut. +game.error.rateLimitWait=Bitte warten Sie, bevor Sie einen weiteren Einsatz platzieren. Rate-Limit: 1 Einsatz pro Sekunde. +game.error.roomNotJoinable=Raum ist derzeit nicht beitretbar +game.error.invalidRequest=Ungültige Anfrage. Anfragekörper ist erforderlich. + +# User Service +user.error.referralLevelInvalid=Ungültiges Empfehlungslevel: {0}. Muss 1, 2 oder 3 sein. +user.error.depositAmountInvalid=Einzahlungsbetrag muss positiv sein +user.error.balanceNotFound=Benutzerguthaben nicht gefunden + +# Task Controller +task.message.claimed=Aufgabe erfolgreich erhalten +task.message.notCompleted=Aufgabe nicht abgeschlossen! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Lade 1 Freund ein +task.title.inviteFriends.3=Lade 3 Freunde ein +task.title.inviteFriends.7=Lade 7 Freunde ein +task.title.inviteFriends.15=Lade 15 Freunde ein +task.title.inviteFriends.30=Lade 30 Freunde ein +task.title.inviteFriends.50=Lade 50 Freunde ein +task.title.inviteFriends.100=Lade 100 Freunde ein +task.title.followChannel=Folgen Sie unserem Nachrichtenkanal +task.title.followChannelWithdrawals=Folgen Sie dem Kanal für Zahlungsnachweise +task.title.dailyBonus=Täglicher Bonus +task.title.deposit=Zahle {0} Tickets ein +task.description.inviteFriends.1=Lade 1 Freund mit deinem eindeutigen Empfehlungslink ein +task.description.inviteFriends.3=Lade 3 Freunde mit deinem eindeutigen Empfehlungslink ein +task.description.inviteFriends.7=Lade 7 Freunde mit deinem eindeutigen Empfehlungslink ein +task.description.inviteFriends.15=Lade 15 Freunde mit deinem eindeutigen Empfehlungslink ein +task.description.inviteFriends.30=Lade 30 Freunde mit deinem eindeutigen Empfehlungslink ein +task.description.inviteFriends.50=Lade 50 Freunde mit deinem eindeutigen Empfehlungslink ein +task.description.inviteFriends.100=Lade 100 Freunde mit deinem eindeutigen Empfehlungslink ein +task.description.followChannel=Folgen Sie unserem Nachrichtenkanal +task.description.followChannelWithdrawals=Folgen Sie dem Kanal für Zahlungsnachweise +task.description.dailyBonus=Hole dir dein tägliches kostenloses Ticket! +task.description.deposit=Zahle {0} Tickets auf dein Konto ein + +# Task Reward Text +task.reward.tickets.1=+5 Tickets +task.reward.tickets.3=+15 Tickets +task.reward.tickets.7=+35 Tickets +task.reward.tickets.15=+75 Tickets +task.reward.tickets.30=+110 Tickets +task.reward.tickets.50=+150 Tickets +task.reward.tickets.100=+375 Tickets +task.reward.tickets.follow=+7 Tickets +task.reward.tickets.daily=+1 Ticket +task.reward.tickets.other.50=+5 Tickets +task.reward.tickets.other.250=+25 Tickets +task.reward.tickets.other.1000=+100 Tickets +task.reward.tickets.other.2000=+100 Tickets +task.reward.tickets.other.5000=+250 Tickets +task.reward.tickets.other.10000=+500 Tickets +task.reward.tickets.other.500=+100 Tickets +task.reward.tickets.other.50000=+2500 Tickets +task.reward.tickets.other.150000=+7500 Tickets +task.reward.tickets.other.500000=+25000 Tickets + +# Date/Time formatting +dateTime.at=um + +# Telegram Bot Messages +bot.button.startSpinning=Spiel starten +bot.button.startSpinningInline=↘️ SPIEL STARTEN ↙️ +bot.button.usersPayouts=Benutzerauszahlungen +bot.button.infoChannel=Info-Kanal +bot.button.openChannel=Kanal öffnen +bot.button.goToChannel=Zum Kanal gehen +bot.welcome.firstMessage=Versuchen Sie Ihr Glück mit Win Spin! +bot.welcome.message=Beginnen Sie zu wetten, gewinnen Sie und ziehen Sie direkt auf Ihre Wallet ab.\n\n👉 Um zu beginnen, schauen Sie sich das Video oben an. +bot.message.startSpinning=Verwenden Sie diese Schaltfläche, um die Win Spin-App zu öffnen: +bot.message.usersPayouts=🔒 Echtzeit-Updates.\n\nAlle Auszahlungen werden im Kanal angezeigt. Sie können alles selbst überprüfen: +bot.message.infoChannel=📢 Bleiben Sie über alle neuesten Projekt-Updates informiert — direkt hier in unserem Kanal +bot.message.paySupport=Hallo! Um eine Supportanfrage bezüglich Ihrer Zahlung zu erstellen, kontaktieren Sie bitte @winspinpaysupport.\n\nBitte beachten Sie: Rückerstattungen werden nur gewährt, wenn die gekauften Tickets noch nicht in Spielrunden verwendet wurden. +bot.message.unrecognized=Wir konnten das nicht erkennen. Bitte nutzen Sie einen der Buttons unten. + diff --git a/src/main/resources/messages_es.properties b/src/main/resources/messages_es.properties new file mode 100644 index 0000000..b57d0cf --- /dev/null +++ b/src/main/resources/messages_es.properties @@ -0,0 +1,144 @@ +# Spanish messages +common.error.unknown=Ha ocurrido un error inesperado +common.error.validation=Error de validación +common.success=Éxito + +feature.depositsUnavailable=Los depósitos no están disponibles temporalmente. Nos disculpamos por las molestias. Por favor, inténtelo de nuevo más tarde. +feature.payoutsUnavailable=Los retiros no están disponibles temporalmente. Nos disculpamos por las molestias. Por favor, inténtelo de nuevo más tarde. + +auth.error.invalid=Autenticación inválida +auth.error.expired=Sesión expirada +auth.error.required=Autenticación requerida +auth.error.accessRestricted=Acceso a la aplicación restringido. + +user.error.notFound=Usuario no encontrado +user.error.banned=El usuario está baneado +user.language.updated=Idioma actualizado exitosamente + +game.error.roomNotFound=Sala no encontrada. Por favor, inténtelo de nuevo. +game.error.invalidBetAmount=El monto de la apuesta debe estar entre {0} y {1} +game.error.insufficientBalance=Saldo insuficiente +game.error.rateLimit=Demasiadas solicitudes. Por favor, espere un momento antes de intentar de nuevo. +game.error.roundNotActive=La ronda no está activa +game.error.maxBetExceeded=Has excedido el límite máximo de apuesta de {0} para esta sala. Tu apuesta total actual es {1}, por lo que puedes apostar hasta {2} más. +game.error.betMustBePositive=El monto de la apuesta debe ser un número entero positivo. +game.error.roomNumberInvalid=El número de sala debe estar entre 1 y 3. + +task.error.notFound=Tarea no encontrada +task.error.notCompleted=¡Tarea no completada! +task.error.alreadyClaimed=Tarea ya reclamada +task.success.claimed=Tarea reclamada exitosamente +task.claimed=Reclamado + +transaction.error.notFound=Transacción no encontrada + +payout.error.withdrawalsRestrictedForAccount=Lo sentimos, pero los retiros han sido restringidos para su cuenta. Por favor, contacte con Soporte. +payout.error.depositRequiredToWithdraw=Debido al aumento del fraude, se requiere al menos un depósito para crear una solicitud de retiro. +payout.error.withdrawExceedsWinAfterDeposit=Debido al aumento del fraude, las solicitudes de retiro no pueden exceder sus ganancias totales desde su último depósito. Actualmente puede retirar un máximo de {0} tickets. +payout.error.invalidAmount=Monto de retiro inválido +payout.error.minimumNotMet=Monto mínimo de retiro no alcanzado +payout.error.insufficientBalance=Saldo insuficiente para el retiro +payout.error.withdrawalInProgress=Ya tiene un retiro en curso. Espere a que se complete. +payout.success.requested=Solicitud de retiro enviada exitosamente + +withdraw.error.walletInvalidFormat=El formato de la dirección de la wallet es incorrecto. Compruebe e inténtelo de nuevo. +withdraw.error.tryLater=Hay un problema con los retiros en este momento. Por favor, inténtelo de nuevo un poco más tarde. + +payment.error.invalidAmount=Monto de pago inválido +payment.error.minStars=El monto debe ser al menos {0} +payment.error.maxStars=El monto no puede exceder {0} +payment.error.usdMaxTwoDecimals=El monto en USD debe tener como máximo 2 decimales (ej. 2,45). +payment.error.legacyNotSupported=Este método de pago ya no es compatible. Utilice los métodos disponibles para comprar boletos. +payment.error.failed=Pago fallido. Por favor, inténtelo de nuevo. +payment.error.statusUnknown=Estado de pago desconocido. Por favor, verifique su saldo. +payment.success.purchased=¡{0} boletos comprados exitosamente! + +support.error.ticketNotFound=Boleto no encontrado +support.error.messageFailed=No se pudo enviar el mensaje +support.error.closeFailed=No se pudo cerrar el boleto +support.error.rateLimitWait=Por favor, espere {0} segundo(s) antes de enviar otro mensaje. + +validation.error.required={0} es requerido +validation.error.positive={0} debe ser positivo +validation.error.range={0} debe estar entre {1} y {2} + +# Game Room (additional messages) +game.error.roundNotFound=Ronda de juego no encontrada. Por favor, inténtelo de nuevo. +game.error.participantNotFound=Participante no encontrado. Por favor, inténtelo de nuevo. +game.error.rateLimitWait=Por favor, espere antes de realizar otra apuesta. Límite de velocidad: 1 apuesta por segundo. +game.error.roomNotJoinable=La sala no es accesible en este momento +game.error.invalidRequest=Solicitud inválida. El cuerpo de la solicitud es requerido. + +# User Service +user.error.referralLevelInvalid=Nivel de referido inválido: {0}. Debe ser 1, 2 o 3. +user.error.depositAmountInvalid=El monto del depósito debe ser positivo +user.error.balanceNotFound=Saldo de usuario no encontrado + +# Task Controller +task.message.claimed=Tarea reclamada exitosamente +task.message.notCompleted=¡Tarea no completada! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Invita 1 amigo +task.title.inviteFriends.3=Invita 3 amigos +task.title.inviteFriends.7=Invita 7 amigos +task.title.inviteFriends.15=Invita 15 amigos +task.title.inviteFriends.30=Invita 30 amigos +task.title.inviteFriends.50=Invita 50 amigos +task.title.inviteFriends.100=Invita 100 amigos +task.title.followChannel=Sigue nuestro canal de noticias +task.title.followChannelWithdrawals=Sigue nuestro canal de comprobantes de pago +task.title.dailyBonus=Bonus diario +task.title.deposit=Deposita {0} boletos +task.description.inviteFriends.1=Invita 1 amigo usando tu enlace de referido único +task.description.inviteFriends.3=Invita 3 amigos usando tu enlace de referido único +task.description.inviteFriends.7=Invita 7 amigos usando tu enlace de referido único +task.description.inviteFriends.15=Invita 15 amigos usando tu enlace de referido único +task.description.inviteFriends.30=Invita 30 amigos usando tu enlace de referido único +task.description.inviteFriends.50=Invita 50 amigos usando tu enlace de referido único +task.description.inviteFriends.100=Invita 100 amigos usando tu enlace de referido único +task.description.followChannel=Sigue nuestro canal de noticias +task.description.followChannelWithdrawals=Sigue nuestro canal de comprobantes de pago +task.description.dailyBonus=¡Reclama tu boleto gratuito diario! +task.description.deposit=Deposita {0} boletos en tu cuenta + +# Task Reward Text +task.reward.tickets.1=+5 Boletos +task.reward.tickets.3=+15 Boletos +task.reward.tickets.7=+35 Boletos +task.reward.tickets.15=+75 Boletos +task.reward.tickets.30=+110 Boletos +task.reward.tickets.50=+150 Boletos +task.reward.tickets.100=+375 Boletos +task.reward.tickets.follow=+7 Boletos +task.reward.tickets.daily=+1 Boleto +task.reward.tickets.other.50=+5 Boletos +task.reward.tickets.other.250=+25 Boletos +task.reward.tickets.other.1000=+100 Boletos +task.reward.tickets.other.2000=+100 Boletos +task.reward.tickets.other.5000=+250 Boletos +task.reward.tickets.other.10000=+500 Boletos +task.reward.tickets.other.500=+100 Boletos +task.reward.tickets.other.50000=+2500 Boletos +task.reward.tickets.other.150000=+7500 Boletos +task.reward.tickets.other.500000=+25000 Boletos + +# Date/Time formatting +dateTime.at=a las + +# Telegram Bot Messages +bot.button.startSpinning=Empezar a jugar +bot.button.startSpinningInline=↘️ EMPEZAR A JUGAR ↙️ +bot.button.usersPayouts=Pagos de usuarios +bot.button.infoChannel=Canal de información +bot.button.openChannel=Abrir canal +bot.button.goToChannel=Ir al canal +bot.welcome.firstMessage=¡Prueba tu suerte con Win Spin! +bot.welcome.message=Comienza a apostar, gana y retira directamente a tu billetera.\n\n👉 Para comenzar, mira el video de arriba. +bot.message.startSpinning=Usa este botón para abrir la aplicación Win Spin: +bot.message.usersPayouts=🔒 Actualizaciones en tiempo real.\n\nTodas las retiradas se muestran en el canal. Puedes verificar todo tú mismo: +bot.message.infoChannel=📢 Mantente informado con todas las últimas actualizaciones del proyecto — aquí mismo en nuestro canal +bot.message.paySupport=¡Hola! Para crear una solicitud de soporte sobre tu pago, por favor contacta a @winspinpaysupport.\n\nNota: Los reembolsos solo se otorgan si los boletos comprados aún no han sido utilizados en rondas de juego. +bot.message.unrecognized=No hemos podido reconocer eso. Por favor, usa uno de los botones de abajo. + diff --git a/src/main/resources/messages_fr.properties b/src/main/resources/messages_fr.properties new file mode 100644 index 0000000..3797def --- /dev/null +++ b/src/main/resources/messages_fr.properties @@ -0,0 +1,144 @@ +# French messages +common.error.unknown=Une erreur inattendue s'est produite +common.error.validation=Erreur de validation +common.success=Succès + +feature.depositsUnavailable=Les dépôts sont temporairement indisponibles. Nous nous excusons pour la gêne occasionnée. Veuillez réessayer plus tard. +feature.payoutsUnavailable=Les retraits sont temporairement indisponibles. Nous nous excusons pour la gêne occasionnée. Veuillez réessayer plus tard. + +auth.error.invalid=Authentification invalide +auth.error.expired=Session expirée +auth.error.required=Authentification requise +auth.error.accessRestricted=Accès à l'application restreint. + +user.error.notFound=Utilisateur introuvable +user.error.banned=L'utilisateur est banni +user.language.updated=Langue mise à jour avec succès + +game.error.roomNotFound=Salle introuvable. Veuillez réessayer. +game.error.invalidBetAmount=Le montant du pari doit être entre {0} et {1} +game.error.insufficientBalance=Solde insuffisant +game.error.rateLimit=Trop de demandes. Veuillez attendre un moment avant de réessayer. +game.error.roundNotActive=Le round n'est pas actif +game.error.maxBetExceeded=Vous avez dépassé la limite maximale de pari de {0} pour cette salle. Votre pari total actuel est {1}, vous pouvez donc parier jusqu'à {2} de plus. +game.error.betMustBePositive=Le montant du pari doit être un entier positif. +game.error.roomNumberInvalid=Le numéro de salle doit être entre 1 et 3. + +task.error.notFound=Tâche introuvable +task.error.notCompleted=Tâche non terminée! +task.error.alreadyClaimed=Tâche déjà réclamée +task.success.claimed=Tâche réclamée avec succès +task.claimed=Réclamé + +transaction.error.notFound=Transaction introuvable + +payout.error.withdrawalsRestrictedForAccount=Nous sommes désolés, mais les retraits ont été restreints pour votre compte. Veuillez contacter le support. +payout.error.depositRequiredToWithdraw=En raison de l'augmentation des fraudes, au moins un dépôt est requis pour créer une demande de retrait. +payout.error.withdrawExceedsWinAfterDeposit=En raison de l'augmentation des fraudes, les demandes de retrait ne peuvent pas dépasser vos gains totaux depuis votre dernier dépôt. Vous pouvez actuellement retirer un maximum de {0} tickets. +payout.error.invalidAmount=Montant de retrait invalide +payout.error.minimumNotMet=Montant minimum de retrait non atteint +payout.error.insufficientBalance=Solde insuffisant pour le retrait +payout.error.withdrawalInProgress=Vous avez déjà un retrait en cours. Veuillez attendre qu'il soit terminé. +payout.success.requested=Demande de retrait envoyée avec succès + +withdraw.error.walletInvalidFormat=Le format de l'adresse du portefeuille est incorrect. Veuillez vérifier et réessayer. +withdraw.error.tryLater=Un problème est survenu avec les retraits pour le moment. Veuillez réessayer un peu plus tard. + +payment.error.invalidAmount=Montant de paiement invalide +payment.error.minStars=Le montant doit être d'au moins {0} +payment.error.maxStars=Le montant ne peut pas dépasser {0} +payment.error.usdMaxTwoDecimals=Le montant en USD doit avoir au plus 2 décimales (ex. 2,45). +payment.error.legacyNotSupported=Ce mode de paiement n'est plus pris en charge. Veuillez utiliser les méthodes disponibles pour acheter des billets. +payment.error.failed=Paiement échoué. Veuillez réessayer. +payment.error.statusUnknown=Statut de paiement inconnu. Veuillez vérifier votre solde. +payment.success.purchased={0} tickets achetés avec succès! + +support.error.ticketNotFound=Ticket introuvable +support.error.messageFailed=Impossible d'envoyer le message +support.error.closeFailed=Impossible de fermer le ticket +support.error.rateLimitWait=Veuillez attendre {0} seconde(s) avant d'envoyer un autre message. + +validation.error.required={0} est requis +validation.error.positive={0} doit être positif +validation.error.range={0} doit être entre {1} et {2} + +# Game Room (additional messages) +game.error.roundNotFound=Round de jeu introuvable. Veuillez réessayer. +game.error.participantNotFound=Participant introuvable. Veuillez réessayer. +game.error.rateLimitWait=Veuillez attendre avant de placer un autre pari. Limite de débit: 1 pari par seconde. +game.error.roomNotJoinable=La salle n'est pas accessible en ce moment +game.error.invalidRequest=Demande invalide. Le corps de la demande est requis. + +# User Service +user.error.referralLevelInvalid=Niveau de parrainage invalide: {0}. Doit être 1, 2 ou 3. +user.error.depositAmountInvalid=Le montant du dépôt doit être positif +user.error.balanceNotFound=Solde utilisateur introuvable + +# Task Controller +task.message.claimed=Tâche réclamée avec succès +task.message.notCompleted=Tâche non terminée! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Invitez 1 ami +task.title.inviteFriends.3=Invitez 3 amis +task.title.inviteFriends.7=Invitez 7 amis +task.title.inviteFriends.15=Invitez 15 amis +task.title.inviteFriends.30=Invitez 30 amis +task.title.inviteFriends.50=Invitez 50 amis +task.title.inviteFriends.100=Invitez 100 amis +task.title.followChannel=Suivez notre chaîne d'actualités +task.title.followChannelWithdrawals=Suivez notre chaîne de preuves de paiement +task.title.dailyBonus=Bonus quotidien +task.title.deposit=Déposez {0} billets +task.description.inviteFriends.1=Invitez 1 ami en utilisant votre lien de parrainage unique +task.description.inviteFriends.3=Invitez 3 amis en utilisant votre lien de parrainage unique +task.description.inviteFriends.7=Invitez 7 amis en utilisant votre lien de parrainage unique +task.description.inviteFriends.15=Invitez 15 amis en utilisant votre lien de parrainage unique +task.description.inviteFriends.30=Invitez 30 amis en utilisant votre lien de parrainage unique +task.description.inviteFriends.50=Invitez 50 amis en utilisant votre lien de parrainage unique +task.description.inviteFriends.100=Invitez 100 amis en utilisant votre lien de parrainage unique +task.description.followChannel=Suivez notre chaîne d'actualités +task.description.followChannelWithdrawals=Suivez notre chaîne de preuves de paiement +task.description.dailyBonus=Réclamez votre billet gratuit quotidien! +task.description.deposit=Déposez {0} billets sur votre compte + +# Task Reward Text +task.reward.tickets.1=+5 Billets +task.reward.tickets.3=+15 Billets +task.reward.tickets.7=+35 Billets +task.reward.tickets.15=+75 Billets +task.reward.tickets.30=+110 Billets +task.reward.tickets.50=+150 Billets +task.reward.tickets.100=+375 Billets +task.reward.tickets.follow=+7 Billets +task.reward.tickets.daily=+1 Billet +task.reward.tickets.other.50=+5 Billets +task.reward.tickets.other.250=+25 Billets +task.reward.tickets.other.1000=+100 Billets +task.reward.tickets.other.2000=+100 Billets +task.reward.tickets.other.5000=+250 Billets +task.reward.tickets.other.10000=+500 Billets +task.reward.tickets.other.500=+100 Billets +task.reward.tickets.other.50000=+2500 Billets +task.reward.tickets.other.150000=+7500 Billets +task.reward.tickets.other.500000=+25000 Billets + +# Date/Time formatting +dateTime.at=à + +# Telegram Bot Messages +bot.button.startSpinning=Commencer à jouer +bot.button.startSpinningInline=↘️ COMMENCER À JOUER ↙️ +bot.button.usersPayouts=Paiements des utilisateurs +bot.button.infoChannel=Canal d'information +bot.button.openChannel=Ouvrir le canal +bot.button.goToChannel=Aller au canal +bot.welcome.firstMessage=Essayez votre chance avec Win Spin! +bot.welcome.message=Commencez à parier, gagnez et retirez directement vers votre portefeuille.\n\n👉 Pour commencer, regardez la vidéo ci-dessus. +bot.message.startSpinning=Utilisez ce bouton pour ouvrir l'application Win Spin: +bot.message.usersPayouts=🔒 Mises à jour en temps réel.\n\nTous les retraits sont affichés dans le canal. Vous pouvez tout vérifier vous-même: +bot.message.infoChannel=📢 Restez informé de toutes les dernières mises à jour du projet — ici même sur notre canal +bot.message.paySupport=Bonjour! Pour créer une demande de support concernant votre paiement, veuillez contacter @winspinpaysupport.\n\nVeuillez noter: Les remboursements ne sont accordés que si les billets achetés n'ont pas encore été utilisés dans les rounds de jeu. +bot.message.unrecognized=Nous n'avons pas pu reconnaître cela. Veuillez utiliser l'un des boutons ci-dessous. + diff --git a/src/main/resources/messages_id.properties b/src/main/resources/messages_id.properties new file mode 100644 index 0000000..3b335bf --- /dev/null +++ b/src/main/resources/messages_id.properties @@ -0,0 +1,144 @@ +# Indonesian messages +common.error.unknown=Terjadi kesalahan yang tidak terduga +common.error.validation=Kesalahan validasi +common.success=Berhasil + +feature.depositsUnavailable=Setoran untuk sementara tidak tersedia. Kami minta maaf atas ketidaknyamanannya. Silakan coba lagi nanti. +feature.payoutsUnavailable=Penarikan untuk sementara tidak tersedia. Kami minta maaf atas ketidaknyamanannya. Silakan coba lagi nanti. + +auth.error.invalid=Autentikasi tidak valid +auth.error.expired=Sesi telah berakhir +auth.error.required=Autentikasi diperlukan +auth.error.accessRestricted=Akses aplikasi dibatasi. + +user.error.notFound=Pengguna tidak ditemukan +user.error.banned=Pengguna dilarang +user.language.updated=Bahasa berhasil diperbarui + +game.error.roomNotFound=Ruangan tidak ditemukan. Silakan coba lagi. +game.error.invalidBetAmount=Jumlah taruhan harus antara {0} dan {1} +game.error.insufficientBalance=Saldo tidak mencukupi +game.error.rateLimit=Terlalu banyak permintaan. Silakan tunggu sebentar sebelum mencoba lagi. +game.error.roundNotActive=Putaran tidak aktif +game.error.maxBetExceeded=Anda telah melebihi batas taruhan maksimum {0} untuk ruangan ini. Total taruhan Anda saat ini adalah {1}, jadi Anda dapat bertaruh hingga {2} lagi. +game.error.betMustBePositive=Jumlah taruhan harus berupa bilangan bulat positif. +game.error.roomNumberInvalid=Nomor ruangan harus antara 1 dan 3. + +task.error.notFound=Tugas tidak ditemukan +task.error.notCompleted=Tugas tidak selesai! +task.error.alreadyClaimed=Tugas sudah diklaim +task.success.claimed=Tugas berhasil diklaim +task.claimed=Diklaim + +transaction.error.notFound=Transaksi tidak ditemukan + +payout.error.withdrawalsRestrictedForAccount=Maaf, penarikan telah dibatasi untuk akun Anda. Silakan hubungi Dukungan. +payout.error.depositRequiredToWithdraw=Karena meningkatnya penipuan, setidaknya satu setoran diperlukan untuk membuat permintaan penarikan. +payout.error.withdrawExceedsWinAfterDeposit=Karena meningkatnya penipuan, permintaan penarikan tidak boleh melebihi total kemenangan Anda sejak setoran terakhir. Saat ini Anda dapat menarik maksimal {0} tiket. +payout.error.invalidAmount=Jumlah penarikan tidak valid +payout.error.minimumNotMet=Jumlah penarikan minimum tidak tercapai +payout.error.insufficientBalance=Saldo tidak mencukupi untuk penarikan +payout.error.withdrawalInProgress=Anda sudah memiliki penarikan yang sedang diproses. Harap tunggu sampai selesai. +payout.success.requested=Permintaan penarikan berhasil dikirim + +withdraw.error.walletInvalidFormat=Format alamat dompet tidak benar. Silakan periksa dan coba lagi. +withdraw.error.tryLater=Ada masalah dengan penarikan saat ini. Silakan coba lagi nanti. + +payment.error.invalidAmount=Jumlah pembayaran tidak valid +payment.error.minStars=Jumlah harus setidaknya {0} +payment.error.maxStars=Jumlah tidak boleh melebihi {0} +payment.error.usdMaxTwoDecimals=Jumlah USD harus memiliki paling banyak 2 desimal (mis. 2,45). +payment.error.legacyNotSupported=Metode pembayaran ini tidak lagi didukung. Gunakan metode yang tersedia untuk membeli tiket. +payment.error.failed=Pembayaran gagal. Silakan coba lagi. +payment.error.statusUnknown=Status pembayaran tidak diketahui. Silakan periksa saldo Anda. +payment.success.purchased=Berhasil membeli {0} tiket! + +support.error.ticketNotFound=Tiket tidak ditemukan +support.error.messageFailed=Gagal mengirim pesan +support.error.closeFailed=Gagal menutup tiket +support.error.rateLimitWait=Harap tunggu {0} detik sebelum mengirim pesan lain. + +validation.error.required={0} diperlukan +validation.error.positive={0} harus positif +validation.error.range={0} harus antara {1} dan {2} + +# Game Room (additional messages) +game.error.roundNotFound=Putaran permainan tidak ditemukan. Silakan coba lagi. +game.error.participantNotFound=Peserta tidak ditemukan. Silakan coba lagi. +game.error.rateLimitWait=Silakan tunggu sebelum menempatkan taruhan lain. Batas kecepatan: 1 taruhan per detik. +game.error.roomNotJoinable=Ruangan tidak dapat diakses saat ini +game.error.invalidRequest=Permintaan tidak valid. Badan permintaan diperlukan. + +# User Service +user.error.referralLevelInvalid=Level referral tidak valid: {0}. Harus 1, 2, atau 3. +user.error.depositAmountInvalid=Jumlah deposit harus positif +user.error.balanceNotFound=Saldo pengguna tidak ditemukan + +# Task Controller +task.message.claimed=Tugas berhasil diklaim +task.message.notCompleted=Tugas tidak selesai! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Undang 1 teman +task.title.inviteFriends.3=Undang 3 teman +task.title.inviteFriends.7=Undang 7 teman +task.title.inviteFriends.15=Undang 15 teman +task.title.inviteFriends.30=Undang 30 teman +task.title.inviteFriends.50=Undang 50 teman +task.title.inviteFriends.100=Undang 100 teman +task.title.followChannel=Ikuti saluran berita kami +task.title.followChannelWithdrawals=Ikuti saluran bukti pembayaran kami +task.title.dailyBonus=Bonus harian +task.title.deposit=Setor {0} tiket +task.description.inviteFriends.1=Undang 1 teman menggunakan tautan referral unik Anda +task.description.inviteFriends.3=Undang 3 teman menggunakan tautan referral unik Anda +task.description.inviteFriends.7=Undang 7 teman menggunakan tautan referral unik Anda +task.description.inviteFriends.15=Undang 15 teman menggunakan tautan referral unik Anda +task.description.inviteFriends.30=Undang 30 teman menggunakan tautan referral unik Anda +task.description.inviteFriends.50=Undang 50 teman menggunakan tautan referral unik Anda +task.description.inviteFriends.100=Undang 100 teman menggunakan tautan referral unik Anda +task.description.followChannel=Ikuti saluran berita kami +task.description.followChannelWithdrawals=Ikuti saluran bukti pembayaran kami +task.description.dailyBonus=Klaim tiket gratis harian Anda! +task.description.deposit=Setor {0} tiket ke akun Anda + +# Task Reward Text +task.reward.tickets.1=+5 Tiket +task.reward.tickets.3=+15 Tiket +task.reward.tickets.7=+35 Tiket +task.reward.tickets.15=+75 Tiket +task.reward.tickets.30=+110 Tiket +task.reward.tickets.50=+150 Tiket +task.reward.tickets.100=+375 Tiket +task.reward.tickets.follow=+7 Tiket +task.reward.tickets.daily=+1 Tiket +task.reward.tickets.other.50=+5 Tiket +task.reward.tickets.other.250=+25 Tiket +task.reward.tickets.other.1000=+100 Tiket +task.reward.tickets.other.2000=+100 Tiket +task.reward.tickets.other.5000=+250 Tiket +task.reward.tickets.other.10000=+500 Tiket +task.reward.tickets.other.500=+100 Tiket +task.reward.tickets.other.50000=+2500 Tiket +task.reward.tickets.other.150000=+7500 Tiket +task.reward.tickets.other.500000=+25000 Tiket + +# Date/Time formatting +dateTime.at=pada + +# Telegram Bot Messages +bot.button.startSpinning=Mulai Bermain +bot.button.startSpinningInline=↘️ MULAI BERMAIN ↙️ +bot.button.usersPayouts=Pembayaran pengguna +bot.button.infoChannel=Saluran informasi +bot.button.openChannel=Buka saluran +bot.button.goToChannel=Pergi ke saluran +bot.welcome.firstMessage=Coba keberuntungan Anda dengan Win Spin! +bot.welcome.message=Mulai bertaruh, menang, dan tarik langsung ke dompet Anda.\n\n👉 Untuk memulai, tonton video di atas. +bot.message.startSpinning=Gunakan tombol ini untuk membuka aplikasi Win Spin: +bot.message.usersPayouts=🔒 Pembaruan waktu nyata.\n\nSemua penarikan ditampilkan di saluran. Anda dapat memeriksa semuanya sendiri: +bot.message.infoChannel=📢 Tetap terinformasi dengan semua pembaruan proyek terbaru — di sini di saluran kami +bot.message.paySupport=Halo! Untuk membuat permintaan dukungan terkait pembayaran Anda, silakan hubungi @winspinpaysupport.\n\nHarap dicatat: Pengembalian dana hanya diberikan jika tiket yang dibeli belum digunakan dalam putaran permainan. +bot.message.unrecognized=Kami tidak dapat mengenali itu. Silakan gunakan salah satu tombol di bawah. + diff --git a/src/main/resources/messages_it.properties b/src/main/resources/messages_it.properties new file mode 100644 index 0000000..700fde5 --- /dev/null +++ b/src/main/resources/messages_it.properties @@ -0,0 +1,144 @@ +# Italian messages +common.error.unknown=Si è verificato un errore imprevisto +common.error.validation=Errore di validazione +common.success=Successo + +feature.depositsUnavailable=I depositi sono temporaneamente non disponibili. Ci scusiamo per il disagio. Si prega di riprovare più tardi. +feature.payoutsUnavailable=I prelievi sono temporaneamente non disponibili. Ci scusiamo per il disagio. Si prega di riprovare più tardi. + +auth.error.invalid=Autenticazione non valida +auth.error.expired=Sessione scaduta +auth.error.required=Autenticazione richiesta +auth.error.accessRestricted=Accesso all'applicazione limitato. + +user.error.notFound=Utente non trovato +user.error.banned=L'utente è stato bannato +user.language.updated=Lingua aggiornata con successo + +game.error.roomNotFound=Stanza non trovata. Si prega di riprovare. +game.error.invalidBetAmount=L'importo della scommessa deve essere compreso tra {0} e {1} +game.error.insufficientBalance=Saldo insufficiente +game.error.rateLimit=Troppe richieste. Si prega di attendere un momento prima di riprovare. +game.error.roundNotActive=Il round non è attivo +game.error.maxBetExceeded=Hai superato il limite massimo di scommessa di {0} per questa stanza. La tua scommessa totale attuale è {1}, quindi puoi scommettere fino a {2} in più. +game.error.betMustBePositive=L'importo della scommessa deve essere un numero intero positivo. +game.error.roomNumberInvalid=Il numero della stanza deve essere compreso tra 1 e 3. + +task.error.notFound=Attività non trovata +task.error.notCompleted=Attività non completata! +task.error.alreadyClaimed=Attività già ottenuta +task.success.claimed=Attività ottenuta con successo +task.claimed=Ottenuto + +transaction.error.notFound=Transazione non trovata + +payout.error.withdrawalsRestrictedForAccount=Ci scusiamo, ma i prelievi sono stati limitati per il tuo account. Contatta l'Assistenza. +payout.error.depositRequiredToWithdraw=A causa dell'aumento delle frodi, è necessario effettuare almeno un deposito per creare una richiesta di prelievo. +payout.error.withdrawExceedsWinAfterDeposit=A causa dell'aumento delle frodi, le richieste di prelievo non possono superare le vincite totali dall'ultimo deposito. Attualmente puoi prelevare un massimo di {0} biglietti. +payout.error.invalidAmount=Importo di prelievo non valido +payout.error.minimumNotMet=Importo minimo di prelievo non raggiunto +payout.error.insufficientBalance=Saldo insufficiente per il prelievo +payout.error.withdrawalInProgress=Hai già un prelievo in corso. Attendi che venga completato. +payout.success.requested=Richiesta di prelievo inviata con successo + +withdraw.error.walletInvalidFormat=Il formato dell'indirizzo del portafoglio non è corretto. Controlla e riprova. +withdraw.error.tryLater=C'è un problema con i prelievi al momento. Riprova tra poco. + +payment.error.invalidAmount=Importo di pagamento non valido +payment.error.minStars=L'importo deve essere almeno {0} +payment.error.maxStars=L'importo non può superare {0} +payment.error.usdMaxTwoDecimals=L'importo in USD deve avere al massimo 2 decimali (es. 2,45). +payment.error.legacyNotSupported=Questo metodo di pagamento non è più supportato. Usa i metodi disponibili per acquistare biglietti. +payment.error.failed=Pagamento fallito. Si prega di riprovare. +payment.error.statusUnknown=Stato del pagamento sconosciuto. Si prega di controllare il saldo. +payment.success.purchased=Acquistati con successo {0} biglietti! + +support.error.ticketNotFound=Biglietto non trovato +support.error.messageFailed=Impossibile inviare il messaggio +support.error.closeFailed=Impossibile chiudere il biglietto +support.error.rateLimitWait=Attendere {0} secondo/i prima di inviare un altro messaggio. + +validation.error.required={0} è richiesto +validation.error.positive={0} deve essere positivo +validation.error.range={0} deve essere compreso tra {1} e {2} + +# Game Room (additional messages) +game.error.roundNotFound=Round di gioco non trovato. Si prega di riprovare. +game.error.participantNotFound=Partecipante non trovato. Si prega di riprovare. +game.error.rateLimitWait=Si prega di attendere prima di piazzare un'altra scommessa. Limite di frequenza: 1 scommessa al secondo. +game.error.roomNotJoinable=La stanza non è accessibile in questo momento +game.error.invalidRequest=Richiesta non valida. Il corpo della richiesta è richiesto. + +# User Service +user.error.referralLevelInvalid=Livello di referral non valido: {0}. Deve essere 1, 2 o 3. +user.error.depositAmountInvalid=L'importo del deposito deve essere positivo +user.error.balanceNotFound=Saldo utente non trovato + +# Task Controller +task.message.claimed=Attività ottenuta con successo +task.message.notCompleted=Attività non completata! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Invita 1 amico +task.title.inviteFriends.3=Invita 3 amici +task.title.inviteFriends.7=Invita 7 amici +task.title.inviteFriends.15=Invita 15 amici +task.title.inviteFriends.30=Invita 30 amici +task.title.inviteFriends.50=Invita 50 amici +task.title.inviteFriends.100=Invita 100 amici +task.title.followChannel=Seguici sul nostro canale notizie +task.title.followChannelWithdrawals=Seguici sul canale delle prove di pagamento +task.title.dailyBonus=Bonus giornaliero +task.title.deposit=Deposita {0} biglietti +task.description.inviteFriends.1=Invita 1 amico usando il tuo link di referral unico +task.description.inviteFriends.3=Invita 3 amici usando il tuo link di referral unico +task.description.inviteFriends.7=Invita 7 amici usando il tuo link di referral unico +task.description.inviteFriends.15=Invita 15 amici usando il tuo link di referral unico +task.description.inviteFriends.30=Invita 30 amici usando il tuo link di referral unico +task.description.inviteFriends.50=Invita 50 amici usando il tuo link di referral unico +task.description.inviteFriends.100=Invita 100 amici usando il tuo link di referral unico +task.description.followChannel=Seguici sul nostro canale notizie +task.description.followChannelWithdrawals=Seguici sul canale delle prove di pagamento +task.description.dailyBonus=Reclama il tuo biglietto gratuito giornaliero! +task.description.deposit=Deposita {0} biglietti sul tuo account + +# Task Reward Text +task.reward.tickets.1=+5 Biglietti +task.reward.tickets.3=+15 Biglietti +task.reward.tickets.7=+35 Biglietti +task.reward.tickets.15=+75 Biglietti +task.reward.tickets.30=+110 Biglietti +task.reward.tickets.50=+150 Biglietti +task.reward.tickets.100=+375 Biglietti +task.reward.tickets.follow=+7 Biglietti +task.reward.tickets.daily=+1 Biglietto +task.reward.tickets.other.50=+5 Biglietti +task.reward.tickets.other.250=+25 Biglietti +task.reward.tickets.other.1000=+100 Biglietti +task.reward.tickets.other.2000=+100 Biglietti +task.reward.tickets.other.5000=+250 Biglietti +task.reward.tickets.other.10000=+500 Biglietti +task.reward.tickets.other.500=+100 Biglietti +task.reward.tickets.other.50000=+2500 Biglietti +task.reward.tickets.other.150000=+7500 Biglietti +task.reward.tickets.other.500000=+25000 Biglietti + +# Date/Time formatting +dateTime.at=alle + +# Telegram Bot Messages +bot.button.startSpinning=Inizia a giocare +bot.button.startSpinningInline=↘️ INIZIA A GIOCARE ↙️ +bot.button.usersPayouts=Pagamenti degli utenti +bot.button.infoChannel=Canale informazioni +bot.button.openChannel=Apri canale +bot.button.goToChannel=Vai al canale +bot.welcome.firstMessage=Prova la tua fortuna con Win Spin! +bot.welcome.message=Inizia a scommettere, vinci e preleva direttamente sul tuo portafoglio.\n\n👉 Per iniziare, guarda il video sopra. +bot.message.startSpinning=Usa questo pulsante per aprire l'app Win Spin: +bot.message.usersPayouts=🔒 Aggiornamenti in tempo reale.\n\nTutti i prelievi sono mostrati nel canale. Puoi controllare tutto da solo: +bot.message.infoChannel=📢 Rimani informato su tutti gli ultimi aggiornamenti del progetto — proprio qui nel nostro canale +bot.message.paySupport=Ciao! Per creare una richiesta di supporto riguardo al tuo pagamento, contatta @winspinpaysupport.\n\nNota: I rimborsi vengono concessi solo se i biglietti acquistati non sono ancora stati utilizzati nei round di gioco. +bot.message.unrecognized=Non abbiamo riconosciuto il messaggio. Usa uno dei pulsanti qui sotto. + diff --git a/src/main/resources/messages_nl.properties b/src/main/resources/messages_nl.properties new file mode 100644 index 0000000..8bea47c --- /dev/null +++ b/src/main/resources/messages_nl.properties @@ -0,0 +1,144 @@ +# Dutch messages +common.error.unknown=Er is een onverwachte fout opgetreden +common.error.validation=Validatiefout +common.success=Succes + +feature.depositsUnavailable=Stortingen zijn tijdelijk niet beschikbaar. Onze excuses voor het ongemak. Probeer het later opnieuw. +feature.payoutsUnavailable=Opnames zijn tijdelijk niet beschikbaar. Onze excuses voor het ongemak. Probeer het later opnieuw. + +auth.error.invalid=Ongeldige authenticatie +auth.error.expired=Sessie verlopen +auth.error.required=Authenticatie vereist +auth.error.accessRestricted=Toegang tot de applicatie beperkt. + +user.error.notFound=Gebruiker niet gevonden +user.error.banned=Gebruiker is geblokkeerd +user.language.updated=Taal succesvol bijgewerkt + +game.error.roomNotFound=Ruimte niet gevonden. Probeer het opnieuw. +game.error.invalidBetAmount=Inzetbedrag moet tussen {0} en {1} liggen +game.error.insufficientBalance=Onvoldoende saldo +game.error.rateLimit=Te veel verzoeken. Wacht even voordat u het opnieuw probeert. +game.error.roundNotActive=Ronde is niet actief +game.error.maxBetExceeded=U heeft de maximale inzetlimiet van {0} voor deze ruimte overschreden. Uw huidige totale inzet is {1}, dus u kunt nog tot {2} meer inzetten. +game.error.betMustBePositive=Inzetbedrag moet een positief geheel getal zijn. +game.error.roomNumberInvalid=Kamernummer moet tussen 1 en 3 liggen. + +task.error.notFound=Taak niet gevonden +task.error.notCompleted=Taak niet voltooid! +task.error.alreadyClaimed=Taak al ontvangen +task.success.claimed=Taak succesvol ontvangen +task.claimed=Ontvangen + +transaction.error.notFound=Transactie niet gevonden + +payout.error.withdrawalsRestrictedForAccount=Het spijt ons, maar opnames zijn beperkt voor uw account. Neem contact op met de klantenservice. +payout.error.depositRequiredToWithdraw=Vanwege toegenomen fraude is minstens één storting vereist om een opnameverzoek aan te maken. +payout.error.withdrawExceedsWinAfterDeposit=Vanwege toegenomen fraude mogen opnameverzoeken uw totale winsten sinds uw laatste storting niet overschrijden. U kunt momenteel maximaal {0} tickets opnemen. +payout.error.invalidAmount=Ongeldig uitbetalingsbedrag +payout.error.minimumNotMet=Minimum uitbetalingsbedrag niet bereikt +payout.error.insufficientBalance=Onvoldoende saldo voor uitbetaling +payout.error.withdrawalInProgress=U heeft al een opname in behandeling. Wacht tot deze is voltooid. +payout.success.requested=Uitbetalingsverzoek succesvol verzonden + +withdraw.error.walletInvalidFormat=Het formaat van het walletadres is onjuist. Controleer en probeer het opnieuw. +withdraw.error.tryLater=Er is momenteel een probleem met opnames. Probeer het later opnieuw. + +payment.error.invalidAmount=Ongeldig betalingsbedrag +payment.error.minStars=Het bedrag moet minimaal {0} zijn +payment.error.maxStars=Het bedrag mag {0} niet overschrijden +payment.error.usdMaxTwoDecimals=Het USD-bedrag mag maximaal 2 decimalen hebben (bijv. 2,45). +payment.error.legacyNotSupported=Deze betaalmethode wordt niet meer ondersteund. Gebruik de beschikbare methoden om tickets te kopen. +payment.error.failed=Betaling mislukt. Probeer het opnieuw. +payment.error.statusUnknown=Betalingsstatus onbekend. Controleer uw saldo. +payment.success.purchased=Succesvol {0} tickets gekocht! + +support.error.ticketNotFound=Ticket niet gevonden +support.error.messageFailed=Bericht kon niet worden verzonden +support.error.closeFailed=Ticket kon niet worden gesloten +support.error.rateLimitWait=Wacht {0} seconde(n) voordat u een ander bericht verzendt. + +validation.error.required={0} is vereist +validation.error.positive={0} moet positief zijn +validation.error.range={0} moet tussen {1} en {2} liggen + +# Game Room (additional messages) +game.error.roundNotFound=Spelronde niet gevonden. Probeer het opnieuw. +game.error.participantNotFound=Deelnemer niet gevonden. Probeer het opnieuw. +game.error.rateLimitWait=Wacht even voordat u een nieuwe inzet plaatst. Snelheidslimiet: 1 inzet per seconde. +game.error.roomNotJoinable=Ruimte is op dit moment niet toegankelijk +game.error.invalidRequest=Ongeldig verzoek. Verzoekbody is vereist. + +# User Service +user.error.referralLevelInvalid=Ongeldig verwijzingsniveau: {0}. Moet 1, 2 of 3 zijn. +user.error.depositAmountInvalid=Stortingsbedrag moet positief zijn +user.error.balanceNotFound=Gebruikerssaldo niet gevonden + +# Task Controller +task.message.claimed=Taak succesvol ontvangen +task.message.notCompleted=Taak niet voltooid! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Nodig 1 vriend uit +task.title.inviteFriends.3=Nodig 3 vrienden uit +task.title.inviteFriends.7=Nodig 7 vrienden uit +task.title.inviteFriends.15=Nodig 15 vrienden uit +task.title.inviteFriends.30=Nodig 30 vrienden uit +task.title.inviteFriends.50=Nodig 50 vrienden uit +task.title.inviteFriends.100=Nodig 100 vrienden uit +task.title.followChannel=Volg ons nieuwskanaal +task.title.followChannelWithdrawals=Volg ons kanaal voor betalingsbewijzen +task.title.dailyBonus=Dagelijkse bonus +task.title.deposit=Stort {0} tickets +task.description.inviteFriends.1=Nodig 1 vriend uit met je unieke verwijzingslink +task.description.inviteFriends.3=Nodig 3 vrienden uit met je unieke verwijzingslink +task.description.inviteFriends.7=Nodig 7 vrienden uit met je unieke verwijzingslink +task.description.inviteFriends.15=Nodig 15 vrienden uit met je unieke verwijzingslink +task.description.inviteFriends.30=Nodig 30 vrienden uit met je unieke verwijzingslink +task.description.inviteFriends.50=Nodig 50 vrienden uit met je unieke verwijzingslink +task.description.inviteFriends.100=Nodig 100 vrienden uit met je unieke verwijzingslink +task.description.followChannel=Volg ons nieuwskanaal +task.description.followChannelWithdrawals=Volg ons kanaal voor betalingsbewijzen +task.description.dailyBonus=Claim je dagelijkse gratis ticket! +task.description.deposit=Stort {0} tickets op je account + +# Task Reward Text +task.reward.tickets.1=+5 Tickets +task.reward.tickets.3=+15 Tickets +task.reward.tickets.7=+35 Tickets +task.reward.tickets.15=+75 Tickets +task.reward.tickets.30=+110 Tickets +task.reward.tickets.50=+150 Tickets +task.reward.tickets.100=+375 Tickets +task.reward.tickets.follow=+7 Tickets +task.reward.tickets.daily=+1 Ticket +task.reward.tickets.other.50=+5 Tickets +task.reward.tickets.other.250=+25 Tickets +task.reward.tickets.other.1000=+100 Tickets +task.reward.tickets.other.2000=+100 Tickets +task.reward.tickets.other.5000=+250 Tickets +task.reward.tickets.other.10000=+500 Tickets +task.reward.tickets.other.500=+100 Tickets +task.reward.tickets.other.50000=+2500 Tickets +task.reward.tickets.other.150000=+7500 Tickets +task.reward.tickets.other.500000=+25000 Tickets + +# Date/Time formatting +dateTime.at=om + +# Telegram Bot Messages +bot.button.startSpinning=Begin met spelen +bot.button.startSpinningInline=↘️ BEGIN MET SPELEN ↙️ +bot.button.usersPayouts=Gebruikersuitbetalingen +bot.button.infoChannel=Informatiekanaal +bot.button.openChannel=Kanaal openen +bot.button.goToChannel=Ga naar het kanaal +bot.welcome.firstMessage=Probeer uw geluk met Win Spin! +bot.welcome.message=Begin met wedden, win en trek direct op naar je portemonnee.\n\n👉 Om te beginnen, bekijk de video hierboven. +bot.message.startSpinning=Gebruik deze knop om de Win Spin-app te openen: +bot.message.usersPayouts=🔒 Updates in realtime.\n\nAlle opnames worden getoond in het kanaal. U kunt alles zelf controleren: +bot.message.infoChannel=📢 Blijf op de hoogte van alle laatste projectupdates — hier in ons kanaal +bot.message.paySupport=Hallo! Om een ondersteuningsverzoek met betrekking tot uw betaling aan te maken, neem contact op met @winspinpaysupport.\n\nLet op: Restituties worden alleen verleend als de gekochte tickets nog niet zijn gebruikt in spelsessies. +bot.message.unrecognized=We konden dat niet herkennen. Gebruik een van de knoppen hieronder. + diff --git a/src/main/resources/messages_pl.properties b/src/main/resources/messages_pl.properties new file mode 100644 index 0000000..7f2ac51 --- /dev/null +++ b/src/main/resources/messages_pl.properties @@ -0,0 +1,144 @@ +# Polish messages +common.error.unknown=Wystąpił nieoczekiwany błąd +common.error.validation=Błąd walidacji +common.success=Sukces + +feature.depositsUnavailable=Wpłaty są tymczasowo niedostępne. Przepraszamy za niedogodności. Proszę spróbować później. +feature.payoutsUnavailable=Wypłaty są tymczasowo niedostępne. Przepraszamy za niedogodności. Proszę spróbować później. + +auth.error.invalid=Nieprawidłowe uwierzytelnienie +auth.error.expired=Sesja wygasła +auth.error.required=Wymagane uwierzytelnienie +auth.error.accessRestricted=Dostęp do aplikacji ograniczony. + +user.error.notFound=Użytkownik nie znaleziony +user.error.banned=Użytkownik jest zablokowany +user.language.updated=Język został zaktualizowany pomyślnie + +game.error.roomNotFound=Pokój nie znaleziony. Spróbuj ponownie. +game.error.invalidBetAmount=Kwota zakładu musi być między {0} a {1} +game.error.insufficientBalance=Niewystarczające saldo +game.error.rateLimit=Zbyt wiele żądań. Poczekaj chwilę przed ponowną próbą. +game.error.roundNotActive=Runda nie jest aktywna +game.error.maxBetExceeded=Przekroczyłeś maksymalny limit zakładu {0} dla tego pokoju. Twój obecny całkowity zakład to {1}, więc możesz postawić jeszcze do {2}. +game.error.betMustBePositive=Kwota zakładu musi być dodatnią liczbą całkowitą. +game.error.roomNumberInvalid=Numer pokoju musi być między 1 a 3. + +task.error.notFound=Zadanie nie znalezione +task.error.notCompleted=Zadanie nie ukończone! +task.error.alreadyClaimed=Zadanie już odebrane +task.success.claimed=Zadanie pomyślnie odebrane +task.claimed=Odebrane + +transaction.error.notFound=Transakcja nie znaleziona + +payout.error.withdrawalsRestrictedForAccount=Przepraszamy, ale wypłaty zostały ograniczone dla Twojego konta. Skontaktuj się z pomocą techniczną. +payout.error.depositRequiredToWithdraw=Ze względu na wzrost oszustw wymagana jest co najmniej jedna wpłata, aby utworzyć wniosek o wypłatę. +payout.error.withdrawExceedsWinAfterDeposit=Ze względu na wzrost oszustw wnioski o wypłatę nie mogą przekraczać łącznych wygranych od ostatniej wpłaty. Obecnie możesz wypłacić maksymalnie {0} biletów. +payout.error.invalidAmount=Nieprawidłowa kwota wypłaty +payout.error.minimumNotMet=Minimalna kwota wypłaty nie osiągnięta +payout.error.insufficientBalance=Niewystarczające saldo do wypłaty +payout.error.withdrawalInProgress=Masz już wniosek o wypłatę w toku. Poczekaj na jego zakończenie. +payout.success.requested=Wniosek o wypłatę został pomyślnie wysłany + +withdraw.error.walletInvalidFormat=Format adresu portfela jest nieprawidłowy. Sprawdź i spróbuj ponownie. +withdraw.error.tryLater=Wystąpił problem z wypłatami. Spróbuj ponownie za chwilę. + +payment.error.invalidAmount=Nieprawidłowa kwota płatności +payment.error.minStars=Kwota musi wynosić co najmniej {0} +payment.error.maxStars=Kwota nie może przekraczać {0} +payment.error.usdMaxTwoDecimals=Kwota w USD może mieć co najwyżej 2 miejsca po przecinku (np. 2,45). +payment.error.legacyNotSupported=Ta metoda płatności nie jest już obsługiwana. Użyj dostępnych metod do zakupu biletów. +payment.error.failed=Płatność nie powiodła się. Spróbuj ponownie. +payment.error.statusUnknown=Status płatności nieznany. Sprawdź saldo. +payment.success.purchased=Pomyślnie zakupiono {0} biletów! + +support.error.ticketNotFound=Bilet nie znaleziony +support.error.messageFailed=Nie udało się wysłać wiadomości +support.error.closeFailed=Nie udało się zamknąć biletu +support.error.rateLimitWait=Poczekaj {0} sekund(y), zanim wyślesz kolejną wiadomość. + +validation.error.required={0} jest wymagane +validation.error.positive={0} musi być dodatnie +validation.error.range={0} musi być między {1} a {2} + +# Game Room (additional messages) +game.error.roundNotFound=Runda gry nie znaleziona. Spróbuj ponownie. +game.error.participantNotFound=Uczestnik nie znaleziony. Spróbuj ponownie. +game.error.rateLimitWait=Poczekaj przed postawieniem kolejnego zakładu. Limit: 1 zakład na sekundę. +game.error.roomNotJoinable=Pokój nie jest dostępny w tym momencie +game.error.invalidRequest=Nieprawidłowe żądanie. Treść żądania jest wymagana. + +# User Service +user.error.referralLevelInvalid=Nieprawidłowy poziom polecającego: {0}. Musi być 1, 2 lub 3. +user.error.depositAmountInvalid=Kwota wpłaty musi być dodatnia +user.error.balanceNotFound=Saldo użytkownika nie znalezione + +# Task Controller +task.message.claimed=Zadanie pomyślnie odebrane +task.message.notCompleted=Zadanie nie ukończone! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Zaproś 1 znajomego +task.title.inviteFriends.3=Zaproś 3 znajomych +task.title.inviteFriends.7=Zaproś 7 znajomych +task.title.inviteFriends.15=Zaproś 15 znajomych +task.title.inviteFriends.30=Zaproś 30 znajomych +task.title.inviteFriends.50=Zaproś 50 znajomych +task.title.inviteFriends.100=Zaproś 100 znajomych +task.title.followChannel=Śledź nasz kanał informacyjny +task.title.followChannelWithdrawals=Śledź nasz kanał potwierdzeń wypłat +task.title.dailyBonus=Bonus dzienny +task.title.deposit=Wpłać {0} biletów +task.description.inviteFriends.1=Zaproś 1 znajomego używając swojego unikalnego linku polecającego +task.description.inviteFriends.3=Zaproś 3 znajomych używając swojego unikalnego linku polecającego +task.description.inviteFriends.7=Zaproś 7 znajomych używając swojego unikalnego linku polecającego +task.description.inviteFriends.15=Zaproś 15 znajomych używając swojego unikalnego linku polecającego +task.description.inviteFriends.30=Zaproś 30 znajomych używając swojego unikalnego linku polecającego +task.description.inviteFriends.50=Zaproś 50 znajomych używając swojego unikalnego linku polecającego +task.description.inviteFriends.100=Zaproś 100 znajomych używając swojego unikalnego linku polecającego +task.description.followChannel=Śledź nasz kanał informacyjny +task.description.followChannelWithdrawals=Śledź nasz kanał potwierdzeń wypłat +task.description.dailyBonus=Odbierz swój dzienny darmowy bilet! +task.description.deposit=Wpłać {0} biletów na swoje konto + +# Task Reward Text +task.reward.tickets.1=+5 Biletów +task.reward.tickets.3=+15 Biletów +task.reward.tickets.7=+35 Biletów +task.reward.tickets.15=+75 Biletów +task.reward.tickets.30=+110 Biletów +task.reward.tickets.50=+150 Biletów +task.reward.tickets.100=+375 Biletów +task.reward.tickets.follow=+7 Biletów +task.reward.tickets.daily=+1 Bilet +task.reward.tickets.other.50=+5 Biletów +task.reward.tickets.other.250=+25 Biletów +task.reward.tickets.other.1000=+100 Biletów +task.reward.tickets.other.2000=+100 Biletów +task.reward.tickets.other.5000=+250 Biletów +task.reward.tickets.other.10000=+500 Biletów +task.reward.tickets.other.500=+100 Biletów +task.reward.tickets.other.50000=+2500 Biletów +task.reward.tickets.other.150000=+7500 Biletów +task.reward.tickets.other.500000=+25000 Biletów + +# Date/Time formatting +dateTime.at=o + +# Telegram Bot Messages +bot.button.startSpinning=Zacznij grę +bot.button.startSpinningInline=↘️ ZACZNIJ GRĘ ↙️ +bot.button.usersPayouts=Wypłaty użytkowników +bot.button.infoChannel=Kanał informacyjny +bot.button.openChannel=Otwórz kanał +bot.button.goToChannel=Przejdź do kanału +bot.welcome.firstMessage=Spróbuj szczęścia z Win Spin! +bot.welcome.message=Zacznij obstawiać, wygrywaj i wypłacaj prosto na swój portfel.\n\n👉 Aby rozpocząć, obejrzyj wideo powyżej. +bot.message.startSpinning=Użyj tego przycisku, aby otworzyć aplikację Win Spin: +bot.message.usersPayouts=🔒 Aktualizacje w czasie rzeczywistym.\n\nWszystkie wypłaty są pokazane w kanale. Możesz sprawdzić wszystko samodzielnie: +bot.message.infoChannel=📢 Bądź na bieżąco ze wszystkimi najnowszymi aktualizacjami projektu — właśnie tutaj w naszym kanale +bot.message.paySupport=Witaj! Aby utworzyć zgłoszenie dotyczące płatności, skontaktuj się z @winspinpaysupport.\n\nUwaga: Zwroty są przyznawane tylko wtedy, gdy zakupione bilety nie zostały jeszcze wykorzystane w rundach gry. +bot.message.unrecognized=Nie rozpoznaliśmy tego. Użyj jednego z przycisków poniżej. + diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties new file mode 100644 index 0000000..19f19de --- /dev/null +++ b/src/main/resources/messages_ru.properties @@ -0,0 +1,145 @@ +# Russian messages +common.error.unknown=Произошла непредвиденная ошибка +common.error.validation=Ошибка валидации +common.success=Успешно + +feature.depositsUnavailable=Пополнения временно недоступны. Приносим извинения за неудобства. Пожалуйста, попробуйте позже. +feature.payoutsUnavailable=Выводы временно недоступны. Приносим извинения за неудобства. Пожалуйста, попробуйте позже. + +auth.error.invalid=Неверная аутентификация +auth.error.expired=Сессия истекла +auth.error.required=Требуется аутентификация +auth.error.accessRestricted=Доступ к приложению ограничен. + +user.error.notFound=Пользователь не найден +user.error.banned=Пользователь заблокирован +user.language.updated=Язык успешно обновлен + +game.error.roomNotFound=Комната не найдена. Пожалуйста, попробуйте снова. +game.error.invalidBetAmount=Сумма ставки должна быть между {0} и {1} +game.error.insufficientBalance=Недостаточно средств +game.error.rateLimit=Слишком много запросов. Пожалуйста, подождите немного перед повторной попыткой. +game.error.roundNotActive=Раунд не активен +game.error.maxBetExceeded=Вы превысили максимальный лимит ставки {0} для этой комнаты. Ваша текущая общая ставка составляет {1}, поэтому вы можете поставить еще до {2}. +game.error.betMustBePositive=Сумма ставки должна быть положительным целым числом. +game.error.roomNumberInvalid=Номер комнаты должен быть от 1 до 3. + +task.error.notFound=Задача не найдена +task.error.notCompleted=Задача не выполнена! +task.error.alreadyClaimed=Задача уже получена +task.success.claimed=Задача успешно получена +task.claimed=Получено + +transaction.error.notFound=Транзакция не найдена + +payout.error.withdrawalsRestrictedForAccount=Извините, но вывод средств для вашего аккаунта ограничен. Пожалуйста, обратитесь в поддержку. +payout.error.depositRequiredToWithdraw=Из-за участившихся случаев мошенничества для создания запроса на вывод необходимо сделать хотя бы один депозит. +payout.error.withdrawExceedsWinAfterDeposit=Из-за участившихся случаев мошенничества сумма вывода не может превышать ваши выигрыши после последнего депозита. Сейчас вы можете вывести максимум {0} билетов. +payout.error.invalidAmount=Неверная сумма вывода +payout.error.minimumNotMet=Минимальная сумма вывода не достигнута +payout.error.insufficientBalance=Недостаточно средств для вывода +payout.error.withdrawalInProgress=У вас уже есть запрос на вывод. Дождитесь его завершения. +payout.error.withdrawalAmountMaxTwoDecimals=Сумма вывода должна содержать не более 2 знаков после запятой (например, 125.25). Значения вроде 125.125 не поддерживаются. +payout.success.requested=Запрос на вывод успешно отправлен + +withdraw.error.walletInvalidFormat=Неверный формат адреса кошелька. Проверьте и попробуйте снова. +withdraw.error.tryLater=В данный момент возникла проблема с выводами. Попробуйте позже. + +payment.error.invalidAmount=Неверная сумма платежа +payment.error.minStars=Сумма должна быть не менее {0} +payment.error.maxStars=Сумма не может превышать {0} +payment.error.usdMaxTwoDecimals=Сумма в USD должна содержать не более 2 знаков после запятой (например, 2.45). +payment.error.legacyNotSupported=Этот способ оплаты больше не поддерживается. Используйте доступные способы для покупки билетов. +payment.error.failed=Платеж не выполнен. Пожалуйста, попробуйте снова. +payment.error.statusUnknown=Статус платежа неизвестен. Пожалуйста, проверьте ваш баланс. +payment.success.purchased=Успешно приобретено {0} билетов! + +support.error.ticketNotFound=Тикет не найден +support.error.messageFailed=Не удалось отправить сообщение +support.error.closeFailed=Не удалось закрыть тикет +support.error.rateLimitWait=Пожалуйста, подождите {0} сек. перед отправкой следующего сообщения. + +validation.error.required={0} обязательно +validation.error.positive={0} должно быть положительным +validation.error.range={0} должно быть между {1} и {2} + +# Game Room (additional messages) +game.error.roundNotFound=Игровой раунд не найден. Пожалуйста, попробуйте снова. +game.error.participantNotFound=Участник не найден. Пожалуйста, попробуйте снова. +game.error.rateLimitWait=Пожалуйста, подождите перед следующей ставкой. Лимит: 1 ставка в секунду. +game.error.roomNotJoinable=Комната недоступна для присоединения в данный момент +game.error.invalidRequest=Неверный запрос. Тело запроса обязательно. + +# User Service +user.error.referralLevelInvalid=Неверный уровень реферала: {0}. Должен быть 1, 2 или 3. +user.error.depositAmountInvalid=Сумма депозита должна быть положительной +user.error.balanceNotFound=Баланс пользователя не найден + +# Task Controller +task.message.claimed=Задача успешно получена +task.message.notCompleted=Задача не выполнена! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=Пригласите 1 друга +task.title.inviteFriends.3=Пригласите 3 друзей +task.title.inviteFriends.7=Пригласите 7 друзей +task.title.inviteFriends.15=Пригласите 15 друзей +task.title.inviteFriends.30=Пригласите 30 друзей +task.title.inviteFriends.50=Пригласите 50 друзей +task.title.inviteFriends.100=Пригласите 100 друзей +task.title.followChannel=Подпишитесь на наш новостной канал +task.title.followChannelWithdrawals=Подпишитесь на канал подтверждений выплат +task.title.dailyBonus=Ежедневный бонус +task.title.deposit=Пополните {0} билетов +task.description.inviteFriends.1=Пригласите 1 друга, используя вашу уникальную реферальную ссылку +task.description.inviteFriends.3=Пригласите 3 друзей, используя вашу уникальную реферальную ссылку +task.description.inviteFriends.7=Пригласите 7 друзей, используя вашу уникальную реферальную ссылку +task.description.inviteFriends.15=Пригласите 15 друзей, используя вашу уникальную реферальную ссылку +task.description.inviteFriends.30=Пригласите 30 друзей, используя вашу уникальную реферальную ссылку +task.description.inviteFriends.50=Пригласите 50 друзей, используя вашу уникальную реферальную ссылку +task.description.inviteFriends.100=Пригласите 100 друзей, используя вашу уникальную реферальную ссылку +task.description.followChannel=Подпишитесь на наш новостной канал +task.description.followChannelWithdrawals=Подпишитесь на канал подтверждений выплат +task.description.dailyBonus=Получите свой ежедневный бесплатный билет! +task.description.deposit=Пополните {0} билетов на ваш счет + +# Task Reward Text (localized with proper grammar) +task.reward.tickets.1=+5 Билетов +task.reward.tickets.3=+15 Билетов +task.reward.tickets.7=+35 Билетов +task.reward.tickets.15=+75 Билетов +task.reward.tickets.30=+110 Билетов +task.reward.tickets.50=+150 Билетов +task.reward.tickets.100=+375 Билетов +task.reward.tickets.follow=+7 Билетов +task.reward.tickets.daily=+1 Билет +task.reward.tickets.other.50=+5 Билетов +task.reward.tickets.other.250=+25 Билетов +task.reward.tickets.other.1000=+100 Билетов +task.reward.tickets.other.2000=+100 Билетов +task.reward.tickets.other.5000=+250 Билетов +task.reward.tickets.other.10000=+500 Билетов +task.reward.tickets.other.500=+100 Билетов +task.reward.tickets.other.50000=+2500 Билетов +task.reward.tickets.other.150000=+7500 Билетов +task.reward.tickets.other.500000=+25000 Билетов + +# Date/Time formatting +dateTime.at=в + +# Telegram Bot Messages +bot.button.startSpinning=Начать игру +bot.button.startSpinningInline=↘️ НАЧАТЬ ИГРУ ↙️ +bot.button.usersPayouts=Выплаты пользователей +bot.button.infoChannel=Информационный канал +bot.button.openChannel=Открыть канал +bot.button.goToChannel=Перейти в канал +bot.welcome.firstMessage=Попробуйте удачу с Win Spin! +bot.welcome.message=Начните делать ставки, выигрывайте и выводите средства прямо на свой кошелёк.\n\n👉 Чтобы начать, посмотрите видео выше. +bot.message.startSpinning=Используйте эту кнопку, чтобы открыть приложение Win Spin: +bot.message.usersPayouts=🔒 Обновления в реальном времени.\n\nВсе выплаты отображаются в канале. Вы можете проверить все самостоятельно: +bot.message.infoChannel=📢 Будьте в курсе всех последних обновлений проекта — прямо здесь, в нашем канале +bot.message.paySupport=Привет! Чтобы создать запрос в службу поддержки по поводу вашего платежа, пожалуйста, свяжитесь с @winspinpaysupport.\n\nОбратите внимание: Возврат средств предоставляется только в том случае, если купленные билеты еще не были использованы в игровых раундах. +bot.message.unrecognized=Мы не смогли это распознать. Пожалуйста, воспользуйтесь одной из кнопок ниже. + diff --git a/src/main/resources/messages_tr.properties b/src/main/resources/messages_tr.properties new file mode 100644 index 0000000..70d4c5f --- /dev/null +++ b/src/main/resources/messages_tr.properties @@ -0,0 +1,145 @@ +# Turkish messages +common.error.unknown=Beklenmeyen bir hata oluştu +common.error.validation=Doğrulama hatası +common.success=Başarılı + +feature.depositsUnavailable=Yatırımlar geçici olarak kullanılamıyor. Verdiğimiz rahatsızlıktan dolayı özür dileriz. Lütfen daha sonra tekrar deneyin. +feature.payoutsUnavailable=Çekimler geçici olarak kullanılamıyor. Verdiğimiz rahatsızlıktan dolayı özür dileriz. Lütfen daha sonra tekrar deneyin. + +auth.error.invalid=Geçersiz kimlik doğrulama +auth.error.expired=Oturum süresi doldu +auth.error.required=Kimlik doğrulama gerekli +auth.error.accessRestricted=Uygulama erişimi kısıtlandı. + +user.error.notFound=Kullanıcı bulunamadı +user.error.banned=Kullanıcı yasaklandı +user.language.updated=Dil başarıyla güncellendi + +game.error.roomNotFound=Oda bulunamadı. Lütfen tekrar deneyin. +game.error.invalidBetAmount=Bahis tutarı {0} ile {1} arasında olmalıdır +game.error.insufficientBalance=Yetersiz bakiye +game.error.rateLimit=Çok fazla istek. Lütfen tekrar denemeden önce bir süre bekleyin. +game.error.roundNotActive=Tur aktif değil +game.error.maxBetExceeded=Bu oda için maksimum bahis limiti olan {0}'ı aştınız. Mevcut toplam bahsiniz {1}, bu yüzden {2} daha fazla bahis yapabilirsiniz. +game.error.betMustBePositive=Bahis tutarı pozitif bir tam sayı olmalıdır. +game.error.roomNumberInvalid=Oda numarası 1 ile 3 arasında olmalıdır. + +task.error.notFound=Görev bulunamadı +task.error.notCompleted=Görev tamamlanmadı! +task.error.alreadyClaimed=Görev zaten alındı +task.success.claimed=Görev başarıyla alındı +task.claimed=Alındı + +transaction.error.notFound=İşlem bulunamadı + +payout.error.withdrawalsRestrictedForAccount=Üzgünüz, ancak hesabınız için para çekme işlemleri kısıtlandı. Lütfen Destek ile iletişime geçin. +payout.error.depositRequiredToWithdraw=Artan dolandırıcılık nedeniyle para çekme talebi oluşturmak için en az bir yatırım yapmış olmanız gerekir. +payout.error.withdrawExceedsWinAfterDeposit=Artan dolandırıcılık nedeniyle para çekme talepleri son yatırımınızdan bu yana toplam kazançlarınızı aşamaz. Şu anda maksimum {0} bilet çekebilirsiniz. +payout.error.invalidAmount=Geçersiz çekim tutarı +payout.error.minimumNotMet=Minimum çekim tutarına ulaşılmadı +payout.error.insufficientBalance=Çekim için yetersiz bakiye +payout.error.withdrawalInProgress=Zaten devam eden bir çekim talebiniz var. Tamamlanmasını bekleyin. +payout.error.withdrawalAmountMaxTwoDecimals=Çekim tutarı en fazla 2 ondalık basamak içermelidir (örn. 125,25). 125,125 gibi değerler desteklenmez. +payout.success.requested=Çekim talebi başarıyla gönderildi + +withdraw.error.walletInvalidFormat=Cüzdan adresi formatı yanlış. Lütfen kontrol edip tekrar deneyin. +withdraw.error.tryLater=Şu anda çekimlerle ilgili bir sorun var. Lütfen biraz sonra tekrar deneyin. + +payment.error.invalidAmount=Geçersiz ödeme tutarı +payment.error.minStars=Miktar en az {0} olmalıdır +payment.error.maxStars=Miktar {0} değerini aşamaz +payment.error.usdMaxTwoDecimals=USD tutarı en fazla 2 ondalık basamak içermelidir (örn. 2,45). +payment.error.legacyNotSupported=Bu ödeme yöntemi artık desteklenmemektedir. Bilet satın almak için mevcut yöntemleri kullanın. +payment.error.failed=Ödeme başarısız. Lütfen tekrar deneyin. +payment.error.statusUnknown=Ödeme durumu bilinmiyor. Lütfen bakiyenizi kontrol edin. +payment.success.purchased={0} bilet başarıyla satın alındı! + +support.error.ticketNotFound=Bilet bulunamadı +support.error.messageFailed=Mesaj gönderilemedi +support.error.closeFailed=Bilet kapatılamadı +support.error.rateLimitWait=Lütfen başka bir mesaj göndermeden önce {0} saniye bekleyin. + +validation.error.required={0} gereklidir +validation.error.positive={0} pozitif olmalıdır +validation.error.range={0} {1} ile {2} arasında olmalıdır + +# Game Room (additional messages) +game.error.roundNotFound=Oyun turu bulunamadı. Lütfen tekrar deneyin. +game.error.participantNotFound=Katılımcı bulunamadı. Lütfen tekrar deneyin. +game.error.rateLimitWait=Lütfen başka bir bahis yapmadan önce bekleyin. Hız limiti: saniyede 1 bahis. +game.error.roomNotJoinable=Oda şu anda erişilebilir değil +game.error.invalidRequest=Geçersiz istek. İstek gövdesi gereklidir. + +# User Service +user.error.referralLevelInvalid=Geçersiz referans seviyesi: {0}. 1, 2 veya 3 olmalıdır. +user.error.depositAmountInvalid=Depozito tutarı pozitif olmalıdır +user.error.balanceNotFound=Kullanıcı bakiyesi bulunamadı + +# Task Controller +task.message.claimed=Görev başarıyla alındı +task.message.notCompleted=Görev tamamlanmadı! + +# Task Titles and Descriptions +# Referral tasks - separate keys for each requirement for proper grammar +task.title.inviteFriends.1=1 arkadaş davet et +task.title.inviteFriends.3=3 arkadaş davet et +task.title.inviteFriends.7=7 arkadaş davet et +task.title.inviteFriends.15=15 arkadaş davet et +task.title.inviteFriends.30=30 arkadaş davet et +task.title.inviteFriends.50=50 arkadaş davet et +task.title.inviteFriends.100=100 arkadaş davet et +task.title.followChannel=Haber kanalımızı takip edin +task.title.followChannelWithdrawals=Ödeme kanıtı kanalımızı takip edin +task.title.dailyBonus=Günlük bonus +task.title.deposit={0} bilet yatır +task.description.inviteFriends.1=Benzersiz referans bağlantınızı kullanarak 1 arkadaş davet edin +task.description.inviteFriends.3=Benzersiz referans bağlantınızı kullanarak 3 arkadaş davet edin +task.description.inviteFriends.7=Benzersiz referans bağlantınızı kullanarak 7 arkadaş davet edin +task.description.inviteFriends.15=Benzersiz referans bağlantınızı kullanarak 15 arkadaş davet et +task.description.inviteFriends.30=Benzersiz referans bağlantınızı kullanarak 30 arkadaş davet edin +task.description.inviteFriends.50=Benzersiz referans bağlantınızı kullanarak 50 arkadaş davet edin +task.description.inviteFriends.100=Benzersiz referans bağlantınızı kullanarak 100 arkadaş davet edin +task.description.followChannel=Haber kanalımızı takip edin +task.description.followChannelWithdrawals=Ödeme kanıtı kanalımızı takip edin +task.description.dailyBonus=Günlük ücretsiz biletinizi talep edin! +task.description.deposit=Hesabınıza {0} bilet yatırın + +# Task Reward Text +task.reward.tickets.1=+5 Bilet +task.reward.tickets.3=+15 Bilet +task.reward.tickets.7=+35 Bilet +task.reward.tickets.15=+75 Bilet +task.reward.tickets.30=+110 Bilet +task.reward.tickets.50=+150 Bilet +task.reward.tickets.100=+375 Bilet +task.reward.tickets.follow=+7 Bilet +task.reward.tickets.daily=+1 Bilet +task.reward.tickets.other.50=+5 Bilet +task.reward.tickets.other.250=+25 Bilet +task.reward.tickets.other.1000=+100 Bilet +task.reward.tickets.other.2000=+100 Bilet +task.reward.tickets.other.5000=+250 Bilet +task.reward.tickets.other.10000=+500 Bilet +task.reward.tickets.other.500=+100 Bilet +task.reward.tickets.other.50000=+2500 Bilet +task.reward.tickets.other.150000=+7500 Bilet +task.reward.tickets.other.500000=+25000 Bilet + +# Date/Time formatting +dateTime.at=saat + +# Telegram Bot Messages +bot.button.startSpinning=Oyunu Başlat +bot.button.startSpinningInline=↘️ OYUNU BAŞLAT ↙️ +bot.button.usersPayouts=Kullanıcı ödemeleri +bot.button.infoChannel=Bilgi kanalı +bot.button.openChannel=Kanalı aç +bot.button.goToChannel=Kanala git +bot.welcome.firstMessage=Win Spin ile şansınızı deneyin! +bot.welcome.message=Bahis yapmaya başlayın, kazanın ve paranızı doğrudan cüzdanınıza çekin.\n\n👉 Başlamak için yukarıdaki videoyu izleyin. +bot.message.startSpinning=Win Spin uygulamasını açmak için bu düğmeyi kullanın: +bot.message.usersPayouts=🔒 Gerçek zamanlı güncellemeler.\n\nTüm para çekme işlemleri kanalda gösterilir. Her şeyi kendiniz kontrol edebilirsiniz: +bot.message.infoChannel=📢 Tüm en son proje güncellemelerinden haberdar olun — tam burada, kanalımızda +bot.message.paySupport=Merhaba! Ödemenizle ilgili bir destek talebi oluşturmak için lütfen @winspinpaysupport ile iletişime geçin.\n\nLütfen dikkat: İade, yalnızca satın alınan biletler henüz oyun turlarında kullanılmadıysa verilir. +bot.message.unrecognized=Bunu tanıyamadık. Lütfen aşağıdaki düğmelerden birini kullanın. +

!6Xv3^S$u@Ru$nsq1?9gJo-unkE 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�DzG?r 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