diff --git a/src/main/java/com/honey/honey/config/OpenApiExamplesCustomizer.java b/src/main/java/com/honey/honey/config/OpenApiExamplesCustomizer.java index 8047721..34c58c9 100644 --- a/src/main/java/com/honey/honey/config/OpenApiExamplesCustomizer.java +++ b/src/main/java/com/honey/honey/config/OpenApiExamplesCustomizer.java @@ -43,8 +43,8 @@ public class OpenApiExamplesCustomizer implements GlobalOpenApiCustomizer { value.put("screenName", "The Boy"); value.put("dateReg", 1772897273); value.put("ip", "165.165.165.165"); - value.put("balanceA", 100000); - value.put("balanceB", 100000); + value.put("balanceA", 10000); + value.put("balanceB", 10000); value.put("avatarUrl", "/avatars/0/0/1/06c98267.png?v=1772897273"); value.put("languageCode", "EN"); value.put("paymentEnabled", true); diff --git a/src/main/java/com/honey/honey/controller/UserController.java b/src/main/java/com/honey/honey/controller/UserController.java index a9a6ed6..bce11ce 100644 --- a/src/main/java/com/honey/honey/controller/UserController.java +++ b/src/main/java/com/honey/honey/controller/UserController.java @@ -24,6 +24,9 @@ import org.springframework.web.bind.annotation.*; @RequiredArgsConstructor public class UserController { + /** Divisor for converting balance bigint to display value (backend sends display value only). */ + private static final double BALANCE_DISPLAY_DIVISOR = 1_000_000.0; + private final UserService userService; private final UserBRepository userBRepository; private final AvatarService avatarService; @@ -37,10 +40,12 @@ public class UserController { // Convert IP from byte[] to string for display String ipAddress = IpUtils.bytesToIp(user.getIp()); - // Get balances from UserB + // Get balances from UserB; convert to display value (divide by 1_000_000) var userBOpt = userBRepository.findById(user.getId()); - Long balanceA = userBOpt.map(UserB::getBalanceA).orElse(0L); - Long balanceB = userBOpt.map(UserB::getBalanceB).orElse(0L); + long rawBalanceA = userBOpt.map(UserB::getBalanceA).orElse(0L); + long rawBalanceB = userBOpt.map(UserB::getBalanceB).orElse(0L); + Double balanceA = rawBalanceA / BALANCE_DISPLAY_DIVISOR; + Double balanceB = rawBalanceB / BALANCE_DISPLAY_DIVISOR; Integer depositCount = userBOpt.map(UserB::getDepositCount).orElse(0); // Generate avatar URL on-the-fly (deterministic from userId) @@ -126,7 +131,6 @@ public class UserController { UserA user = UserContext.get(); Page referralsPage = userService.getReferrals(user.getId(), level, page); - return new ReferralsResponse( referralsPage.getContent(), referralsPage.getNumber(), diff --git a/src/main/java/com/honey/honey/dto/ReferralDto.java b/src/main/java/com/honey/honey/dto/ReferralDto.java index 63b0610..d6f7310 100644 --- a/src/main/java/com/honey/honey/dto/ReferralDto.java +++ b/src/main/java/com/honey/honey/dto/ReferralDto.java @@ -5,13 +5,14 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +/** API response DTO for referral list. Built from {@link ReferralProjection} in service. */ @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) + private String name; + private Double commission; // display value (raw from DB / 1_000_000) } diff --git a/src/main/java/com/honey/honey/dto/ReferralProjection.java b/src/main/java/com/honey/honey/dto/ReferralProjection.java new file mode 100644 index 0000000..b66b44d --- /dev/null +++ b/src/main/java/com/honey/honey/dto/ReferralProjection.java @@ -0,0 +1,10 @@ +package com.honey.honey.dto; + +/** + * Persistence/read model: raw referral row from DB (used only between repository and service). + * Not sent to frontend. Map to {@link ReferralDto} for API response. + */ +public interface ReferralProjection { + String getName(); + Long getCommission(); +} diff --git a/src/main/java/com/honey/honey/dto/UserDto.java b/src/main/java/com/honey/honey/dto/UserDto.java index becca3b..6389ab1 100644 --- a/src/main/java/com/honey/honey/dto/UserDto.java +++ b/src/main/java/com/honey/honey/dto/UserDto.java @@ -16,8 +16,8 @@ public class UserDto { 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 Long balanceB; // Second balance (stored as bigint) + private Double balanceA; // Balance for display (raw bigint / 1_000_000) + private Double balanceB; // Second balance for display (raw bigint / 1_000_000) 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 diff --git a/src/main/java/com/honey/honey/repository/UserDRepository.java b/src/main/java/com/honey/honey/repository/UserDRepository.java index 2a1ca0f..e1324c1 100644 --- a/src/main/java/com/honey/honey/repository/UserDRepository.java +++ b/src/main/java/com/honey/honey/repository/UserDRepository.java @@ -1,6 +1,6 @@ package com.honey.honey.repository; -import com.honey.honey.dto.ReferralDto; +import com.honey.honey.dto.ReferralProjection; import com.honey.honey.model.UserD; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -52,36 +52,33 @@ public interface UserDRepository extends JpaRepository { /** * Finds referrals for level 1 (where referer_id_1 = userId). - * Returns referrals with their screen_name and to_referer_1 commission. - * Ordered by commission DESC, then id DESC. + * Returns raw rows (name, commission bigint). Map to ReferralDto in service. */ - @Query("SELECT new com.honey.honey.dto.ReferralDto(ud.screenName, ud.toReferer1) " + + @Query("SELECT ud.screenName as name, ud.toReferer1 as commission " + "FROM UserD ud " + "WHERE ud.refererId1 = :userId AND ud.refererId1 > 0 " + "ORDER BY ud.toReferer1 DESC, ud.id DESC") - Page findReferralsLevel1(@Param("userId") Integer userId, Pageable pageable); + 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. - * Ordered by commission DESC, then id DESC. + * Returns raw rows (name, commission bigint). Map to ReferralDto in service. */ - @Query("SELECT new com.honey.honey.dto.ReferralDto(ud.screenName, ud.toReferer2) " + + @Query("SELECT ud.screenName as name, ud.toReferer2 as commission " + "FROM UserD ud " + "WHERE ud.refererId2 = :userId AND ud.refererId2 > 0 " + "ORDER BY ud.toReferer2 DESC, ud.id DESC") - Page findReferralsLevel2(@Param("userId") Integer userId, Pageable pageable); + 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. - * Ordered by commission DESC, then id DESC. + * Returns raw rows (name, commission bigint). Map to ReferralDto in service. */ - @Query("SELECT new com.honey.honey.dto.ReferralDto(ud.screenName, ud.toReferer3) " + + @Query("SELECT ud.screenName as name, ud.toReferer3 as commission " + "FROM UserD ud " + "WHERE ud.refererId3 = :userId AND ud.refererId3 > 0 " + "ORDER BY ud.toReferer3 DESC, ud.id DESC") - Page findReferralsLevel3(@Param("userId") Integer userId, Pageable pageable); + Page findReferralsLevel3(@Param("userId") Integer userId, Pageable pageable); /** * Masters: users whose id equals their master_id (and master_id > 0). diff --git a/src/main/java/com/honey/honey/service/UserService.java b/src/main/java/com/honey/honey/service/UserService.java index c77dd68..43f315b 100644 --- a/src/main/java/com/honey/honey/service/UserService.java +++ b/src/main/java/com/honey/honey/service/UserService.java @@ -1,6 +1,7 @@ package com.honey.honey.service; import com.honey.honey.dto.ReferralDto; +import com.honey.honey.dto.ReferralProjection; import com.honey.honey.model.UserA; import com.honey.honey.model.UserB; import com.honey.honey.model.UserD; @@ -13,13 +14,16 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; 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.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; /** * Service for user management with sharded tables. @@ -453,25 +457,33 @@ public class UserService { return userARepository.findByTelegramId(telegramId); } + private static final double COMMISSION_DISPLAY_DIVISOR = 1_000_000.0; + /** * Gets referrals for a specific level with pagination. - * Returns 10 results per page, ordered by commission DESC, then id DESC. + * Fetches raw rows (ReferralProjection), maps to API DTO (ReferralDto) with display commission. * * @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 + * @return Page of ReferralDto (name, commission as display value) */ public Page getReferrals(Integer userId, Integer level, Integer page) { Pageable pageable = PageRequest.of(page, 10); - - return switch (level) { + Page projectionPage = 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))); }; + List content = projectionPage.getContent().stream() + .map(p -> ReferralDto.builder() + .name(p.getName()) + .commission(p.getCommission() != null ? p.getCommission() / COMMISSION_DISPLAY_DIVISOR : 0.0) + .build()) + .collect(Collectors.toList()); + return new PageImpl<>(content, projectionPage.getPageable(), projectionPage.getTotalElements()); } }