This commit is contained in:
@@ -36,7 +36,8 @@ public class AdminUserController {
|
|||||||
private static final Set<String> SORTABLE_FIELDS = Set.of(
|
private static final Set<String> SORTABLE_FIELDS = Set.of(
|
||||||
"id", "screenName", "telegramId", "telegramName", "isPremium",
|
"id", "screenName", "telegramId", "telegramName", "isPremium",
|
||||||
"languageCode", "countryCode", "deviceCode", "dateReg", "dateLogin", "banned",
|
"languageCode", "countryCode", "deviceCode", "dateReg", "dateLogin", "banned",
|
||||||
"balanceA", "depositTotal", "withdrawTotal", "referralCount", "profit"
|
"balanceA", "balanceB", "depositTotal", "withdrawTotal", "referralCount", "profit",
|
||||||
|
"referrerTelegramName"
|
||||||
);
|
);
|
||||||
private static final Set<String> DEPOSIT_SORT_FIELDS = Set.of("id", "usdAmount", "status", "orderId", "createdAt", "completedAt");
|
private static final Set<String> DEPOSIT_SORT_FIELDS = Set.of("id", "usdAmount", "status", "orderId", "createdAt", "completedAt");
|
||||||
private static final Set<String> WITHDRAWAL_SORT_FIELDS = Set.of("id", "usdAmount", "cryptoName", "amountToSend", "txhash", "status", "paymentId", "createdAt", "resolvedAt");
|
private static final Set<String> WITHDRAWAL_SORT_FIELDS = Set.of("id", "usdAmount", "cryptoName", "amountToSend", "txhash", "status", "paymentId", "createdAt", "resolvedAt");
|
||||||
@@ -68,10 +69,12 @@ public class AdminUserController {
|
|||||||
@RequestParam(required = false) Integer referralLevel,
|
@RequestParam(required = false) Integer referralLevel,
|
||||||
@RequestParam(required = false) String ip,
|
@RequestParam(required = false) String ip,
|
||||||
@RequestParam(required = false) Boolean botActive,
|
@RequestParam(required = false) Boolean botActive,
|
||||||
@RequestParam(required = false) Integer depositCountMin) {
|
@RequestParam(required = false) Integer depositCountMin,
|
||||||
|
@RequestParam(required = false) Boolean hideSubAndBanned) {
|
||||||
|
|
||||||
// Build sort. Fields on UserB/UserD (balanceA, depositTotal, withdrawTotal, referralCount) are handled in service via custom query.
|
// Build sort. Fields on UserB/UserD (balanceA, balanceB, depositTotal, etc.) are handled in service via custom query.
|
||||||
Set<String> sortRequiresJoin = Set.of("balanceA", "depositTotal", "withdrawTotal", "referralCount", "profit");
|
Set<String> sortRequiresJoin = Set.of(
|
||||||
|
"balanceA", "balanceB", "depositTotal", "withdrawTotal", "referralCount", "profit", "referrerTelegramName");
|
||||||
String effectiveSortBy = sortBy != null && sortBy.trim().isEmpty() ? null : (sortBy != null ? sortBy.trim() : null);
|
String effectiveSortBy = sortBy != null && sortBy.trim().isEmpty() ? null : (sortBy != null ? sortBy.trim() : null);
|
||||||
if (effectiveSortBy != null && sortRequiresJoin.contains(effectiveSortBy)) {
|
if (effectiveSortBy != null && sortRequiresJoin.contains(effectiveSortBy)) {
|
||||||
// Pass through; service will use custom ordered query
|
// Pass through; service will use custom ordered query
|
||||||
@@ -113,7 +116,8 @@ public class AdminUserController {
|
|||||||
depositCountMin,
|
depositCountMin,
|
||||||
effectiveSortBy,
|
effectiveSortBy,
|
||||||
sortDir,
|
sortDir,
|
||||||
excludeMasters
|
excludeMasters,
|
||||||
|
Boolean.TRUE.equals(hideSubAndBanned)
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ public class AdminUserDetailDto {
|
|||||||
private Integer banned;
|
private Integer banned;
|
||||||
/** IP address as string (e.g. xxx.xxx.xxx.xxx), converted from varbinary in DB. */
|
/** IP address as string (e.g. xxx.xxx.xxx.xxx), converted from varbinary in DB. */
|
||||||
private String ipAddress;
|
private String ipAddress;
|
||||||
|
/** Number of users sharing the same IP (including this user). */
|
||||||
|
private Long ipAddressUserCount;
|
||||||
|
|
||||||
// Balance Info
|
// Balance Info
|
||||||
private Long balanceA;
|
private Long balanceA;
|
||||||
@@ -45,8 +47,10 @@ public class AdminUserDetailDto {
|
|||||||
// Referral Info
|
// Referral Info
|
||||||
private Integer referralCount;
|
private Integer referralCount;
|
||||||
private Long totalCommissionsEarned;
|
private Long totalCommissionsEarned;
|
||||||
/** Total commissions earned in USD (converted from tickets). */
|
/** Total commissions earned in USD (Honey: bigint / 10_000_000_000). */
|
||||||
private java.math.BigDecimal totalCommissionsEarnedUsd;
|
private java.math.BigDecimal totalCommissionsEarnedUsd;
|
||||||
|
/** Sum of COMPLETED payment USD across referral levels 1–3 for this user. */
|
||||||
|
private java.math.BigDecimal totalReferralDepositsUsd;
|
||||||
private Integer masterId;
|
private Integer masterId;
|
||||||
private List<ReferralLevelDto> referralLevels;
|
private List<ReferralLevelDto> referralLevels;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,21 @@ public class AdminUserDto {
|
|||||||
private Long totalCommissionsEarned; // Total commissions earned from referrals
|
private Long totalCommissionsEarned; // Total commissions earned from referrals
|
||||||
/** Profit in tickets (bigint): depositTotal - withdrawTotal */
|
/** Profit in tickets (bigint): depositTotal - withdrawTotal */
|
||||||
private Long profit;
|
private Long profit;
|
||||||
/** USD from db_users_b: depositTotal (tickets/1000) */
|
/** USD from db_users_b deposit_total (Honey scale). */
|
||||||
private BigDecimal depositTotalUsd;
|
private BigDecimal depositTotalUsd;
|
||||||
/** USD from db_users_b: withdrawTotal (tickets/1000) */
|
/** USD from db_users_b withdraw_total (Honey scale). */
|
||||||
private BigDecimal withdrawTotalUsd;
|
private BigDecimal withdrawTotalUsd;
|
||||||
/** USD from db_users_b: profit (tickets/1000) */
|
/** USD from db_users_b: profit (Honey: bigint / 10_000_000_000). */
|
||||||
private BigDecimal profitUsd;
|
private BigDecimal profitUsd;
|
||||||
|
/** True when user has not blocked the bot. */
|
||||||
|
private Boolean botActive;
|
||||||
|
/** True when user has a session with created_at <= now and expires_at >= now. */
|
||||||
|
private Boolean online;
|
||||||
|
/** Direct referrer (db_users_d.referer_id_1), if any. */
|
||||||
|
private Integer referrerId;
|
||||||
|
/** Telegram name of direct referrer; "-" when absent. */
|
||||||
|
private String referrerTelegramName;
|
||||||
|
/** (1 - withdrawUsd/depositUsd) * 100 when depositUsd > 0; otherwise null. */
|
||||||
|
private BigDecimal profitPercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,16 @@ import java.math.BigDecimal;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ReferralLevelDto {
|
public class ReferralLevelDto {
|
||||||
private Integer level; // 1-5
|
private Integer level; // 1–3 in admin UI; legacy rows may exist in DB
|
||||||
private Integer refererId;
|
private Integer refererId;
|
||||||
private Integer referralCount;
|
private Integer referralCount;
|
||||||
private Long commissionsEarned;
|
private Long commissionsEarned;
|
||||||
private Long commissionsPaid;
|
private Long commissionsPaid;
|
||||||
/** Commissions earned in USD (converted from tickets: 1000 tickets = 1 USD). */
|
/** Commissions earned in USD (Honey: bigint / 10_000_000_000). */
|
||||||
private BigDecimal commissionsEarnedUsd;
|
private BigDecimal commissionsEarnedUsd;
|
||||||
/** Commissions paid in USD (converted from tickets). */
|
/** Commissions paid in USD (Honey: bigint / 10_000_000_000). */
|
||||||
private BigDecimal commissionsPaidUsd;
|
private BigDecimal commissionsPaidUsd;
|
||||||
|
/** Sum of COMPLETED payment USD from referrals at this level. */
|
||||||
|
private BigDecimal depositsUsd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,5 +99,15 @@ public interface PaymentRepository extends JpaRepository<Payment, Long>, JpaSpec
|
|||||||
@Param("start") Instant start,
|
@Param("start") Instant start,
|
||||||
@Param("end") Instant end
|
@Param("end") Instant end
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Sum COMPLETED payment USD for users whose level-1 referrer is {@code userId}. */
|
||||||
|
@Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payment p, UserD d WHERE p.userId = d.id AND p.status = 'COMPLETED' AND p.usdAmount IS NOT NULL AND d.refererId1 = :userId")
|
||||||
|
java.math.BigDecimal sumCompletedUsdForReferralsLevel1(@Param("userId") Integer userId);
|
||||||
|
|
||||||
|
@Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payment p, UserD d WHERE p.userId = d.id AND p.status = 'COMPLETED' AND p.usdAmount IS NOT NULL AND d.refererId2 = :userId")
|
||||||
|
java.math.BigDecimal sumCompletedUsdForReferralsLevel2(@Param("userId") Integer userId);
|
||||||
|
|
||||||
|
@Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payment p, UserD d WHERE p.userId = d.id AND p.status = 'COMPLETED' AND p.usdAmount IS NOT NULL AND d.refererId3 = :userId")
|
||||||
|
java.math.BigDecimal sumCompletedUsdForReferralsLevel3(@Param("userId") Integer userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import org.springframework.data.repository.query.Param;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface SessionRepository extends JpaRepository<Session, Long> {
|
public interface SessionRepository extends JpaRepository<Session, Long> {
|
||||||
@@ -46,6 +48,13 @@ public interface SessionRepository extends JpaRepository<Session, Long> {
|
|||||||
* Returns the number of deleted rows.
|
* Returns the number of deleted rows.
|
||||||
* Note: MySQL requires LIMIT to be a literal or bound parameter, so we use a native query.
|
* Note: MySQL requires LIMIT to be a literal or bound parameter, so we use a native query.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* User IDs in {@code userIds} that have at least one session valid at {@code now}
|
||||||
|
* ({@code created_at <= now} and {@code expires_at >= now}).
|
||||||
|
*/
|
||||||
|
@Query("SELECT DISTINCT s.userId FROM Session s WHERE s.userId IN :userIds AND s.createdAt <= :now AND s.expiresAt >= :now")
|
||||||
|
Set<Integer> findOnlineUserIdsAmong(@Param("userIds") Collection<Integer> userIds, @Param("now") LocalDateTime now);
|
||||||
|
|
||||||
@Modifying(clearAutomatically = true, flushAutomatically = true)
|
@Modifying(clearAutomatically = true, flushAutomatically = true)
|
||||||
@Query(value = "DELETE FROM sessions WHERE expires_at < :now LIMIT :batchSize", nativeQuery = true)
|
@Query(value = "DELETE FROM sessions WHERE expires_at < :now LIMIT :batchSize", nativeQuery = true)
|
||||||
int deleteExpiredSessionsBatch(@Param("now") LocalDateTime now, @Param("batchSize") int batchSize);
|
int deleteExpiredSessionsBatch(@Param("now") LocalDateTime now, @Param("batchSize") int batchSize);
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ public interface UserARepository extends JpaRepository<UserA, Integer>, JpaSpeci
|
|||||||
*/
|
*/
|
||||||
@Query("SELECT u FROM UserA u WHERE u.id >= :fromId AND u.id <= :toId AND u.botActive = true ORDER BY u.id")
|
@Query("SELECT u FROM UserA u WHERE u.id >= :fromId AND u.id <= :toId AND u.botActive = true ORDER BY u.id")
|
||||||
Page<UserA> findByIdBetweenAndBotActiveTrue(@Param("fromId") int fromId, @Param("toId") int toId, Pageable pageable);
|
Page<UserA> findByIdBetweenAndBotActiveTrue(@Param("fromId") int fromId, @Param("toId") int toId, Pageable pageable);
|
||||||
|
|
||||||
|
/** Count users sharing the same IP (varbinary); returns 0 if {@code ip} is null. */
|
||||||
|
@Query("SELECT COUNT(u) FROM UserA u WHERE u.ip IS NOT NULL AND u.ip = :ip")
|
||||||
|
long countByIpEqual(@Param("ip") byte[] ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AdminMasterService {
|
public class AdminMasterService {
|
||||||
|
|
||||||
private static final BigDecimal USD_DIVISOR = new BigDecimal("1000000000");
|
/** Honey admin: balance bigint → USD (same scale as AdminUserService). */
|
||||||
|
private static final BigDecimal USD_DIVISOR = new BigDecimal("10000000000");
|
||||||
|
|
||||||
private final UserDRepository userDRepository;
|
private final UserDRepository userDRepository;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import jakarta.persistence.criteria.Subquery;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -36,7 +37,10 @@ public class AdminUserService {
|
|||||||
|
|
||||||
private static final long TICKETS_MULTIPLIER = 1_000_000L;
|
private static final long TICKETS_MULTIPLIER = 1_000_000L;
|
||||||
|
|
||||||
private static final BigDecimal TICKETS_TO_USD = new BigDecimal("0.001"); // 1000 tickets = 1 USD
|
/**
|
||||||
|
* Honey: display balance = DB bigint / 1_000_000; 1 USD = 10_000 display units → USD = DB / 10_000_000_000.
|
||||||
|
*/
|
||||||
|
private static final BigDecimal HONEY_DB_UNITS_PER_USD = new BigDecimal("10000000000");
|
||||||
|
|
||||||
private final UserARepository userARepository;
|
private final UserARepository userARepository;
|
||||||
private final UserBRepository userBRepository;
|
private final UserBRepository userBRepository;
|
||||||
@@ -46,6 +50,7 @@ public class AdminUserService {
|
|||||||
private final PayoutRepository payoutRepository;
|
private final PayoutRepository payoutRepository;
|
||||||
private final UserTaskClaimRepository userTaskClaimRepository;
|
private final UserTaskClaimRepository userTaskClaimRepository;
|
||||||
private final TaskRepository taskRepository;
|
private final TaskRepository taskRepository;
|
||||||
|
private final SessionRepository sessionRepository;
|
||||||
private final EntityManager entityManager;
|
private final EntityManager entityManager;
|
||||||
|
|
||||||
public Page<AdminUserDto> getUsers(
|
public Page<AdminUserDto> getUsers(
|
||||||
@@ -69,7 +74,8 @@ public class AdminUserService {
|
|||||||
Integer depositCountMin,
|
Integer depositCountMin,
|
||||||
String sortBy,
|
String sortBy,
|
||||||
String sortDir,
|
String sortDir,
|
||||||
boolean excludeMasters) {
|
boolean excludeMasters,
|
||||||
|
Boolean hideSubAndBanned) {
|
||||||
|
|
||||||
List<Integer> masterIds = excludeMasters ? userDRepository.findMasterUserIds() : List.of();
|
List<Integer> masterIds = excludeMasters ? userDRepository.findMasterUserIds() : List.of();
|
||||||
|
|
||||||
@@ -195,10 +201,22 @@ public class AdminUserService {
|
|||||||
predicates.add(cb.in(root.get("id")).value(subDep));
|
predicates.add(cb.in(root.get("id")).value(subDep));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(hideSubAndBanned)) {
|
||||||
|
predicates.add(cb.equal(root.get("banned"), 0));
|
||||||
|
Subquery<Integer> subMasterSelf = query.subquery(Integer.class);
|
||||||
|
Root<UserD> dm = subMasterSelf.from(UserD.class);
|
||||||
|
subMasterSelf.select(dm.get("id"));
|
||||||
|
subMasterSelf.where(cb.and(
|
||||||
|
cb.equal(dm.get("id"), dm.get("masterId")),
|
||||||
|
cb.gt(dm.get("masterId"), 0)));
|
||||||
|
predicates.add(cb.not(root.get("id").in(subMasterSelf)));
|
||||||
|
}
|
||||||
|
|
||||||
return cb.and(predicates.toArray(new Predicate[0]));
|
return cb.and(predicates.toArray(new Predicate[0]));
|
||||||
};
|
};
|
||||||
|
|
||||||
Set<String> sortRequiresJoin = Set.of("balanceA", "balanceB", "depositTotal", "withdrawTotal", "referralCount", "profit");
|
Set<String> sortRequiresJoin = Set.of(
|
||||||
|
"balanceA", "balanceB", "depositTotal", "withdrawTotal", "referralCount", "profit", "referrerTelegramName");
|
||||||
boolean useJoinSort = sortBy != null && sortRequiresJoin.contains(sortBy);
|
boolean useJoinSort = sortBy != null && sortRequiresJoin.contains(sortBy);
|
||||||
List<UserA> userList;
|
List<UserA> userList;
|
||||||
long totalElements;
|
long totalElements;
|
||||||
@@ -210,6 +228,7 @@ public class AdminUserService {
|
|||||||
referralCountMin, referralCountMax,
|
referralCountMin, referralCountMax,
|
||||||
referrerId, referralLevel, ipFilter,
|
referrerId, referralLevel, ipFilter,
|
||||||
botActive, depositCountMin,
|
botActive, depositCountMin,
|
||||||
|
hideSubAndBanned,
|
||||||
sortBy, sortDir != null ? sortDir : "desc",
|
sortBy, sortDir != null ? sortDir : "desc",
|
||||||
pageable.getPageSize(), (int) pageable.getOffset(),
|
pageable.getPageSize(), (int) pageable.getOffset(),
|
||||||
masterIds);
|
masterIds);
|
||||||
@@ -236,6 +255,20 @@ public class AdminUserService {
|
|||||||
Map<Integer, UserD> userDMap = userDRepository.findAllById(userIds).stream()
|
Map<Integer, UserD> userDMap = userDRepository.findAllById(userIds).stream()
|
||||||
.collect(Collectors.toMap(UserD::getId, ud -> ud));
|
.collect(Collectors.toMap(UserD::getId, ud -> ud));
|
||||||
|
|
||||||
|
LocalDateTime sessionNow = LocalDateTime.now();
|
||||||
|
Set<Integer> onlineIds = userIds.isEmpty()
|
||||||
|
? Set.of()
|
||||||
|
: sessionRepository.findOnlineUserIdsAmong(userIds, sessionNow);
|
||||||
|
|
||||||
|
Set<Integer> refererIds = userDMap.values().stream()
|
||||||
|
.map(UserD::getRefererId1)
|
||||||
|
.filter(id -> id != null && id > 0)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Map<Integer, UserA> referrersById = refererIds.isEmpty()
|
||||||
|
? Map.of()
|
||||||
|
: userARepository.findAllById(refererIds).stream()
|
||||||
|
.collect(Collectors.toMap(UserA::getId, u -> u));
|
||||||
|
|
||||||
// Map to DTOs (filtering is done in DB via specification subqueries)
|
// Map to DTOs (filtering is done in DB via specification subqueries)
|
||||||
List<AdminUserDto> pageContent = userList.stream()
|
List<AdminUserDto> pageContent = userList.stream()
|
||||||
.map(userA -> {
|
.map(userA -> {
|
||||||
@@ -273,6 +306,21 @@ public class AdminUserService {
|
|||||||
BigDecimal depositTotalUsd = ticketsToUsd(userB.getDepositTotal());
|
BigDecimal depositTotalUsd = ticketsToUsd(userB.getDepositTotal());
|
||||||
BigDecimal withdrawTotalUsd = ticketsToUsd(userB.getWithdrawTotal());
|
BigDecimal withdrawTotalUsd = ticketsToUsd(userB.getWithdrawTotal());
|
||||||
BigDecimal profitUsd = ticketsToUsd(profit);
|
BigDecimal profitUsd = ticketsToUsd(profit);
|
||||||
|
BigDecimal profitPercent = computeProfitPercent(depositTotalUsd, withdrawTotalUsd);
|
||||||
|
|
||||||
|
Integer referrerIdVal = null;
|
||||||
|
String referrerTelegramNameVal = null;
|
||||||
|
int rid = userD.getRefererId1();
|
||||||
|
if (rid > 0) {
|
||||||
|
referrerIdVal = rid;
|
||||||
|
UserA refA = referrersById.get(rid);
|
||||||
|
String tn = refA != null ? refA.getTelegramName() : null;
|
||||||
|
if (tn == null || tn.isBlank() || "-".equals(tn)) {
|
||||||
|
referrerTelegramNameVal = "-";
|
||||||
|
} else {
|
||||||
|
referrerTelegramNameVal = tn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return AdminUserDto.builder()
|
return AdminUserDto.builder()
|
||||||
.id(userA.getId())
|
.id(userA.getId())
|
||||||
@@ -296,6 +344,11 @@ public class AdminUserService {
|
|||||||
.depositTotalUsd(depositTotalUsd)
|
.depositTotalUsd(depositTotalUsd)
|
||||||
.withdrawTotalUsd(withdrawTotalUsd)
|
.withdrawTotalUsd(withdrawTotalUsd)
|
||||||
.profitUsd(profitUsd)
|
.profitUsd(profitUsd)
|
||||||
|
.profitPercent(profitPercent)
|
||||||
|
.botActive(userA.isBotActive())
|
||||||
|
.online(onlineIds.contains(userA.getId()))
|
||||||
|
.referrerId(referrerIdVal)
|
||||||
|
.referrerTelegramName(referrerTelegramNameVal)
|
||||||
.build();
|
.build();
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -325,6 +378,7 @@ public class AdminUserService {
|
|||||||
String ipFilter,
|
String ipFilter,
|
||||||
Boolean botActive,
|
Boolean botActive,
|
||||||
Integer depositCountMin,
|
Integer depositCountMin,
|
||||||
|
Boolean hideSubAndBanned,
|
||||||
String sortBy,
|
String sortBy,
|
||||||
String sortDir,
|
String sortDir,
|
||||||
int limit,
|
int limit,
|
||||||
@@ -333,7 +387,8 @@ public class AdminUserService {
|
|||||||
StringBuilder sql = new StringBuilder(
|
StringBuilder sql = new StringBuilder(
|
||||||
"SELECT a.id FROM db_users_a a " +
|
"SELECT a.id FROM db_users_a a " +
|
||||||
"INNER JOIN db_users_b b ON a.id = b.id " +
|
"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");
|
"INNER JOIN db_users_d d ON a.id = d.id " +
|
||||||
|
"LEFT JOIN db_users_a ref ON d.referer_id_1 = ref.id WHERE 1=1");
|
||||||
List<Object> params = new ArrayList<>();
|
List<Object> params = new ArrayList<>();
|
||||||
int paramIndex = 1;
|
int paramIndex = 1;
|
||||||
|
|
||||||
@@ -451,6 +506,10 @@ public class AdminUserService {
|
|||||||
params.add(depositCountMin);
|
params.add(depositCountMin);
|
||||||
paramIndex++;
|
paramIndex++;
|
||||||
}
|
}
|
||||||
|
if (Boolean.TRUE.equals(hideSubAndBanned)) {
|
||||||
|
sql.append(" AND a.banned = 0");
|
||||||
|
sql.append(" AND NOT (d.id = d.master_id AND d.master_id > 0)");
|
||||||
|
}
|
||||||
|
|
||||||
String orderColumn = switch (sortBy != null ? sortBy : "") {
|
String orderColumn = switch (sortBy != null ? sortBy : "") {
|
||||||
case "balanceA" -> "b.balance_a";
|
case "balanceA" -> "b.balance_a";
|
||||||
@@ -459,6 +518,7 @@ public class AdminUserService {
|
|||||||
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)";
|
||||||
case "profit" -> "(b.deposit_total - b.withdraw_total)";
|
case "profit" -> "(b.deposit_total - b.withdraw_total)";
|
||||||
|
case "referrerTelegramName" -> "ref.telegram_name";
|
||||||
default -> "a.id";
|
default -> "a.id";
|
||||||
};
|
};
|
||||||
String direction = "asc".equalsIgnoreCase(sortDir) ? " ASC" : " DESC";
|
String direction = "asc".equalsIgnoreCase(sortDir) ? " ASC" : " DESC";
|
||||||
@@ -551,41 +611,47 @@ public class AdminUserService {
|
|||||||
long totalCommissions = userD.getFromReferals1() + userD.getFromReferals2() +
|
long totalCommissions = userD.getFromReferals1() + userD.getFromReferals2() +
|
||||||
userD.getFromReferals3() + userD.getFromReferals4() + userD.getFromReferals5();
|
userD.getFromReferals3() + userD.getFromReferals4() + userD.getFromReferals5();
|
||||||
|
|
||||||
// Build referral levels
|
BigDecimal refDep1 = paymentRepository.sumCompletedUsdForReferralsLevel1(userId);
|
||||||
|
BigDecimal refDep2 = paymentRepository.sumCompletedUsdForReferralsLevel2(userId);
|
||||||
|
BigDecimal refDep3 = paymentRepository.sumCompletedUsdForReferralsLevel3(userId);
|
||||||
|
if (refDep1 == null) refDep1 = BigDecimal.ZERO;
|
||||||
|
if (refDep2 == null) refDep2 = BigDecimal.ZERO;
|
||||||
|
if (refDep3 == null) refDep3 = BigDecimal.ZERO;
|
||||||
|
BigDecimal totalReferralDepositsUsd = refDep1.add(refDep2).add(refDep3);
|
||||||
|
|
||||||
|
// Build referral levels (admin shows 1–3 only)
|
||||||
List<ReferralLevelDto> referralLevels = new ArrayList<>();
|
List<ReferralLevelDto> referralLevels = new ArrayList<>();
|
||||||
for (int level = 1; level <= 5; level++) {
|
for (int level = 1; level <= 3; level++) {
|
||||||
int refererId = switch (level) {
|
int refererId = switch (level) {
|
||||||
case 1 -> userD.getRefererId1();
|
case 1 -> userD.getRefererId1();
|
||||||
case 2 -> userD.getRefererId2();
|
case 2 -> userD.getRefererId2();
|
||||||
case 3 -> userD.getRefererId3();
|
case 3 -> userD.getRefererId3();
|
||||||
case 4 -> userD.getRefererId4();
|
|
||||||
case 5 -> userD.getRefererId5();
|
|
||||||
default -> 0;
|
default -> 0;
|
||||||
};
|
};
|
||||||
int referralCount = switch (level) {
|
int referralCount = switch (level) {
|
||||||
case 1 -> userD.getReferals1();
|
case 1 -> userD.getReferals1();
|
||||||
case 2 -> userD.getReferals2();
|
case 2 -> userD.getReferals2();
|
||||||
case 3 -> userD.getReferals3();
|
case 3 -> userD.getReferals3();
|
||||||
case 4 -> userD.getReferals4();
|
|
||||||
case 5 -> userD.getReferals5();
|
|
||||||
default -> 0;
|
default -> 0;
|
||||||
};
|
};
|
||||||
long commissionsEarned = switch (level) {
|
long commissionsEarned = switch (level) {
|
||||||
case 1 -> userD.getFromReferals1();
|
case 1 -> userD.getFromReferals1();
|
||||||
case 2 -> userD.getFromReferals2();
|
case 2 -> userD.getFromReferals2();
|
||||||
case 3 -> userD.getFromReferals3();
|
case 3 -> userD.getFromReferals3();
|
||||||
case 4 -> userD.getFromReferals4();
|
|
||||||
case 5 -> userD.getFromReferals5();
|
|
||||||
default -> 0L;
|
default -> 0L;
|
||||||
};
|
};
|
||||||
long commissionsPaid = switch (level) {
|
long commissionsPaid = switch (level) {
|
||||||
case 1 -> userD.getToReferer1();
|
case 1 -> userD.getToReferer1();
|
||||||
case 2 -> userD.getToReferer2();
|
case 2 -> userD.getToReferer2();
|
||||||
case 3 -> userD.getToReferer3();
|
case 3 -> userD.getToReferer3();
|
||||||
case 4 -> userD.getToReferer4();
|
|
||||||
case 5 -> userD.getToReferer5();
|
|
||||||
default -> 0L;
|
default -> 0L;
|
||||||
};
|
};
|
||||||
|
BigDecimal depositsUsd = switch (level) {
|
||||||
|
case 1 -> refDep1;
|
||||||
|
case 2 -> refDep2;
|
||||||
|
case 3 -> refDep3;
|
||||||
|
default -> BigDecimal.ZERO;
|
||||||
|
};
|
||||||
|
|
||||||
referralLevels.add(ReferralLevelDto.builder()
|
referralLevels.add(ReferralLevelDto.builder()
|
||||||
.level(level)
|
.level(level)
|
||||||
@@ -595,6 +661,7 @@ public class AdminUserService {
|
|||||||
.commissionsPaid(commissionsPaid)
|
.commissionsPaid(commissionsPaid)
|
||||||
.commissionsEarnedUsd(ticketsToUsd(commissionsEarned))
|
.commissionsEarnedUsd(ticketsToUsd(commissionsEarned))
|
||||||
.commissionsPaidUsd(ticketsToUsd(commissionsPaid))
|
.commissionsPaidUsd(ticketsToUsd(commissionsPaid))
|
||||||
|
.depositsUsd(depositsUsd)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,6 +669,11 @@ public class AdminUserService {
|
|||||||
BigDecimal withdrawTotalUsd = ticketsToUsd(userB.getWithdrawTotal());
|
BigDecimal withdrawTotalUsd = ticketsToUsd(userB.getWithdrawTotal());
|
||||||
BigDecimal totalCommissionsEarnedUsd = ticketsToUsd(totalCommissions);
|
BigDecimal totalCommissionsEarnedUsd = ticketsToUsd(totalCommissions);
|
||||||
|
|
||||||
|
long ipAddressUserCount = 0L;
|
||||||
|
if (userA.getIp() != null) {
|
||||||
|
ipAddressUserCount = userARepository.countByIpEqual(userA.getIp());
|
||||||
|
}
|
||||||
|
|
||||||
return AdminUserDetailDto.builder()
|
return AdminUserDetailDto.builder()
|
||||||
.id(userA.getId())
|
.id(userA.getId())
|
||||||
.screenName(userA.getScreenName())
|
.screenName(userA.getScreenName())
|
||||||
@@ -616,6 +688,7 @@ public class AdminUserService {
|
|||||||
.dateLogin(userA.getDateLogin())
|
.dateLogin(userA.getDateLogin())
|
||||||
.banned(userA.getBanned())
|
.banned(userA.getBanned())
|
||||||
.ipAddress(IpUtils.bytesToIp(userA.getIp()))
|
.ipAddress(IpUtils.bytesToIp(userA.getIp()))
|
||||||
|
.ipAddressUserCount(ipAddressUserCount)
|
||||||
.balanceA(userB.getBalanceA())
|
.balanceA(userB.getBalanceA())
|
||||||
.balanceB(userB.getBalanceB())
|
.balanceB(userB.getBalanceB())
|
||||||
.depositTotal(userB.getDepositTotal())
|
.depositTotal(userB.getDepositTotal())
|
||||||
@@ -628,6 +701,7 @@ public class AdminUserService {
|
|||||||
.referralCount(totalReferrals)
|
.referralCount(totalReferrals)
|
||||||
.totalCommissionsEarned(totalCommissions)
|
.totalCommissionsEarned(totalCommissions)
|
||||||
.totalCommissionsEarnedUsd(totalCommissionsEarnedUsd)
|
.totalCommissionsEarnedUsd(totalCommissionsEarnedUsd)
|
||||||
|
.totalReferralDepositsUsd(totalReferralDepositsUsd)
|
||||||
.masterId(userD.getMasterId() > 0 ? userD.getMasterId() : null)
|
.masterId(userD.getMasterId() > 0 ? userD.getMasterId() : null)
|
||||||
.referralLevels(referralLevels)
|
.referralLevels(referralLevels)
|
||||||
.build();
|
.build();
|
||||||
@@ -635,7 +709,20 @@ public class AdminUserService {
|
|||||||
|
|
||||||
private static BigDecimal ticketsToUsd(long ticketsBigint) {
|
private static BigDecimal ticketsToUsd(long ticketsBigint) {
|
||||||
if (ticketsBigint == 0) return BigDecimal.ZERO;
|
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);
|
return BigDecimal.valueOf(ticketsBigint)
|
||||||
|
.divide(HONEY_DB_UNITS_PER_USD, 8, RoundingMode.HALF_UP)
|
||||||
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigDecimal computeProfitPercent(BigDecimal depositUsd, BigDecimal withdrawUsd) {
|
||||||
|
if (depositUsd == null || depositUsd.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BigDecimal w = withdrawUsd != null ? withdrawUsd : BigDecimal.ZERO;
|
||||||
|
return BigDecimal.ONE
|
||||||
|
.subtract(w.divide(depositUsd, 8, RoundingMode.HALF_UP))
|
||||||
|
.multiply(BigDecimal.valueOf(100))
|
||||||
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<AdminTransactionDto> getUserTransactions(Integer userId, Pageable pageable) {
|
public Page<AdminTransactionDto> getUserTransactions(Integer userId, Pageable pageable) {
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- Speed up admin "online" batch lookup: sessions active at a point in time by user_id
|
||||||
|
CREATE INDEX idx_sessions_user_expires ON sessions (user_id, expires_at);
|
||||||
Reference in New Issue
Block a user