admin panel cleanup
All checks were successful
Deploy to VPS / deploy (push) Successful in 1m26s

This commit is contained in:
Tihon
2026-03-18 13:15:08 +02:00
parent 955c6d1c01
commit 90efdf1f59
11 changed files with 64 additions and 58 deletions

View File

@@ -2,11 +2,9 @@ package com.honey.honey.controller;
import com.honey.honey.model.Payment; import com.honey.honey.model.Payment;
import com.honey.honey.model.Payout; import com.honey.honey.model.Payout;
import com.honey.honey.model.SupportTicket;
import com.honey.honey.model.UserA; import com.honey.honey.model.UserA;
import com.honey.honey.repository.PaymentRepository; import com.honey.honey.repository.PaymentRepository;
import com.honey.honey.repository.PayoutRepository; import com.honey.honey.repository.PayoutRepository;
import com.honey.honey.repository.SupportTicketRepository;
import com.honey.honey.repository.UserARepository; import com.honey.honey.repository.UserARepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -30,7 +28,6 @@ public class AdminDashboardController {
private final UserARepository userARepository; private final UserARepository userARepository;
private final PaymentRepository paymentRepository; private final PaymentRepository paymentRepository;
private final PayoutRepository payoutRepository; private final PayoutRepository payoutRepository;
private final SupportTicketRepository supportTicketRepository;
@GetMapping("/stats") @GetMapping("/stats")
public ResponseEntity<Map<String, Object>> getDashboardStats() { public ResponseEntity<Map<String, Object>> getDashboardStats() {
@@ -103,15 +100,6 @@ public class AdminDashboardController {
BigDecimal cryptoNetRevenueWeek = cryptoRevenueWeek.subtract(cryptoPayoutsWeek); BigDecimal cryptoNetRevenueWeek = cryptoRevenueWeek.subtract(cryptoPayoutsWeek);
BigDecimal cryptoNetRevenueMonth = cryptoRevenueMonth.subtract(cryptoPayoutsMonth); BigDecimal cryptoNetRevenueMonth = cryptoRevenueMonth.subtract(cryptoPayoutsMonth);
// 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 // Build response
stats.put("users", Map.of( stats.put("users", Map.of(
"total", totalUsers, "total", totalUsers,
@@ -162,19 +150,6 @@ public class AdminDashboardController {
crypto.put("profitUsdMonth", cryptoNetRevenueMonth); crypto.put("profitUsdMonth", cryptoNetRevenueMonth);
stats.put("crypto", crypto); stats.put("crypto", crypto);
stats.put("rounds", Map.of(
"total", 0L,
"today", 0L,
"week", 0L,
"month", 0L,
"avgPool", 0
));
stats.put("supportTickets", Map.of(
"open", openTickets,
"resolvedToday", ticketsResolvedToday
));
return ResponseEntity.ok(stats); return ResponseEntity.ok(stats);
} }
} }

View File

@@ -124,6 +124,7 @@ public class AdminPaymentController {
.orderId(payment.getOrderId()) .orderId(payment.getOrderId())
.starsAmount(payment.getStarsAmount()) .starsAmount(payment.getStarsAmount())
.ticketsAmount(payment.getTicketsAmount()) .ticketsAmount(payment.getTicketsAmount())
.usdAmount(payment.getUsdAmount())
.status(payment.getStatus().name()) .status(payment.getStatus().name())
.telegramPaymentChargeId(payment.getTelegramPaymentChargeId()) .telegramPaymentChargeId(payment.getTelegramPaymentChargeId())
.telegramProviderPaymentChargeId(payment.getTelegramProviderPaymentChargeId()) .telegramProviderPaymentChargeId(payment.getTelegramProviderPaymentChargeId())

View File

@@ -135,6 +135,7 @@ public class AdminPayoutController {
.type(payout.getType().name()) .type(payout.getType().name())
.giftName(payout.getGiftName() != null ? payout.getGiftName().name() : null) .giftName(payout.getGiftName() != null ? payout.getGiftName().name() : null)
.total(payout.getTotal()) .total(payout.getTotal())
.usdAmount(payout.getUsdAmount())
.starsAmount(payout.getStarsAmount()) .starsAmount(payout.getStarsAmount())
.quantity(payout.getQuantity()) .quantity(payout.getQuantity())
.status(payout.getStatus().name()) .status(payout.getStatus().name())

View File

@@ -58,8 +58,10 @@ public class AdminUserController {
@RequestParam(required = false) String languageCode, @RequestParam(required = false) String languageCode,
@RequestParam(required = false) Integer dateRegFrom, @RequestParam(required = false) Integer dateRegFrom,
@RequestParam(required = false) Integer dateRegTo, @RequestParam(required = false) Integer dateRegTo,
@RequestParam(required = false) Long balanceMin, @RequestParam(required = false) Long balanceAMin,
@RequestParam(required = false) Long balanceMax, @RequestParam(required = false) Long balanceAMax,
@RequestParam(required = false) Long balanceBMin,
@RequestParam(required = false) Long balanceBMax,
@RequestParam(required = false) Integer referralCountMin, @RequestParam(required = false) Integer referralCountMin,
@RequestParam(required = false) Integer referralCountMax, @RequestParam(required = false) Integer referralCountMax,
@RequestParam(required = false) Integer referrerId, @RequestParam(required = false) Integer referrerId,
@@ -81,9 +83,11 @@ public class AdminUserController {
} }
Pageable pageable = PageRequest.of(page, size, sort); Pageable pageable = PageRequest.of(page, size, sort);
// Convert balance filters from tickets (divide by 1000000) to bigint format // Convert balance filters from display value to bigint format
Long balanceMinBigint = balanceMin != null ? balanceMin * 1000000L : null; Long balanceAMinBigint = balanceAMin != null ? balanceAMin * 1000000L : null;
Long balanceMaxBigint = balanceMax != null ? balanceMax * 1000000L : null; Long balanceAMaxBigint = balanceAMax != null ? balanceAMax * 1000000L : null;
Long balanceBMinBigint = balanceBMin != null ? balanceBMin * 1000000L : null;
Long balanceBMaxBigint = balanceBMax != null ? balanceBMax * 1000000L : null;
boolean excludeMasters = isSupport(); boolean excludeMasters = isSupport();
Page<AdminUserDto> dtoPage = adminUserService.getUsers( Page<AdminUserDto> dtoPage = adminUserService.getUsers(
@@ -94,8 +98,10 @@ public class AdminUserController {
languageCode, languageCode,
dateRegFrom, dateRegFrom,
dateRegTo, dateRegTo,
balanceMinBigint, balanceAMinBigint,
balanceMaxBigint, balanceAMaxBigint,
balanceBMinBigint,
balanceBMaxBigint,
referralCountMin, referralCountMin,
referralCountMax, referralCountMax,
referrerId, referrerId,

View File

@@ -5,6 +5,7 @@ import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
@Data @Data
@@ -18,6 +19,8 @@ public class AdminPaymentDto {
private String orderId; private String orderId;
private Integer starsAmount; private Integer starsAmount;
private Long ticketsAmount; private Long ticketsAmount;
/** USD amount (e.g. 1.25). */
private BigDecimal usdAmount;
private String status; private String status;
private String telegramPaymentChargeId; private String telegramPaymentChargeId;
private String telegramProviderPaymentChargeId; private String telegramProviderPaymentChargeId;

View File

@@ -5,6 +5,7 @@ import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
@Data @Data
@@ -19,6 +20,8 @@ public class AdminPayoutDto {
private String type; private String type;
private String giftName; private String giftName;
private Long total; private Long total;
/** USD amount (e.g. 1.25). */
private BigDecimal usdAmount;
private Integer starsAmount; private Integer starsAmount;
private Integer quantity; private Integer quantity;
private String status; private String status;

View File

@@ -30,6 +30,7 @@ public class AdminUserDetailDto {
// Balance Info // Balance Info
private Long balanceA; private Long balanceA;
private Long balanceB;
private Long depositTotal; private Long depositTotal;
private Integer depositCount; private Integer depositCount;
private Long withdrawTotal; private Long withdrawTotal;

View File

@@ -6,7 +6,6 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
@Data @Data
@Builder @Builder
@@ -17,13 +16,12 @@ public class BalanceAdjustmentRequest {
private BalanceType balanceType; // A or B private BalanceType balanceType; // A or B
@NotNull(message = "Amount is required") @NotNull(message = "Amount is required")
private Long amount; // In bigint format (tickets * 1,000,000) private Long amount; // In bigint format (display value * 1,000,000)
@NotNull(message = "Operation is required") @NotNull(message = "Operation is required")
private OperationType operation; // ADD or SUBTRACT private OperationType operation; // ADD or SUBTRACT
@NotBlank(message = "Reason is required") private String reason; // Optional reason for adjustment (for audit log)
private String reason; // Reason for adjustment (for audit log)
public enum BalanceType { public enum BalanceType {
A, B A, B

View File

@@ -56,8 +56,10 @@ public class AdminUserService {
String languageCode, String languageCode,
Integer dateRegFrom, Integer dateRegFrom,
Integer dateRegTo, Integer dateRegTo,
Long balanceMin, Long balanceAMin,
Long balanceMax, Long balanceAMax,
Long balanceBMin,
Long balanceBMax,
Integer referralCountMin, Integer referralCountMin,
Integer referralCountMax, Integer referralCountMax,
Integer referrerId, Integer referrerId,
@@ -137,20 +139,16 @@ public class AdminUserService {
predicates.add(cb.lessThanOrEqualTo(root.get("dateReg"), endOfDayTimestamp)); predicates.add(cb.lessThanOrEqualTo(root.get("dateReg"), endOfDayTimestamp));
} }
// Balance / referral filters via subqueries so DB handles pagination // Balance A / Balance B filters via subqueries
if (balanceMin != null || balanceMax != null) { if (balanceAMin != null || balanceAMax != null || balanceBMin != null || balanceBMax != null) {
Subquery<Integer> subB = query.subquery(Integer.class); Subquery<Integer> subB = query.subquery(Integer.class);
Root<UserB> br = subB.from(UserB.class); Root<UserB> br = subB.from(UserB.class);
subB.select(br.get("id")); subB.select(br.get("id"));
List<Predicate> subPreds = new ArrayList<>(); List<Predicate> subPreds = new ArrayList<>();
if (balanceMin != null) { if (balanceAMin != null) subPreds.add(cb.greaterThanOrEqualTo(br.get("balanceA"), balanceAMin));
subPreds.add(cb.greaterThanOrEqualTo( if (balanceAMax != null) subPreds.add(cb.lessThanOrEqualTo(br.get("balanceA"), balanceAMax));
cb.sum(br.get("balanceA"), br.get("balanceB")), balanceMin)); if (balanceBMin != null) subPreds.add(cb.greaterThanOrEqualTo(br.get("balanceB"), balanceBMin));
} if (balanceBMax != null) subPreds.add(cb.lessThanOrEqualTo(br.get("balanceB"), balanceBMax));
if (balanceMax != null) {
subPreds.add(cb.lessThanOrEqualTo(
cb.sum(br.get("balanceA"), br.get("balanceB")), balanceMax));
}
subB.where(cb.and(subPreds.toArray(new Predicate[0]))); subB.where(cb.and(subPreds.toArray(new Predicate[0])));
predicates.add(cb.in(root.get("id")).value(subB)); predicates.add(cb.in(root.get("id")).value(subB));
} }
@@ -186,7 +184,7 @@ public class AdminUserService {
return cb.and(predicates.toArray(new Predicate[0])); return cb.and(predicates.toArray(new Predicate[0]));
}; };
Set<String> sortRequiresJoin = Set.of("balanceA", "depositTotal", "withdrawTotal", "referralCount", "profit"); Set<String> sortRequiresJoin = Set.of("balanceA", "balanceB", "depositTotal", "withdrawTotal", "referralCount", "profit");
boolean useJoinSort = sortBy != null && sortRequiresJoin.contains(sortBy); boolean useJoinSort = sortBy != null && sortRequiresJoin.contains(sortBy);
List<UserA> userList; List<UserA> userList;
long totalElements; long totalElements;
@@ -194,7 +192,7 @@ public class AdminUserService {
if (useJoinSort) { if (useJoinSort) {
List<Integer> orderedIds = getOrderedUserIdsForAdminList( List<Integer> orderedIds = getOrderedUserIdsForAdminList(
search, banned, countryCode, languageCode, search, banned, countryCode, languageCode,
dateRegFrom, dateRegTo, balanceMin, balanceMax, dateRegFrom, dateRegTo, balanceAMin, balanceAMax, balanceBMin, balanceBMax,
referralCountMin, referralCountMax, referralCountMin, referralCountMax,
referrerId, referralLevel, ipFilter, referrerId, referralLevel, ipFilter,
sortBy, sortDir != null ? sortDir : "desc", sortBy, sortDir != null ? sortDir : "desc",
@@ -301,8 +299,10 @@ public class AdminUserService {
String languageCode, String languageCode,
Integer dateRegFrom, Integer dateRegFrom,
Integer dateRegTo, Integer dateRegTo,
Long balanceMin, Long balanceAMin,
Long balanceMax, Long balanceAMax,
Long balanceBMin,
Long balanceBMax,
Integer referralCountMin, Integer referralCountMin,
Integer referralCountMax, Integer referralCountMax,
Integer referrerId, Integer referrerId,
@@ -374,14 +374,24 @@ public class AdminUserService {
params.add(dateRegTo); params.add(dateRegTo);
paramIndex++; paramIndex++;
} }
if (balanceMin != null) { if (balanceAMin != null) {
sql.append(" AND (b.balance_a + b.balance_b) >= ?"); sql.append(" AND b.balance_a >= ?");
params.add(balanceMin); params.add(balanceAMin);
paramIndex++; paramIndex++;
} }
if (balanceMax != null) { if (balanceAMax != null) {
sql.append(" AND (b.balance_a + b.balance_b) <= ?"); sql.append(" AND b.balance_a <= ?");
params.add(balanceMax); params.add(balanceAMax);
paramIndex++;
}
if (balanceBMin != null) {
sql.append(" AND b.balance_b >= ?");
params.add(balanceBMin);
paramIndex++;
}
if (balanceBMax != null) {
sql.append(" AND b.balance_b <= ?");
params.add(balanceBMax);
paramIndex++; paramIndex++;
} }
if (referralCountMin != null || referralCountMax != null) { if (referralCountMin != null || referralCountMax != null) {
@@ -417,6 +427,7 @@ public class AdminUserService {
String orderColumn = switch (sortBy != null ? sortBy : "") { String orderColumn = switch (sortBy != null ? sortBy : "") {
case "balanceA" -> "b.balance_a"; case "balanceA" -> "b.balance_a";
case "balanceB" -> "b.balance_b";
case "depositTotal" -> "b.deposit_total"; case "depositTotal" -> "b.deposit_total";
case "withdrawTotal" -> "b.withdraw_total"; case "withdrawTotal" -> "b.withdraw_total";
case "referralCount" -> "(d.referals_1 + d.referals_2 + d.referals_3 + d.referals_4 + d.referals_5)"; case "referralCount" -> "(d.referals_1 + d.referals_2 + d.referals_3 + d.referals_4 + d.referals_5)";
@@ -579,6 +590,7 @@ public class AdminUserService {
.banned(userA.getBanned()) .banned(userA.getBanned())
.ipAddress(IpUtils.bytesToIp(userA.getIp())) .ipAddress(IpUtils.bytesToIp(userA.getIp()))
.balanceA(userB.getBalanceA()) .balanceA(userB.getBalanceA())
.balanceB(userB.getBalanceB())
.depositTotal(userB.getDepositTotal()) .depositTotal(userB.getDepositTotal())
.depositCount(userB.getDepositCount()) .depositCount(userB.getDepositCount())
.withdrawTotal(userB.getWithdrawTotal()) .withdrawTotal(userB.getWithdrawTotal())

View File

@@ -0,0 +1,4 @@
-- Ensure payment_enabled and payout_enabled feature switches exist (for admin panel and runtime).
INSERT INTO feature_switches (`key`, `enabled`, `updated_at`)
VALUES ('payment_enabled', 1, CURRENT_TIMESTAMP), ('payout_enabled', 1, CURRENT_TIMESTAMP)
ON DUPLICATE KEY UPDATE `updated_at` = CURRENT_TIMESTAMP;

View File

@@ -0,0 +1,2 @@
-- Index for admin users list: filter and sort by Balance B
CREATE INDEX idx_users_b_balance_b ON db_users_b(balance_b);