ソースを参照

排行榜、钱包、团队

xudm 2 ヶ月 前
コミット
62fcba802e
81 ファイル変更2358 行追加434 行削除
  1. 5 0
      pom.xml
  2. 1 1
      src/main/java/com/xs/core/common/constant/GoldCoinConstant.java
  3. 40 27
      src/main/java/com/xs/core/common/validation/CheckUtils.java
  4. 42 0
      src/main/java/com/xs/core/config/i18n/CustomLocaleChangeInterceptor.java
  5. 17 0
      src/main/java/com/xs/core/config/i18n/InternationalizationConfig.java
  6. 19 0
      src/main/java/com/xs/core/config/i18n/LocaleConfig.java
  7. 4 0
      src/main/java/com/xs/core/config/satoken/SaTokenConfigure.java
  8. 20 4
      src/main/java/com/xs/core/controller/competition/CompetitionController.java
  9. 25 0
      src/main/java/com/xs/core/controller/team/DistributionController.java
  10. 56 2
      src/main/java/com/xs/core/controller/team/TeamShareController.java
  11. 19 0
      src/main/java/com/xs/core/controller/user/LoginController.java
  12. 29 0
      src/main/java/com/xs/core/controller/wallet/WalletController.java
  13. 11 7
      src/main/java/com/xs/core/filter/GlobalExceptionHandler.java
  14. 2 2
      src/main/java/com/xs/core/filter/RequestHandler.java
  15. 23 0
      src/main/java/com/xs/core/init/MessageSourceInitializer.java
  16. 16 0
      src/main/java/com/xs/core/mapper/team/InviteRewardsRuleMapper.java
  17. 16 0
      src/main/java/com/xs/core/mapper/team/InviteTaskRewardsFetchRecordMapper.java
  18. 10 0
      src/main/java/com/xs/core/mapper/team/TeamInviteRewardRecordMapper.java
  19. 5 0
      src/main/java/com/xs/core/mapper/team/TeamShareRewardRecordMapper.java
  20. 22 6
      src/main/java/com/xs/core/mapper/user/UserMapper.java
  21. 16 0
      src/main/java/com/xs/core/mapper/wallet/UserWalletMapper.java
  22. 62 105
      src/main/java/com/xs/core/model/ResponseResult.java
  23. 90 0
      src/main/java/com/xs/core/model/team/entity/InviteRewardsRule.java
  24. 76 0
      src/main/java/com/xs/core/model/team/entity/InviteTaskRewardsFetchRecord.java
  25. 16 0
      src/main/java/com/xs/core/model/team/req/ClaimTeamInviteTaskRewardReq.java
  26. 15 0
      src/main/java/com/xs/core/model/team/req/TeamShareReq.java
  27. 28 0
      src/main/java/com/xs/core/model/team/resp/TeamInviteAvailableRewardsResp.java
  28. 72 0
      src/main/java/com/xs/core/model/team/resp/TeamInviteRewardsResp.java
  29. 18 0
      src/main/java/com/xs/core/model/team/resp/TeamShareInfoResp.java
  30. 83 0
      src/main/java/com/xs/core/model/team/resp/TeamShareUserResp.java
  31. 0 7
      src/main/java/com/xs/core/model/team/resp/TeamUserResp.java
  32. 47 3
      src/main/java/com/xs/core/model/team/resp/TeamsInfoResp.java
  33. 4 29
      src/main/java/com/xs/core/model/user/entity/User.java
  34. 0 2
      src/main/java/com/xs/core/model/user/req/GoldCoinCompetitionRankingReq.java
  35. 21 0
      src/main/java/com/xs/core/model/user/resp/GoldCoinLeaderboardInfo.java
  36. 79 0
      src/main/java/com/xs/core/model/user/resp/TeamCompetitionResp.java
  37. 20 12
      src/main/java/com/xs/core/model/user/resp/UserCompetitionResp.java
  38. 75 0
      src/main/java/com/xs/core/model/wallet/entity/UserWallet.java
  39. 54 0
      src/main/java/com/xs/core/model/wallet/resp/WalletInfoResp.java
  40. 3 3
      src/main/java/com/xs/core/mq/producer/GoldCoinMessageProducer.java
  41. 25 19
      src/main/java/com/xs/core/service/Impl/coin/GoldCoinServiceImpl.java
  42. 15 4
      src/main/java/com/xs/core/service/Impl/competition/CompetitionServiceImpl.java
  43. 29 0
      src/main/java/com/xs/core/service/Impl/team/InviteRewardsRuleServiceImpl.java
  44. 36 0
      src/main/java/com/xs/core/service/Impl/team/InviteTaskRewardsFetchRecordServiceImpl.java
  45. 8 0
      src/main/java/com/xs/core/service/Impl/team/TeamInviteRewardRecordServiceImpl.java
  46. 153 33
      src/main/java/com/xs/core/service/Impl/team/TeamShareGoldCoinSettlementServiceImpl.java
  47. 21 0
      src/main/java/com/xs/core/service/Impl/team/TeamShareRewardRecordServiceImpl.java
  48. 26 15
      src/main/java/com/xs/core/service/Impl/user/AppLoginServiceImpl.java
  49. 0 69
      src/main/java/com/xs/core/service/Impl/user/TgUserServiceImpl.java
  50. 60 0
      src/main/java/com/xs/core/service/Impl/user/UserServiceImpl.java
  51. 93 0
      src/main/java/com/xs/core/service/Impl/wallet/UserWalletServiceImpl.java
  52. 93 0
      src/main/java/com/xs/core/service/Impl/wallet/WalletServiceImpl.java
  53. 15 4
      src/main/java/com/xs/core/service/competition/CompetitionService.java
  54. 19 0
      src/main/java/com/xs/core/service/team/IInviteRewardsRuleService.java
  55. 23 0
      src/main/java/com/xs/core/service/team/IInviteTaskRewardsFetchRecordService.java
  56. 1 0
      src/main/java/com/xs/core/service/team/ITeamInviteConfigService.java
  57. 9 0
      src/main/java/com/xs/core/service/team/ITeamInviteRewardRecordService.java
  58. 21 0
      src/main/java/com/xs/core/service/team/ITeamShareRewardRecordService.java
  59. 40 12
      src/main/java/com/xs/core/service/team/TeamShareService.java
  60. 2 2
      src/main/java/com/xs/core/service/user/AppLoginService.java
  61. 18 14
      src/main/java/com/xs/core/service/user/IUserService.java
  62. 49 0
      src/main/java/com/xs/core/service/wallet/IUserWalletService.java
  63. 15 0
      src/main/java/com/xs/core/service/wallet/WalletService.java
  64. 2 2
      src/main/java/com/xs/core/utils/MyGenerator.java
  65. 4 4
      src/main/resources/application-dev.yml
  66. 1 1
      src/main/resources/application-prod.yml
  67. 17 0
      src/main/resources/mapper/InviteRewardsRuleMapper.xml
  68. 13 0
      src/main/resources/mapper/InviteTaskRewardsFetchRecordMapper.xml
  69. 25 0
      src/main/resources/mapper/TeamInviteRewardRecordMapper.xml
  70. 34 0
      src/main/resources/mapper/TeamShareRewardRecordMapper.xml
  71. 76 45
      src/main/resources/mapper/UserMapper.xml
  72. 28 0
      src/main/resources/messages/messages_en.properties
  73. 24 0
      src/main/resources/messages/messages_fa.properties
  74. 24 0
      src/main/resources/messages/messages_ja.properties
  75. 24 0
      src/main/resources/messages/messages_ko.properties
  76. 24 0
      src/main/resources/messages/messages_pt.properties
  77. 23 0
      src/main/resources/messages/messages_ru.properties
  78. 24 0
      src/main/resources/messages/messages_th.properties
  79. 25 0
      src/main/resources/messages/messages_vi.properties
  80. 34 0
      src/main/resources/messages/messages_zh_CN.properties
  81. 24 0
      src/main/resources/messages/messages_zh_TW.properties

+ 5 - 0
pom.xml

@@ -57,6 +57,11 @@
             </exclusions>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
         <!--  redis      -->
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 1 - 1
src/main/java/com/xs/core/common/constant/GoldCoinConstant.java

@@ -20,7 +20,7 @@ public interface GoldCoinConstant {
     /**
      * 金币产出时间,单位秒
      */
-    long GOLD_COIN_PRODUCT_TIME = 60 * 60 * 12L;
+    long GOLD_COIN_PRODUCT_TIME = 60 * 3L;
 
     long BOOST_RATE_RESIDUE_TIMESTAMP = 60 * 60 * 12L;
 

+ 40 - 27
src/main/java/com/xs/core/common/validation/CheckUtils.java

@@ -2,70 +2,83 @@ package com.xs.core.common.validation;
 
 import cn.hutool.core.text.CharSequenceUtil;
 import com.xs.core.common.exception.BusinessException;
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
 
+import java.util.Locale;
 import java.util.function.BooleanSupplier;
 
 public class CheckUtils extends Validator {
     private static final Class<BusinessException> EXCEPTION_TYPE = BusinessException.class;
+    private static MessageSource messageSource;
 
     private CheckUtils() {
     }
 
+    public static void setMessageSource(MessageSource source) {
+        messageSource = source;
+    }
+
+    private static String getMessage(String key, Object... args) {
+        Locale locale = LocaleContextHolder.getLocale();
+        return messageSource.getMessage(key, args, locale);
+    }
+
     public static void throwIfNotExists(Object obj, String entityName, String fieldName, Object fieldValue) {
-        String message = "%s 为 [%s] 的 %s 记录已不存在".formatted(fieldName, fieldValue, CharSequenceUtil.replace(entityName, "DO", ""));
+        String message = getMessage("error.not.exists", fieldName, fieldValue, CharSequenceUtil.replace(entityName, "DO", ""));
         throwIfNull(obj, message, EXCEPTION_TYPE);
     }
 
-    public static void throwIfNull(Object obj, String template, Object... params) {
-        throwIfNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfNull(Object obj, String messageKey, Object... params) {
+        throwIfNull(obj, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfNotNull(Object obj, String template, Object... params) {
-        throwIfNotNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfNotNull(Object obj, String messageKey, Object... params) {
+        throwIfNotNull(obj, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
     public static void throwIfExists(Object obj, String entityName, String fieldName, Object fieldValue) {
-        String message = "%s 为 [%s] 的 %s 记录已存在".formatted(fieldName, fieldValue, entityName);
+        String message = getMessage("error.exists", fieldName, fieldValue, entityName);
         throwIfNotNull(obj, message, EXCEPTION_TYPE);
     }
 
-    public static void throwIfEmpty(Object obj, String template, Object... params) {
-        throwIfEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfEmpty(Object obj, String messageKey, Object... params) {
+        throwIfEmpty(obj, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfNotEmpty(Object obj, String template, Object... params) {
-        throwIfNotEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfNotEmpty(Object obj, String messageKey, Object... params) {
+        throwIfNotEmpty(obj, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfBlank(CharSequence str, String template, Object... params) {
-        throwIfBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfBlank(CharSequence str, String messageKey, Object... params) {
+        throwIfBlank(str, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfNotBlank(CharSequence str, String template, Object... params) {
-        throwIfNotBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfNotBlank(CharSequence str, String messageKey, Object... params) {
+        throwIfNotBlank(str, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfEqual(Object obj1, Object obj2, String template, Object... params) {
-        throwIfEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfEqual(Object obj1, Object obj2, String messageKey, Object... params) {
+        throwIfEqual(obj1, obj2, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfNotEqual(Object obj1, Object obj2, String template, Object... params) {
-        throwIfNotEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfNotEqual(Object obj1, Object obj2, String messageKey, Object... params) {
+        throwIfNotEqual(obj1, obj2, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String template, Object... params) {
-        throwIfEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String messageKey, Object... params) {
+        throwIfEqualIgnoreCase(str1, str2, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIfNotEqualIgnoreCase(CharSequence str1, CharSequence str2, String template, Object... params) {
-        throwIfNotEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIfNotEqualIgnoreCase(CharSequence str1, CharSequence str2, String messageKey, Object... params) {
+        throwIfNotEqualIgnoreCase(str1, str2, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIf(boolean condition, String template, Object... params) {
-        throwIf(condition, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIf(boolean condition, String messageKey, Object... params) {
+        throwIf(condition, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
 
-    public static void throwIf(BooleanSupplier conditionSupplier, String template, Object... params) {
-        throwIf(conditionSupplier, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+    public static void throwIf(BooleanSupplier conditionSupplier, String messageKey, Object... params) {
+        throwIf(conditionSupplier, getMessage(messageKey, params), EXCEPTION_TYPE);
     }
-}
+}

+ 42 - 0
src/main/java/com/xs/core/config/i18n/CustomLocaleChangeInterceptor.java

@@ -0,0 +1,42 @@
+package com.xs.core.config.i18n;
+
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.support.RequestContextUtils;
+
+import java.util.Locale;
+
+/**
+ * 自定义的国际化拦截器,用于根据请求头中的lang参数来切换国际化语言
+ */
+public class CustomLocaleChangeInterceptor implements HandlerInterceptor {
+    private static final String LANG_HEADER = "lang";
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String lang = request.getHeader(LANG_HEADER);
+        if (lang != null && !lang.isEmpty()) {
+            LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
+            if (localeResolver != null) {
+                Locale locale = parseLocale(lang);
+                localeResolver.setLocale(request, response, locale);
+            }
+        }
+        return true;
+    }
+
+    private Locale parseLocale(String lang) {
+        String[] parts = lang.split("_");
+        if (parts.length == 1) {
+            return new Locale(parts[0]);
+        } else if (parts.length == 2) {
+            return new Locale(parts[0], parts[1]);
+        } else if (parts.length == 3) {
+            return new Locale(parts[0], parts[1], parts[2]);
+        }
+        return Locale.getDefault();
+    }
+}

+ 17 - 0
src/main/java/com/xs/core/config/i18n/InternationalizationConfig.java

@@ -0,0 +1,17 @@
+package com.xs.core.config.i18n;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.ResourceBundleMessageSource;
+
+@Configuration
+public class InternationalizationConfig {
+    @Bean
+    public MessageSource messageSource() {
+        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
+        messageSource.setBasename("messages/messages");
+        messageSource.setDefaultEncoding("UTF-8");
+        return messageSource;
+    }
+}

+ 19 - 0
src/main/java/com/xs/core/config/i18n/LocaleConfig.java

@@ -0,0 +1,19 @@
+package com.xs.core.config.i18n;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.i18n.SessionLocaleResolver;
+
+import java.util.Locale;
+
+@Configuration
+public class LocaleConfig {
+    @Bean
+    public LocaleResolver localeResolver() {
+        SessionLocaleResolver slr = new SessionLocaleResolver();
+//        slr.setDefaultLocale(Locale.ENGLISH);
+        slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); // 设置默认语言为简体中文
+        return slr;
+    }
+}

+ 4 - 0
src/main/java/com/xs/core/config/satoken/SaTokenConfigure.java

@@ -10,6 +10,7 @@ import com.alibaba.fastjson2.JSON;
 import com.xs.core.common.constant.ConstantConfig;
 import com.xs.core.common.content.UserContext;
 import com.xs.core.common.content.UserContextHolder;
+import com.xs.core.config.i18n.CustomLocaleChangeInterceptor;
 import com.xs.core.filter.UserContextInterceptor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Bean;
@@ -36,5 +37,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
                 .addPathPatterns("/**")
                 .excludePathPatterns("/login/coinLogin")
                 .order(-99);
+        // 注册国际化拦截器
+        registry.addInterceptor(new CustomLocaleChangeInterceptor())
+                .order(-98);
     }
 }

+ 20 - 4
src/main/java/com/xs/core/controller/competition/CompetitionController.java

@@ -1,7 +1,11 @@
 package com.xs.core.controller.competition;
 
+import com.xs.core.common.content.UserContext;
+import com.xs.core.common.content.UserContextHolder;
 import com.xs.core.model.ResponseResult;
 import com.xs.core.model.user.req.GoldCoinCompetitionRankingReq;
+import com.xs.core.model.user.resp.GoldCoinLeaderboardInfo;
+import com.xs.core.model.user.resp.TeamCompetitionResp;
 import com.xs.core.model.user.resp.UserCompetitionResp;
 import com.xs.core.service.competition.CompetitionService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -13,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
 
 @RestController
 @RequestMapping("/competition")
@@ -21,10 +26,21 @@ public class CompetitionController {
     @Autowired
     private CompetitionService competitionService;
 
-    @Operation(summary = "金币排行榜", description = "金币排行榜")
+    @Operation(summary = "个人金币排行榜", description = "个人金币排行榜")
     @PostMapping("/goldCoinCompetitionRanking")
-    public ResponseResult<List<UserCompetitionResp>> goldCoinCompetitionRanking(@RequestBody GoldCoinCompetitionRankingReq query) {
-        List<UserCompetitionResp> userCompetitionRespList = competitionService.goldCoinCompetitionRanking(query.getLimit(), query.getInvitees());
-        return ResponseResult.success(userCompetitionRespList);
+    public ResponseResult<GoldCoinLeaderboardInfo> goldCoinCompetitionRanking(@RequestBody GoldCoinCompetitionRankingReq query) {
+        UserContext userContext = UserContextHolder.getContext();
+        CompletableFuture<List<UserCompetitionResp>> listCompletableFuture = CompletableFuture.supplyAsync(() -> competitionService.individualGoldCoinCompetitionRanking(query.getLimit()));
+        CompletableFuture<UserCompetitionResp> userCompetitionRespCompletableFuture = CompletableFuture.supplyAsync(() -> competitionService.getIndividualGoldCoinCompetitionInfo(userContext.getId()));
+        CompletableFuture.allOf(listCompletableFuture, userCompetitionRespCompletableFuture);
+        GoldCoinLeaderboardInfo goldCoinLeaderboardInfo = new GoldCoinLeaderboardInfo(listCompletableFuture.join(), userCompetitionRespCompletableFuture.join());
+        return ResponseResult.success(goldCoinLeaderboardInfo);
+    }
+
+    @Operation(summary = "团队邀请排行榜", description = "团队邀请排行榜")
+    @PostMapping("/teamInviteCompetitionRanking")
+    public ResponseResult<List<TeamCompetitionResp>> teamInviteCompetitionRanking(@RequestBody GoldCoinCompetitionRankingReq query) {
+        List<TeamCompetitionResp> teamCompetitionResp = competitionService.teamInviteCompetitionRanking(query.getLimit(), 500);
+        return ResponseResult.success(teamCompetitionResp);
     }
 }

+ 25 - 0
src/main/java/com/xs/core/controller/team/DistributionController.java

@@ -0,0 +1,25 @@
+package com.xs.core.controller.team;
+
+import com.xs.core.model.ResponseResult;
+import com.xs.core.service.user.IUserService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/distribution")
+@Tag(name = "团队邀请模块", description = "团队邀请模块")
+@AllArgsConstructor
+public class DistributionController {
+    private final IUserService userService;
+
+    @PostMapping("/getShareCode")
+    @Operation(summary = "获取邀请码", description = "获取邀请码")
+    public ResponseResult<?> getShareCode() {
+        String inviteCode = userService.getInviteCode();
+        return ResponseResult.success(inviteCode);
+    }
+}

+ 56 - 2
src/main/java/com/xs/core/controller/team/TeamShareController.java

@@ -3,16 +3,22 @@ package com.xs.core.controller.team;
 import com.xs.core.common.content.UserContext;
 import com.xs.core.common.content.UserContextHolder;
 import com.xs.core.model.ResponseResult;
-import com.xs.core.model.team.resp.TeamsInfoResp;
+import com.xs.core.model.team.req.ClaimTeamInviteTaskRewardReq;
+import com.xs.core.model.team.req.TeamShareReq;
+import com.xs.core.model.team.resp.*;
 import com.xs.core.service.team.TeamShareService;
 import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 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.List;
+
 @RestController
 @RequestMapping("/team/share")
 @Tag(name = "团队邀请", description = "团队邀请")
@@ -27,4 +33,52 @@ public class TeamShareController {
         TeamsInfoResp teamsInfoResp = teamShareService.getMyTeamsInfo(userContext.getId());
         return ResponseResult.success(teamsInfoResp);
     }
+
+    @Operation(summary = "获取团队产出奖励信息", description = "获取团队产出奖励信息")
+    @PostMapping("/getTeamShareUserListByType")
+    public ResponseResult<TeamShareInfoResp> getTeamShareUserListByType(@Validated @RequestBody TeamShareReq req) {
+        UserContext userContext = UserContextHolder.getContext();
+        TeamShareInfoResp teamShareUserInfo = teamShareService.getTeamShareUserInfoByType(userContext.getId(), req);
+        return ResponseResult.success(teamShareUserInfo);
+    }
+
+    @Operation(summary = "收取团队奖励", description = "收取团队奖励")
+    @PostMapping("/claimTeamShareReward")
+    public ResponseResult<List<TeamShareUserResp>> claimTeamShareReward(@Validated @RequestBody TeamShareReq req) {
+        UserContext userContext = UserContextHolder.getContext();
+        teamShareService.claimTeamShareReward(userContext.getId(), req);
+        return ResponseResult.success();
+    }
+
+    @Operation(summary = "获取团队邀请固定奖励列表", description = "获取团队邀请固定奖励列表(邀请一人奖励1000金币)")
+    @PostMapping("/getTeamInviteRewardsList")
+    public ResponseResult<List<TeamInviteRewardsResp>> getTeamInviteRewardsList() {
+        UserContext userContext = UserContextHolder.getContext();
+        List<TeamInviteRewardsResp> teamInviteRewardsList = teamShareService.getTeamInviteRewardsList(userContext.getId());
+        return ResponseResult.success(teamInviteRewardsList);
+    }
+
+    @Operation(summary = "收取团队邀请固定奖励", description = "收取团队邀请固定奖励")
+    @PostMapping("/claimTeamInviteReward")
+    public ResponseResult<?> claimTeamInviteReward() {
+        UserContext userContext = UserContextHolder.getContext();
+        teamShareService.claimTeamInviteReward(userContext.getId());
+        return ResponseResult.success();
+    }
+
+    @Operation(summary = "获取团队邀请任务可领取奖励", description = "获取团队邀请任务可领取奖励")
+    @PostMapping("/getTeamInviteRewardsRules")
+    public ResponseResult<TeamInviteAvailableRewardsResp> getTeamInviteAvailableRewardsRules() {
+        UserContext userContext = UserContextHolder.getContext();
+        TeamInviteAvailableRewardsResp resp = teamShareService.getTeamInviteAvailableRewardsRules(userContext.getId());
+        return ResponseResult.success(resp);
+    }
+
+    @Operation(summary = "领取团队邀请任务奖励", description = "领取团队邀请任务奖励")
+    @PostMapping("/claimTeamInviteTaskReward")
+    public ResponseResult<?> claimTeamInviteTaskReward(@RequestBody ClaimTeamInviteTaskRewardReq req) {
+        UserContext userContext = UserContextHolder.getContext();
+        teamShareService.claimTeamInviteTaskReward(userContext.getId(), req.getRuleId());
+        return ResponseResult.success();
+    }
 }

+ 19 - 0
src/main/java/com/xs/core/controller/user/LoginController.java

@@ -1,26 +1,45 @@
 package com.xs.core.controller.user;
 
+import cn.dev33.satoken.annotation.SaIgnore;
 import cn.dev33.satoken.stp.SaTokenInfo;
 import com.xs.core.model.ResponseResult;
 import com.xs.core.model.user.req.AppLoginReq;
 import com.xs.core.service.user.AppLoginService;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.Locale;
+
 @Slf4j
 @RestController
 @RequestMapping("/login")
+@Tag(name = "登录", description = "登录")
 public class LoginController {
     @Autowired
     private AppLoginService loginService;
 
+    @Autowired
+    private MessageSource messageSource;
+
     @PostMapping("/coinLogin")
     @Operation(summary = "tg账号登录", description = "根据tg用户id进行登录")
     public ResponseResult<SaTokenInfo> coinLogin(@Validated @RequestBody AppLoginReq req) {
         SaTokenInfo saTokenInfo = loginService.tgLogin(req);
         return ResponseResult.success(saTokenInfo);
     }
+
+    @PostMapping("/testI18n")
+    @Operation(summary = "测试国际化", description = "测试国际化")
+    @SaIgnore
+    public ResponseResult<?> testI18n() {
+        Locale locale = LocaleContextHolder.getLocale();
+        String message = messageSource.getMessage("error.exists", null, locale);
+        return ResponseResult.success(message, "error.exists", null);
+    }
 }

+ 29 - 0
src/main/java/com/xs/core/controller/wallet/WalletController.java

@@ -0,0 +1,29 @@
+package com.xs.core.controller.wallet;
+
+import com.xs.core.common.content.UserContext;
+import com.xs.core.common.content.UserContextHolder;
+import com.xs.core.model.ResponseResult;
+import com.xs.core.model.wallet.resp.WalletInfoResp;
+import com.xs.core.service.wallet.WalletService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/wallet")
+@Tag(name = "钱包", description = "钱包")
+public class WalletController {
+
+    @Autowired
+    private WalletService walletService;
+
+    @Operation(summary = "获取钱包信息", description = "获取钱包信息")
+    @PostMapping("/getWalletInfo")
+    public ResponseResult<WalletInfoResp> getWalletInfo() {
+        WalletInfoResp walletInfo = walletService.getWalletInfo();
+        return ResponseResult.success(walletInfo);
+    }
+}

+ 11 - 7
src/main/java/com/xs/core/filter/GlobalExceptionHandler.java

@@ -10,6 +10,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.ConstraintViolationException;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.NoSuchMessageException;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.util.CollectionUtils;
 import org.springframework.validation.BindException;
@@ -24,7 +25,9 @@ import org.springframework.web.multipart.MaxUploadSizeExceededException;
 import org.springframework.web.multipart.support.MissingServletRequestPartException;
 import org.springframework.web.servlet.resource.NoResourceFoundException;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @RestControllerAdvice
 @Slf4j
@@ -32,7 +35,7 @@ public class GlobalExceptionHandler {
     @ExceptionHandler({Exception.class, NotLoginException.class, HttpMessageNotReadableException.class,
             HttpMediaTypeNotSupportedException.class, MaxUploadSizeExceededException.class, NotPermissionException.class,
             HttpRequestMethodNotSupportedException.class, MethodArgumentNotValidException.class, BindException.class,
-            ConstraintViolationException.class, MissingServletRequestPartException.class, SaJwtException.class, NoResourceFoundException.class})
+            ConstraintViolationException.class, MissingServletRequestPartException.class, SaJwtException.class, NoResourceFoundException.class, BusinessException.class})
     @ResponseBody
     ResponseResult<?> serverExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
         if (e instanceof NotLoginException || e instanceof SaJwtException) {
@@ -46,26 +49,27 @@ public class GlobalExceptionHandler {
         } else if (e instanceof NotPermissionException) {
             return ResponseResult.forbidden();
         } else if (e instanceof BindException) {
-            StringBuilder message = new StringBuilder();
             List<FieldError> fieldErrorList = ((BindException) e).getFieldErrors();
             if (!CollectionUtils.isEmpty(fieldErrorList)) {
                 for (FieldError fieldError : fieldErrorList) {
                     if (fieldError != null && fieldError.getDefaultMessage() != null) {
-                        message.append(fieldError.getDefaultMessage()).append(" ");
+                        String field = fieldError.getField();
+                        String defaultMessage = fieldError.getDefaultMessage();
+                        // 尝试从消息源获取国际化消息,如果没有找到,则使用默认消息
+                        return ResponseResult.failed(defaultMessage);
                     }
                 }
             }
-            log.error(message.toString());
-            return ResponseResult.failed(message.toString());
+            return ResponseResult.failed("response.validation.failed");
         } else if (e instanceof ConstraintViolationException) {
             return ResponseResult.badRequest(e.getMessage());
         } else if (e instanceof BusinessException) {
             //增加自定义内容响应
             return ResponseResult.failed(e.getMessage());
         } else if (e instanceof MissingServletRequestPartException) {
-            return ResponseResult.failed("预期的文件缺失");
+            return ResponseResult.failed("response.expected.file.missing");
         } else if (e instanceof NoResourceFoundException) {
-            return ResponseResult.failed("请求的接口不存在");
+            return ResponseResult.failed("request.api.not.exists");
         } else {
             log.error("请求:{} 异常,异常信息:{}", request.getRequestURI(), ExceptionUtil.stacktraceToString(e));
             return ResponseResult.serverError(null);

+ 2 - 2
src/main/java/com/xs/core/filter/RequestHandler.java

@@ -220,14 +220,14 @@ public class RequestHandler implements Filter {
 
     private byte[] _getErrorBytes() {
         if (isEncryption) {
-            String result = SecurityUtil.encryptSM2(responsePublicKey, JSON.toJSONString(ResponseResult.failed("error!")));
+            String result = SecurityUtil.encryptSM2(responsePublicKey, JSON.toJSONString(ResponseResult.failed("response.error.encrypt")));
             String sign = SecurityUtil.getSign(result);
             JSONObject jsonObject = new JSONObject();
             jsonObject.put("encryptData", result);
             jsonObject.put("identifying", sign);
             return jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8);
         } else {
-            return JSON.toJSONString(ResponseResult.failed("error!")).getBytes(StandardCharsets.UTF_8);
+            return JSON.toJSONString(ResponseResult.failed("response.error.encrypt")).getBytes(StandardCharsets.UTF_8);
         }
     }
 

+ 23 - 0
src/main/java/com/xs/core/init/MessageSourceInitializer.java

@@ -0,0 +1,23 @@
+package com.xs.core.init;
+
+import com.xs.core.common.validation.CheckUtils;
+import com.xs.core.model.ResponseResult;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.MessageSource;
+import org.springframework.stereotype.Component;
+
+@Component
+@AllArgsConstructor
+@Slf4j
+public class MessageSourceInitializer implements CommandLineRunner {
+    private final MessageSource messageSource;
+
+    @Override
+    public void run(String... args) throws Exception {
+        ResponseResult.setMessageSource(messageSource);
+        CheckUtils.setMessageSource(messageSource);
+        log.info("MessageSource initialized successfully.");
+    }
+}

+ 16 - 0
src/main/java/com/xs/core/mapper/team/InviteRewardsRuleMapper.java

@@ -0,0 +1,16 @@
+package com.xs.core.mapper.team;
+
+import com.xs.core.model.team.entity.InviteRewardsRule;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励规则 Mapper 接口
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+public interface InviteRewardsRuleMapper extends BaseMapper<InviteRewardsRule> {
+
+}

+ 16 - 0
src/main/java/com/xs/core/mapper/team/InviteTaskRewardsFetchRecordMapper.java

@@ -0,0 +1,16 @@
+package com.xs.core.mapper.team;
+
+import com.xs.core.model.team.entity.InviteTaskRewardsFetchRecord;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励领取记录 Mapper 接口
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+public interface InviteTaskRewardsFetchRecordMapper extends BaseMapper<InviteTaskRewardsFetchRecord> {
+
+}

+ 10 - 0
src/main/java/com/xs/core/mapper/team/TeamInviteRewardRecordMapper.java

@@ -2,6 +2,9 @@ package com.xs.core.mapper.team;
 
 import com.xs.core.model.team.entity.TeamInviteRewardRecord;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.xs.core.model.team.resp.TeamInviteRewardsResp;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +16,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface TeamInviteRewardRecordMapper extends BaseMapper<TeamInviteRewardRecord> {
 
+    /**
+     * 获取团队邀请奖励列表
+     *
+     * @param id
+     * @return
+     */
+    List<TeamInviteRewardsResp> getTeamInviteRewardsRespList(Long id);
 }

+ 5 - 0
src/main/java/com/xs/core/mapper/team/TeamShareRewardRecordMapper.java

@@ -2,6 +2,10 @@ package com.xs.core.mapper.team;
 
 import com.xs.core.model.team.entity.TeamShareRewardRecord;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.xs.core.model.team.resp.TeamShareUserResp;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +17,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface TeamShareRewardRecordMapper extends BaseMapper<TeamShareRewardRecord> {
 
+    List<TeamShareUserResp> getTeamShareUserInfoByType(@Param("userId") Long userId, @Param("type") String type);
 }

+ 22 - 6
src/main/java/com/xs/core/mapper/user/TgUserMapper.java → src/main/java/com/xs/core/mapper/user/UserMapper.java

@@ -1,12 +1,12 @@
 package com.xs.core.mapper.user;
 
 import com.xs.core.model.team.resp.TeamUserResp;
-import com.xs.core.model.user.entity.TgUser;
+import com.xs.core.model.user.entity.User;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.xs.core.model.user.resp.TeamCompetitionResp;
 import com.xs.core.model.user.resp.UserCompetitionResp;
 import org.apache.ibatis.annotations.Param;
 
-import java.io.Serial;
 import java.util.List;
 
 /**
@@ -15,9 +15,9 @@ import java.util.List;
  * </p>
  *
  * @author xudm
- * @since 2024-12-18
+ * @since 2024-12-22
  */
-public interface TgUserMapper extends BaseMapper<TgUser> {
+public interface UserMapper extends BaseMapper<User> {
     /**
      * 查询我的团队列表
      *
@@ -41,7 +41,14 @@ public interface TgUserMapper extends BaseMapper<TgUser> {
      *
      * @return 用户金币排行信息
      */
-    List<UserCompetitionResp> goldCoinCompetitionRanking(@Param("limit") int limit, @Param("invitees") int invitees);
+    List<UserCompetitionResp> individualGoldCoinCompetitionRanking(@Param("limit") int limit);
+
+    /**
+     * 金币排行榜
+     *
+     * @return 用户金币排行个人信息
+     */
+    UserCompetitionResp getIndividualGoldCoinCompetitionInfo(@Param("userId") Long userId);
 
     /**
      * 查询我的邀请人
@@ -49,5 +56,14 @@ public interface TgUserMapper extends BaseMapper<TgUser> {
      * @param userId
      * @return
      */
-    TgUser getInviterByUserId(Long userId);
+    User getInviterByUserId(Long userId);
+
+    /**
+     * 团队邀请排行榜
+     *
+     * @param limit   限制
+     * @param invitee 最少邀请人数
+     * @return
+     */
+    List<TeamCompetitionResp> teamInviteCompetitionRanking(@Param("limit") int limit, @Param("invitee") int invitee);
 }

+ 16 - 0
src/main/java/com/xs/core/mapper/wallet/UserWalletMapper.java

@@ -0,0 +1,16 @@
+package com.xs.core.mapper.wallet;
+
+import com.xs.core.model.wallet.entity.UserWallet;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 用户钱包 Mapper 接口
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-22
+ */
+public interface UserWalletMapper extends BaseMapper<UserWallet> {
+
+}

+ 62 - 105
src/main/java/com/xs/core/model/ResponseResult.java

@@ -1,168 +1,125 @@
 package com.xs.core.model;
 
 import lombok.Data;
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
 
 import java.io.Serializable;
+import java.util.Locale;
 
-/**
- * @author zxw
- * 通用返回对象
- */
 @Data
 public class ResponseResult<T> implements Serializable {
     public static final long serialVersionUID = 42L;
 
     private Integer code;
-
     private String msg;
-
     private T data;
 
+    private static MessageSource messageSource;
+
+    public static void setMessageSource(MessageSource source) {
+        messageSource = source;
+    }
+
+    public static String getMessage(String key, Object... args) {
+        Locale locale = LocaleContextHolder.getLocale();
+        return messageSource.getMessage(key, args, locale);
+    }
+
     protected ResponseResult() {
+    }
 
+    protected ResponseResult(IErrorCode errorCode, String msg) {
+        this.code = errorCode.getCode();
+        this.msg = msg;
     }
 
-    protected ResponseResult(Integer code, String msg, T data) {
+    protected ResponseResult(Integer code, String msgKey, Object... args) {
         this.code = code;
-        this.msg = msg;
+        this.msg = getMessage(msgKey, args);
+    }
+
+    protected ResponseResult(Integer code, String msgKey, T data, Object... args) {
+        this.code = code;
+        this.msg = getMessage(msgKey, args);
         this.data = data;
     }
 
-    protected ResponseResult(Integer code, String msg) {
+    protected ResponseResult(Integer code, String msgKey, T data) {
         this.code = code;
-        this.msg = msg;
+        this.msg = getMessage(msgKey);
+        this.data = data;
     }
 
-    /**
-     * 成功返回结果
-     */
     public static <T> ResponseResult<T> success() {
-        return new ResponseResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg());
+        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), "response.success", null);
     }
 
-    /**
-     * 成功返回结果
-     *
-     * @param data 获取的数据
-     */
     public static <T> ResponseResult<T> success(T data) {
-        return new ResponseResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), data);
-    }
-
-    /**
-     * 成功返回结果
-     *
-     * @param data 获取的数据
-     * @param msg  提示信息
-     */
-    public static <T> ResponseResult<T> success(T data, String msg) {
-        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), msg, data);
-    }
-
-    /**
-     * 成功返回结果
-     *
-     * @param msg 提示信息
-     */
-    public static <T> ResponseResult<T> success(String msg) {
-        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), msg);
-    }
-
-    /**
-     * 失败返回结果
-     *
-     * @param errorCode 错误码
-     */
-    public static <T> ResponseResult<T> failed(IErrorCode errorCode) {
-        return new ResponseResult<>(errorCode.getCode(), errorCode.getMsg(), null);
-    }
-
-    /**
-     * 失败返回结果
-     *
-     * @param errorCode 错误码
-     * @param msg       错误信息
-     */
-    public static <T> ResponseResult<T> failed(IErrorCode errorCode, String msg) {
-        return new ResponseResult<>(errorCode.getCode(), msg, null);
-    }
-
-    /**
-     * 失败返回结果
-     *
-     * @param msg 提示信息
-     */
+        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), "response.success", data);
+    }
+
+    public static <T> ResponseResult<T> success(T data, String msgKey) {
+        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), msgKey, data);
+    }
+
+    public static <T> ResponseResult<T> success(T data, String msgKey, Object... args) {
+        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), msgKey, data, args);
+    }
+
+    public static <T> ResponseResult<T> success(String msgKey, Object... args) {
+        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), msgKey, args);
+    }
+
+    public static <T> ResponseResult<T> failed(IErrorCode errorCode, String msgKey, Object... args) {
+        return new ResponseResult<>(errorCode.getCode(), msgKey, args);
+    }
+
+    public static <T> ResponseResult<T> failed(String msgKey, Object... args) {
+        return new ResponseResult<>(ResultCode.FAILED.getCode(), msgKey, args);
+    }
+
     public static <T> ResponseResult<T> failed(String msg) {
-        return new ResponseResult<>(ResultCode.FAILED.getCode(), msg, null);
+        return new ResponseResult<>(ResultCode.FAILED, msg);
     }
 
-    /**
-     * 失败返回结果
-     */
     public static <T> ResponseResult<T> failed() {
-        return failed(ResultCode.FAILED);
+        return failed(ResultCode.FAILED, "response.failed");
     }
 
-    /**
-     * 未登录返回结果
-     */
     public static <T> ResponseResult<T> unauthorized(T data) {
-        return new ResponseResult<>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMsg(), data);
+        return new ResponseResult<>(ResultCode.UNAUTHORIZED.getCode(), "response.unauthorized", data);
     }
 
-    /**
-     * 未授权返回结果
-     */
     public static <T> ResponseResult<T> forbidden(T data) {
-        return new ResponseResult<>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMsg(), data);
+        return new ResponseResult<>(ResultCode.FORBIDDEN.getCode(), "response.forbidden", data);
     }
 
-    /**
-     * 未授权返回结果
-     */
     public static <T> ResponseResult<T> forbidden() {
         return forbidden(null);
     }
 
-    /**
-     * 请求类型或参数错误
-     */
     public static <T> ResponseResult<T> badRequest(T data) {
-        return new ResponseResult<>(ResultCode.BAD_REQUEST.getCode(), ResultCode.BAD_REQUEST.getMsg(), data);
+        return new ResponseResult<>(ResultCode.BAD_REQUEST.getCode(), "response.bad.request", data);
     }
 
-    /**
-     * 请求类型或参数错误
-     */
     public static <T> ResponseResult<T> badRequest() {
         return badRequest(null);
     }
 
-    /**
-     * 请求报文过大
-     */
     public static <T> ResponseResult<T> requestTooLarge(T data) {
-        return new ResponseResult<>(ResultCode.REQUEST_ENTITY_TOO_LARGE.getCode(), ResultCode.REQUEST_ENTITY_TOO_LARGE.getMsg(), data);
+        return new ResponseResult<>(ResultCode.REQUEST_ENTITY_TOO_LARGE.getCode(), "response.request.too.large", data);
     }
 
-    /**
-     * 请求报文过大
-     */
     public static <T> ResponseResult<T> unsupportedMediaType(T data) {
-        return new ResponseResult<>(ResultCode.UNSUPPORTED_MEDIA_TYPE.getCode(), ResultCode.UNSUPPORTED_MEDIA_TYPE.getMsg(), data);
+        return new ResponseResult<>(ResultCode.UNSUPPORTED_MEDIA_TYPE.getCode(), "response.unsupported.media.type", data);
     }
 
-    /**
-     * 服务器未知错误
-     */
     public static <T> ResponseResult<T> serverError(T data) {
-        return new ResponseResult<>(ResultCode.SERVER_ERROR.getCode(), ResultCode.SERVER_ERROR.getMsg(), data);
+        return new ResponseResult<>(ResultCode.SERVER_ERROR.getCode(), "response.server.error", data);
     }
 
-    /**
-     * 接口未找到
-     */
     public static <T> ResponseResult<T> notFound() {
-        return new ResponseResult<>(ResultCode.NOT_FOUND.getCode(), ResultCode.NOT_FOUND.getMsg());
+        return new ResponseResult<>(ResultCode.NOT_FOUND.getCode(), "response.not.found", null);
     }
-}
+}

+ 90 - 0
src/main/java/com/xs/core/model/team/entity/InviteRewardsRule.java

@@ -0,0 +1,90 @@
+package com.xs.core.model.team.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励规则
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+@Getter
+@Setter
+@TableName("b_invite_rewards_rule")
+public class InviteRewardsRule extends Model<InviteRewardsRule> {
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 奖励名称
+     */
+    @TableField("rewards_name")
+    private String rewardsName;
+
+    /**
+     * 奖励说明
+     */
+    @TableField("rewards_describe")
+    private String rewardsDescribe;
+
+    /**
+     * 邀请好友数量
+     */
+    @TableField("invite_num")
+    private Integer inviteNum;
+
+    /**
+     * 金币数量
+     */
+    @TableField("gold_coin_num")
+    private Integer goldCoinNum;
+
+    /**
+     * 奖励层级
+     */
+    @TableField("rewards_level")
+    private Integer rewardsLevel;
+
+    /**
+     * 创建人
+     */
+    @TableField("created_by")
+    private Long createdBy;
+
+    /**
+     * 创建时间
+     */
+    @TableField("created_time")
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    @TableField("updated_by")
+    private Long updatedBy;
+
+    /**
+     * 更新时间
+     */
+    @TableField("updated_time")
+    private LocalDateTime updatedTime;
+
+    @Override
+    public Serializable pkVal() {
+        return this.id;
+    }
+}

+ 76 - 0
src/main/java/com/xs/core/model/team/entity/InviteTaskRewardsFetchRecord.java

@@ -0,0 +1,76 @@
+package com.xs.core.model.team.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励领取记录
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+@Getter
+@Setter
+@TableName("b_invite_task_rewards_fetch_record")
+@NoArgsConstructor
+public class InviteTaskRewardsFetchRecord extends Model<InviteTaskRewardsFetchRecord> {
+
+    public InviteTaskRewardsFetchRecord(Integer rewardsLevel, Integer goldCoinNum) {
+        this.rewardsLevel = rewardsLevel;
+        this.goldCoinNum = goldCoinNum;
+    }
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 领取人id
+     */
+    @TableField("user_id")
+    private Long userId;
+
+    /**
+     * 规则id
+     */
+    @TableField("rule_id")
+    private Long ruleId;
+
+    /**
+     * 奖励层级
+     */
+    @TableField("rewards_level")
+    private Integer rewardsLevel;
+
+    /**
+     * 金币数量
+     */
+    @TableField("gold_coin_num")
+    private Integer goldCoinNum;
+
+    /**
+     * 创建时间
+     */
+    @TableField("created_time")
+    private LocalDateTime createdTime;
+
+    @Override
+    public Serializable pkVal() {
+        return this.id;
+    }
+}

+ 16 - 0
src/main/java/com/xs/core/model/team/req/ClaimTeamInviteTaskRewardReq.java

@@ -0,0 +1,16 @@
+package com.xs.core.model.team.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@Schema(description = "领取团队邀请任务奖励请求体")
+public class ClaimTeamInviteTaskRewardReq implements Serializable {
+
+    @Schema(description = "任务规则id")
+    @NotBlank(message = "validation.team.claim.ruleId")
+    private String ruleId;
+}

+ 15 - 0
src/main/java/com/xs/core/model/team/req/TeamShareReq.java

@@ -0,0 +1,15 @@
+package com.xs.core.model.team.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@Schema(name = "TeamShareReq", description = "团队产出请求参数")
+public class TeamShareReq implements Serializable {
+    @Schema(description = "团队类型")
+    @NotBlank(message = "validation.team.type")
+    private String type;
+}

+ 28 - 0
src/main/java/com/xs/core/model/team/resp/TeamInviteAvailableRewardsResp.java

@@ -0,0 +1,28 @@
+package com.xs.core.model.team.resp;
+
+import com.xs.core.model.team.entity.InviteRewardsRule;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Schema(description = "团队邀请奖励列表")
+public class TeamInviteAvailableRewardsResp implements Serializable {
+    /**
+     * 邀请人数
+     */
+    @Schema(description = "邀请人数")
+    private Long inviteNum;
+    /**
+     * 邀请奖励规则
+     */
+    @Schema(description = "邀请奖励规则")
+    List<InviteRewardsRule> inviteRewardsRuleList;
+
+    public TeamInviteAvailableRewardsResp(Long inviteNum, List<InviteRewardsRule> inviteRewardsRuleList) {
+        this.inviteNum = inviteNum;
+        this.inviteRewardsRuleList = inviteRewardsRuleList;
+    }
+}

+ 72 - 0
src/main/java/com/xs/core/model/team/resp/TeamInviteRewardsResp.java

@@ -0,0 +1,72 @@
+package com.xs.core.model.team.resp;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Objects;
+
+@Data
+@Schema(description = "团队邀请奖励列表")
+public class TeamInviteRewardsResp implements Serializable {
+
+    /**
+     * tg账号
+     */
+    @Schema(description = "tg账号")
+    private String tgAccount;
+
+    /**
+     * 名
+     */
+    @Schema(description = "名")
+    private String firstName;
+
+    /**
+     * 姓
+     */
+    @Schema(description = "姓")
+    private String lastName;
+
+    /**
+     * 昵称
+     */
+    @Schema(description = "昵称")
+    private String nickname;
+
+    /**
+     * 真实姓名
+     */
+    @Schema(description = "真实姓名")
+    private String realName;
+
+    /**
+     * 头像
+     */
+    @Schema(description = "头像")
+    private String avatar;
+
+
+    /**
+     * 团队产出奖励金额
+     */
+    @Schema(description = "团队产出奖励金额")
+    private BigDecimal inviteRewardAmount;
+
+
+    /**
+     * tg账号脱敏处理
+     */
+    public String getTgAccount() {
+        if (Objects.isNull(tgAccount)) {
+            return "";
+        }
+        //脱敏处理 后几位用*号代替
+        if (tgAccount.length() > 3) {
+            tgAccount = tgAccount.substring(0, tgAccount.length() - 3) + "***";
+        }
+        return tgAccount;
+    }
+
+}

+ 18 - 0
src/main/java/com/xs/core/model/team/resp/TeamShareInfoResp.java

@@ -0,0 +1,18 @@
+package com.xs.core.model.team.resp;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Schema(description = "团队用户信息")
+@Data
+public class TeamShareInfoResp implements Serializable {
+    @Schema(description = "可领取的团队总产出")
+    private BigDecimal total;
+
+    @Schema(description = "团队用户信息")
+    private List<TeamShareUserResp> users;
+}

+ 83 - 0
src/main/java/com/xs/core/model/team/resp/TeamShareUserResp.java

@@ -0,0 +1,83 @@
+package com.xs.core.model.team.resp;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Objects;
+
+@Schema(description = "团队产出用户信息")
+@Data
+public class TeamShareUserResp implements Serializable {
+    /**
+     * tg小程序id
+     */
+    @Schema(description = "tg小程序id")
+    private String tgId;
+
+    /**
+     * tg账号
+     */
+    @Schema(description = "tg账号")
+    private String tgAccount;
+
+    /**
+     * 名
+     */
+    @Schema(description = "名")
+    private String firstName;
+
+    /**
+     * 姓
+     */
+    @Schema(description = "姓")
+    private String lastName;
+
+    /**
+     * 昵称
+     */
+    @Schema(description = "昵称")
+    private String nickname;
+
+    /**
+     * 真实姓名
+     */
+    @Schema(description = "真实姓名")
+    private String realName;
+
+    /**
+     * 头像
+     */
+    @Schema(description = "头像")
+    private String avatar;
+
+    /**
+     * 团队类型
+     */
+    @Schema(description = "团队类型")
+    private String teamType;
+
+    /**
+     * 团队产出奖励金额
+     */
+    @Schema(description = "团队产出奖励金额")
+    private BigDecimal teamRewardAmount;
+
+    /**
+     * 被邀请人总产出金额
+     */
+    @Schema(description = "被邀请人总产出金额")
+    private BigDecimal userYieldTotal;
+
+    public String getTgAccount() {
+        if (Objects.isNull(tgAccount)) {
+            return "";
+        }
+        //脱敏处理 后几位用*号代替
+        if (tgAccount.length() > 3) {
+            tgAccount = tgAccount.substring(0, tgAccount.length() - 3) + "***";
+        }
+        return tgAccount;
+    }
+}

+ 0 - 7
src/main/java/com/xs/core/model/team/resp/TeamUserResp.java

@@ -56,13 +56,6 @@ public class TeamUserResp implements Serializable {
     @Schema(description = "头像")
     private String avatar;
 
-
-    /**
-     * 客流途径 1 新增 2 邀请
-     */
-    @Schema(description = "流途径 1 新增 2 邀请")
-    private Integer passengerFlowWay;
-
     /**
      * 团队类型
      */

+ 47 - 3
src/main/java/com/xs/core/model/team/resp/TeamsInfoResp.java

@@ -1,21 +1,65 @@
 package com.xs.core.model.team.resp;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import com.xs.core.model.team.entity.TeamInviteConfig;
+import com.xs.core.model.team.entity.TeamShareRewardRecord;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
 
 @Data
 @Schema(name = "TeamsInfoResp", description = "团队信息")
 public class TeamsInfoResp implements Serializable {
     @Schema(description = "总人数")
-    private int total;
+    private Long total = 0L;
+    @Schema(description = "可领取奖励人数")
+    private Long rewardsTotal = 0L;
     @Schema(description = "团队A人数")
-    private Long teamACount;
+    private Long teamACount = 0L;
     @Schema(description = "团队B人数")
-    private Long teamBCount;
+    private Long teamBCount = 0L;
     @Schema(description = "团队A金币抽成占比")
     private String proportionA;
     @Schema(description = "团队B金币抽成占比")
     private String proportionB;
+
+    @Schema(description = "团队A可领取奖励人数")
+    private Long claimableRewardsA = 0L;
+    @Schema(description = "团队B可领取奖励人数")
+    private Long claimableRewardsB = 0L;
+
+    @Schema(description = "团队A可领取奖励金额")
+    private BigDecimal claimableAmountA = BigDecimal.ZERO;
+
+    @Schema(description = "团队B可领取奖励金额")
+    private BigDecimal claimableAmountB = BigDecimal.ZERO;
+
+    public TeamsInfoResp(List<TeamInviteRewardsResp> inviteRewardsRespList, List<TeamUserResp> teamUserList, List<TeamShareRewardRecord> shareRewardRecordsA, List<TeamShareRewardRecord> shareRewardRecordsB, TeamInviteConfig config) {
+        if (ObjUtil.isNotNull(config)) {
+            this.proportionA = config.getTeamAScale();
+            this.proportionB = config.getTeamBScale();
+        }
+        if (CollUtil.isNotEmpty(teamUserList)) {
+            long countA = teamUserList.stream().filter(t -> "A".equals(t.getTeamType())).count();
+            long countB = teamUserList.stream().filter(t -> "B".equals(t.getTeamType())).count();
+            this.total = countA;
+            this.teamACount = countA;
+            this.teamBCount = countB;
+        }
+        if (CollUtil.isNotEmpty(inviteRewardsRespList)) {
+            this.rewardsTotal = (long) inviteRewardsRespList.size();
+        }
+        if (CollUtil.isNotEmpty(shareRewardRecordsA)) {
+            this.claimableRewardsA = (long) shareRewardRecordsA.size();
+            this.claimableAmountA = shareRewardRecordsA.stream().map(TeamShareRewardRecord::getRewardAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+        }
+        if (CollUtil.isNotEmpty(shareRewardRecordsB)) {
+            this.claimableRewardsB = (long) shareRewardRecordsB.size();
+            this.claimableAmountB = shareRewardRecordsB.stream().map(TeamShareRewardRecord::getRewardAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+        }
+    }
 }

+ 4 - 29
src/main/java/com/xs/core/model/user/entity/TgUser.java → src/main/java/com/xs/core/model/user/entity/User.java

@@ -7,7 +7,6 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
 
 import java.io.Serializable;
-import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 import lombok.Builder;
@@ -20,13 +19,13 @@ import lombok.Setter;
  * </p>
  *
  * @author xudm
- * @since 2024-12-18
+ * @since 2024-12-22
  */
 @Getter
 @Setter
-@TableName("b_tg_user")
+@TableName("b_user")
 @Builder
-public class TgUser extends Model<TgUser> {
+public class User extends Model<User> {
 
     /**
      * id
@@ -95,7 +94,7 @@ public class TgUser extends Model<TgUser> {
     private Integer ageLimit;
 
     /**
-     * 是否老用户[0=新用户, 1=老用户]
+     * 是否老用户
      */
     @TableField("old_user")
     private Integer oldUser;
@@ -184,36 +183,12 @@ public class TgUser extends Model<TgUser> {
     @TableField("referrer_id")
     private String referrerId;
 
-    /**
-     * 空投数量
-     */
-    @TableField("airdrop_coin")
-    private Integer airdropCoin;
-
-    /**
-     * 金币余额
-     */
-    @TableField("gold_coin_amount")
-    private BigDecimal goldCoinAmount;
-
-    /**
-     * 金币总数量
-     */
-    @TableField("gold_coin_total_his")
-    private BigDecimal goldCoinTotalHis;
-
     /**
      * 在线时间 单位秒
      */
     @TableField("online_time")
     private Integer onlineTime;
 
-    /**
-     * 用户余额
-     */
-    @TableField("user_amount")
-    private BigDecimal userAmount;
-
     /**
      * 创建时间
      */

+ 0 - 2
src/main/java/com/xs/core/model/user/req/GoldCoinCompetitionRankingReq.java

@@ -10,6 +10,4 @@ import java.io.Serializable;
 public class GoldCoinCompetitionRankingReq implements Serializable {
     @Schema(name = "limit", description = "排行榜人数", defaultValue = "10", example = "10")
     private Integer limit = 10;
-    @Schema(name = "invitees", description = "最小邀请人数", defaultValue = "5000", example = "500")
-    private Integer invitees = 5000;
 }

+ 21 - 0
src/main/java/com/xs/core/model/user/resp/GoldCoinLeaderboardInfo.java

@@ -0,0 +1,21 @@
+package com.xs.core.model.user.resp;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Schema(description = "金币排行榜信息")
+public class GoldCoinLeaderboardInfo implements Serializable {
+    @Schema(description = "金币排行榜信息")
+    List<UserCompetitionResp> rankList;
+    @Schema(description = "用户金币排行信息")
+    UserCompetitionResp userInfo;
+
+    public GoldCoinLeaderboardInfo(List<UserCompetitionResp> rankList, UserCompetitionResp userInfo) {
+        this.rankList = rankList;
+        this.userInfo = userInfo;
+    }
+}

+ 79 - 0
src/main/java/com/xs/core/model/user/resp/TeamCompetitionResp.java

@@ -0,0 +1,79 @@
+package com.xs.core.model.user.resp;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+@Schema(name = "TeamCompetitionResp", description = "团队竞赛排行榜")
+@Data
+public class TeamCompetitionResp implements Serializable {
+    /**
+     * 排名
+     */
+    @Schema(description = "排名")
+    private int ranking;
+
+    /**
+     * tg小程序id
+     */
+    @Schema(description = "tg小程序id")
+    private String tgId;
+
+    /**
+     * tg账号
+     */
+    @Schema(description = "tg账号")
+    private String tgAccount;
+
+    /**
+     * 名
+     */
+    @Schema(description = "名")
+    private String firstName;
+
+    /**
+     * 姓
+     */
+    @Schema(description = "姓")
+    private String lastName;
+
+    /**
+     * 昵称
+     */
+    @Schema(description = "昵称")
+    private String nickname;
+
+    /**
+     * 真实姓名
+     */
+    @Schema(description = "真实姓名")
+    private String realName;
+
+    /**
+     * 头像
+     */
+    @Schema(description = "头像")
+    private String avatar;
+
+    /**
+     * 邀请人数
+     */
+    @Schema(description = "邀请人数")
+    private int invitedUsersCount;
+
+    public String getTgAccount() {
+        if (Objects.isNull(tgAccount)) {
+            return "";
+        }
+        //脱敏处理 后几位用*号代替
+        if (tgAccount.length() > 4) {
+            tgAccount = tgAccount.substring(0, tgAccount.length() - 4) + "***";
+        } else {
+            //如过小于四位 则只显示前两位
+            tgAccount = tgAccount.substring(0, 2) + "***";
+        }
+        return tgAccount;
+    }
+}

+ 20 - 12
src/main/java/com/xs/core/model/user/resp/UserCompetitionResp.java

@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 import java.math.BigDecimal;
+import java.util.Objects;
 
 /**
  * 竞赛排行榜
@@ -59,18 +60,6 @@ public class UserCompetitionResp {
     @Schema(description = "头像")
     private String avatar;
 
-    /**
-     * 介绍人_id
-     */
-    @Schema(description = "介绍人_id")
-    private String referrerId;
-
-    /**
-     * 空投数量
-     */
-    @Schema(description = "空投数量")
-    private Integer airdropCoin;
-
     /**
      * 金币余额
      */
@@ -83,4 +72,23 @@ public class UserCompetitionResp {
     @Schema(description = "金币总数量")
     private BigDecimal goldCoinTotalHis;
 
+    /**
+     * 脱敏处理
+     *
+     * @return
+     */
+    public String getTgAccount() {
+        if (Objects.isNull(tgAccount)) {
+            return "";
+        }
+        //脱敏处理 后几位用*号代替
+        if (tgAccount.length() > 4) {
+            tgAccount = tgAccount.substring(0, tgAccount.length() - 4) + "***";
+        } else {
+            //如过小于四位 则只显示前两位
+            tgAccount = tgAccount.substring(0, 2) + "***";
+        }
+        return tgAccount;
+    }
+
 }

+ 75 - 0
src/main/java/com/xs/core/model/wallet/entity/UserWallet.java

@@ -0,0 +1,75 @@
+package com.xs.core.model.wallet.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 用户钱包
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-22
+ */
+@Getter
+@Setter
+@TableName("b_user_wallet")
+public class UserWallet extends Model<UserWallet> {
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 用户id
+     */
+    @TableField("user_id")
+    private Long userId;
+
+    /**
+     * 金币总额 历史总额
+     */
+    @TableField("total_balance")
+    private BigDecimal totalBalance;
+
+    /**
+     * 金币可用余额
+     */
+    @TableField("available_balance")
+    private BigDecimal availableBalance;
+
+    /**
+     * 真实货币余额
+     */
+    @TableField("money_balance")
+    private BigDecimal moneyBalance;
+
+    /**
+     * 更新人
+     */
+    @TableField("updated_by")
+    private Long updatedBy;
+
+    /**
+     * 更新时间
+     */
+    @TableField("updated_time")
+    private LocalDateTime updatedTime;
+
+    @Override
+    public Serializable pkVal() {
+        return this.id;
+    }
+}

+ 54 - 0
src/main/java/com/xs/core/model/wallet/resp/WalletInfoResp.java

@@ -0,0 +1,54 @@
+package com.xs.core.model.wallet.resp;
+
+import com.xs.core.model.coin.entity.UserCoinSpeedUpgradesRecord;
+import com.xs.core.model.wallet.entity.UserWallet;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+@Schema(description = "钱包信息")
+public class WalletInfoResp implements Serializable {
+
+    @Schema(description = "总额")
+    private BigDecimal totalBalance;
+
+    @Schema(description = "可用余额")
+    private BigDecimal availableBalance;
+
+    @Schema(description = "产币总收益")
+    private BigDecimal totalProfitPerHour;
+
+    @Schema(description = "每小时收益")
+    private BigDecimal profitPerHour;
+
+    @Schema(description = "任务奖励")
+    private BigDecimal taskRewards;
+
+    @Schema(description = "团队收益")
+    private BigDecimal teamSharing;
+
+    @Schema(description = "邀请奖励")
+    private BigDecimal shareRewards;
+
+    @Schema(description = "空投")
+    private BigDecimal resourcePlacement;
+
+    @Schema(description = "真实货币余额")
+    private BigDecimal moneyBalance;
+
+    public WalletInfoResp(UserWallet wallet, BigDecimal totalProfitPerHour, BigDecimal shareRewards, BigDecimal taskRewards, BigDecimal teamSharing, BigDecimal airdrops, UserCoinSpeedUpgradesRecord speedUpgradesRecord) {
+        this.totalProfitPerHour = totalProfitPerHour;
+        this.shareRewards = shareRewards;
+        this.taskRewards = taskRewards;
+        this.teamSharing = teamSharing;
+        this.resourcePlacement = airdrops;
+        BigDecimal decimal = new BigDecimal(speedUpgradesRecord.getProductRate());
+        this.profitPerHour = decimal.multiply(new BigDecimal(60 * 60L));
+        this.totalBalance = wallet.getTotalBalance();
+        this.availableBalance = wallet.getAvailableBalance();
+        this.moneyBalance = wallet.getMoneyBalance();
+    }
+}

+ 3 - 3
src/main/java/com/xs/core/mq/producer/GoldCoinMessageProducer.java

@@ -26,7 +26,7 @@ public class GoldCoinMessageProducer {
     private static final int BASE_DELAY = 1000; // 1 second
 
     /**
-     * 发送开始游戏的消息
+     * 发送计算金币产出的消息 loop
      */
     public void sendDelayCalculationMessage(CoinProducerMessage coinProducerMessage) {
         sendMessageWithRetry(coinProducerMessage, MAX_RETRY_COUNT, m -> {
@@ -58,8 +58,8 @@ public class GoldCoinMessageProducer {
      */
     public void sendTeamShareMessage(TeamShareMessage teamShareMessage) {
         sendMessageWithRetry(teamShareMessage, MAX_RETRY_COUNT, m -> rabbitTemplate.convertAndSend(RabbitMQConfig.GOLD_COIN_TEAM_SHARE_EXCHANGE, RabbitMQConfig.GOLD_COIN_TEAM_SHARE_QUEUE, JSON.toJSONString(m), msg -> {
-            //延时10
-            msg.getMessageProperties().setHeader("x-delay", 10 * 1000L);
+            //延时5
+            msg.getMessageProperties().setHeader("x-delay", 5 * 1000L);
             return msg;
         }));
     }

+ 25 - 19
src/main/java/com/xs/core/service/Impl/coin/GoldCoinServiceImpl.java

@@ -19,11 +19,13 @@ import com.xs.core.model.coin.req.TemporaryRateReq;
 import com.xs.core.model.coin.resp.BoostResidueTimesResp;
 import com.xs.core.model.coin.resp.CoinSpeedUpgradesRulesResp;
 import com.xs.core.model.coin.resp.GoldCoinProdStateResp;
-import com.xs.core.model.user.entity.TgUser;
+import com.xs.core.model.user.entity.User;
+import com.xs.core.model.wallet.entity.UserWallet;
 import com.xs.core.mq.producer.GoldCoinMessageProducer;
 import com.xs.core.service.coin.*;
 import com.xs.core.service.redis.RedisService;
-import com.xs.core.service.user.ITgUserService;
+import com.xs.core.service.user.IUserService;
+import com.xs.core.service.wallet.IUserWalletService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -44,7 +46,10 @@ public class GoldCoinServiceImpl implements GoldCoinService {
     private IGoldCoinProdRecordService recordService;
 
     @Autowired
-    private ITgUserService userService;
+    private IUserService userService;
+
+    @Autowired
+    private IUserWalletService walletService;
 
     @Autowired
     private RedisService redisService;
@@ -92,10 +97,10 @@ public class GoldCoinServiceImpl implements GoldCoinService {
     }
 
     private void checkProductState(GoldCoinProdState goldCoinProdState, UserContext userContext) {
-        CheckUtils.throwIf(Boolean.TRUE.equals(goldCoinProdState.getRunning()), "金币产出中,不能重复开启");
+        CheckUtils.throwIf(Boolean.TRUE.equals(goldCoinProdState.getRunning()), "error.gold.product.exists");
         //若是非运行状态 则检查待领取的金币
         GoldCoinProdRecord record = recordService.getNoReceivedGoldCoinProdRecordByUserId(userContext.getId());
-        CheckUtils.throwIf(ObjUtil.isNotNull(record) && !record.getReceiving(), "待领取的金币未领取,不能重复开启");
+        CheckUtils.throwIf(ObjUtil.isNotNull(record) && !record.getReceiving(), "error.gold.product.coin.not.claimed");
     }
 
     /**
@@ -170,9 +175,9 @@ public class GoldCoinServiceImpl implements GoldCoinService {
         //先查询是否存在待领取的金币
         GoldCoinProdRecord record = recordService.getNoReceivedGoldCoinProdRecordByUserId(userContext.getId());
         GoldCoinProdState state = stateService.getGoldCoinProdStateByUser(userContext.getId());
-        CheckUtils.throwIfNull(record, "没有待领取的金币");
-        //把金币结算到用户的账户
-        userService.coinTransaction(CoinTransactionTypeEnum.ADD, CoinTransactionCategoryEnum.PRODUCT, "金币产出结算", "挂机产出金币结算", record.getGoldCoinYieldTotal(), userContext.getId());
+        CheckUtils.throwIfNull(record, "no.gold.claimed");
+        //把金币结算到用户的钱包
+        walletService.coinTransaction(CoinTransactionTypeEnum.ADD, CoinTransactionCategoryEnum.PRODUCT, "金币产出结算", "挂机产出金币结算", record.getGoldCoinYieldTotal(), userContext.getId());
         //更新金币产出记录的领取状态
         resetGoldCoinProdState(state);
         record.setReceiving(Boolean.TRUE);
@@ -206,13 +211,14 @@ public class GoldCoinServiceImpl implements GoldCoinService {
         UserContext context = UserContextHolder.getContext();
         //获取金币升级速率
         CoinSpeedUpgradesRules rules = speedRulesService.getById(req.getRuleId());
-        CheckUtils.throwIfNull(rules, "金币产出速率升级规则不存在");
-        TgUser user = userService.getById(context.getId());
-        BigDecimal goldCoinAmount = user.getGoldCoinAmount();
+        CheckUtils.throwIfNull(rules, "error.gold.product.record.not.exists");
+        User user = userService.getById(context.getId());
+        UserWallet wallet = walletService.getWalletByUserId(context.getId());
+        BigDecimal goldCoinAmount = wallet.getAvailableBalance();
         Integer consumeGoldCoin = rules.getConsumeGoldCoin();
-        CheckUtils.throwIf(consumeGoldCoin > goldCoinAmount.intValue(), "金币不足");
+        CheckUtils.throwIf(consumeGoldCoin > goldCoinAmount.intValue(), "error.gold.insufficient");
         //扣除金币
-        userService.coinTransaction(CoinTransactionTypeEnum.SUBTRACT, CoinTransactionCategoryEnum.SPEED_UPGRADES, "金币产出速率升级", "金币产出速率升级", new BigDecimal(consumeGoldCoin), context.getId());
+        walletService.coinTransaction(CoinTransactionTypeEnum.SUBTRACT, CoinTransactionCategoryEnum.SPEED_UPGRADES, "金币产出速率升级", "金币产出速率升级", new BigDecimal(consumeGoldCoin), context.getId());
         //增加一条速率升级记录
         UserCoinSpeedUpgradesRecord speedUpgradesRecord = new UserCoinSpeedUpgradesRecord();
         speedUpgradesRecord.setSpeedLevel(rules.getLevel());
@@ -263,10 +269,10 @@ public class GoldCoinServiceImpl implements GoldCoinService {
         String rate = temporaryRateReq.getRate();
         BigDecimal rateNum = new BigDecimal(rate).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
         UserContext context = UserContextHolder.getContext();
-        CheckUtils.throwIfNull(context, "用户未登录或用户信息为空");
+        CheckUtils.throwIfNull(context, "response.unauthorized");
         //获取用户的金币产出状态
         GoldCoinProdState goldCoinProdState = stateService.getGoldCoinProdStateByUser(context.getId());
-        CheckUtils.throwIf(Boolean.FALSE.equals(goldCoinProdState.getRunning()), "用户未开启金币产出");
+        CheckUtils.throwIf(Boolean.FALSE.equals(goldCoinProdState.getRunning()), "error.gold.product.not.start");
         //获取用户的临时速率
         String rateTemporarilyKey = GoldCoinConstant.GOLD_COIN_RATE_UPGRADES_KEY + context.getId();
         String goldCoinStateKey = GoldCoinConstant.GOLD_COIN_STATE_KEY + context.getId();
@@ -274,7 +280,7 @@ public class GoldCoinServiceImpl implements GoldCoinService {
         //判断用户的速率升级记录是否存在  存在则不允许升级
         if (redisService.hasKey(rateTemporarilyKey)) {
             GoldRateUpgradesState upgradesState = redisService.get(rateTemporarilyKey, GoldRateUpgradesState.class);
-            CheckUtils.throwIf(upgradesState.isHasTemporaryRate(), "当前生产批次已存在临时速率,暂不允许升级");
+            CheckUtils.throwIf(upgradesState.isHasTemporaryRate(), "error.gold.boost.temporary.exists");
             upgradesState.setHasTemporaryRate(true);
             String currentRateStr = upgradesState.getCurrentRate();
             BigDecimal currentRate = new BigDecimal(currentRateStr);
@@ -291,7 +297,7 @@ public class GoldCoinServiceImpl implements GoldCoinService {
             GoldRateUpgradesState upgradesState = new GoldRateUpgradesState();
             upgradesState.setHasTemporaryRate(true);
             GoldCoinProdState coinProdState = redisService.get(goldCoinStateKey, GoldCoinProdState.class);
-            CheckUtils.throwIfNull(coinProdState, "用户未开启金币产出");
+            CheckUtils.throwIfNull(coinProdState, "error.gold.product.not.start");
             String currentRateStr = coinProdState.getCurrentRate();
             BigDecimal currentRate = new BigDecimal(currentRateStr);
             BigDecimal added = currentRate.add(rateNum);
@@ -314,7 +320,7 @@ public class GoldCoinServiceImpl implements GoldCoinService {
     @Override
     public GoldCoinProdStateResp getGoldCoinProdState() {
         UserContext context = UserContextHolder.getContext();
-        CheckUtils.throwIfNull(context, "用户未登录或用户信息为空");
+        CheckUtils.throwIfNull(context, "response.unauthorized");
         GoldCoinProdStateResp state = stateService.getGoldCoinProdRespStateByUser(context.getId());
         if (state == null) {
             state = new GoldCoinProdStateResp();
@@ -387,7 +393,7 @@ public class GoldCoinServiceImpl implements GoldCoinService {
         UserContext context = UserContextHolder.getContext();
         String key = GoldCoinConstant.GOLD_COIN_STATE_KEY + context.getId();
         GoldCoinProdState goldCoinProdState = redisService.get(key, GoldCoinProdState.class);
-        CheckUtils.throwIfNull(goldCoinProdState, "金币产出结束或未开启金币产出,加成已失效");
+        CheckUtils.throwIfNull(goldCoinProdState, "error.gold.product.end");
         BoostResidueTimesResp boostResidueTimesResp = new BoostResidueTimesResp();
         BeanUtils.copyProperties(goldCoinProdState, boostResidueTimesResp);
         return boostResidueTimesResp;

+ 15 - 4
src/main/java/com/xs/core/service/Impl/competition/CompetitionServiceImpl.java

@@ -1,6 +1,7 @@
 package com.xs.core.service.Impl.competition;
 
-import com.xs.core.mapper.user.TgUserMapper;
+import com.xs.core.mapper.user.UserMapper;
+import com.xs.core.model.user.resp.TeamCompetitionResp;
 import com.xs.core.model.user.resp.UserCompetitionResp;
 import com.xs.core.service.competition.CompetitionService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -12,10 +13,20 @@ import java.util.List;
 public class CompetitionServiceImpl implements CompetitionService {
 
     @Autowired
-    private TgUserMapper userMapper;
+    private UserMapper userMapper;
 
     @Override
-    public List<UserCompetitionResp> goldCoinCompetitionRanking(int limit, int invitees) {
-        return userMapper.goldCoinCompetitionRanking(limit, invitees);
+    public List<TeamCompetitionResp> teamInviteCompetitionRanking(int limit, int invitee) {
+        return userMapper.teamInviteCompetitionRanking(limit, invitee);
+    }
+
+    @Override
+    public List<UserCompetitionResp> individualGoldCoinCompetitionRanking(int limit) {
+        return userMapper.individualGoldCoinCompetitionRanking(limit);
+    }
+
+    @Override
+    public UserCompetitionResp getIndividualGoldCoinCompetitionInfo(Long userId) {
+        return userMapper.getIndividualGoldCoinCompetitionInfo(userId);
     }
 }

+ 29 - 0
src/main/java/com/xs/core/service/Impl/team/InviteRewardsRuleServiceImpl.java

@@ -0,0 +1,29 @@
+package com.xs.core.service.Impl.team;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.xs.core.model.team.entity.InviteRewardsRule;
+import com.xs.core.mapper.team.InviteRewardsRuleMapper;
+import com.xs.core.service.team.IInviteRewardsRuleService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励规则 服务实现类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+@Service
+public class InviteRewardsRuleServiceImpl extends ServiceImpl<InviteRewardsRuleMapper, InviteRewardsRule> implements IInviteRewardsRuleService {
+
+    @Override
+    public List<InviteRewardsRule> getAllByOrderByRewardsLevelAsc() {
+        LambdaQueryWrapper<InviteRewardsRule> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.orderByAsc(InviteRewardsRule::getRewardsLevel);
+        return list(queryWrapper);
+    }
+}

+ 36 - 0
src/main/java/com/xs/core/service/Impl/team/InviteTaskRewardsFetchRecordServiceImpl.java

@@ -0,0 +1,36 @@
+package com.xs.core.service.Impl.team;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.xs.core.model.team.entity.InviteTaskRewardsFetchRecord;
+import com.xs.core.mapper.team.InviteTaskRewardsFetchRecordMapper;
+import com.xs.core.service.team.IInviteTaskRewardsFetchRecordService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励领取记录 服务实现类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+@Service
+public class InviteTaskRewardsFetchRecordServiceImpl extends ServiceImpl<InviteTaskRewardsFetchRecordMapper, InviteTaskRewardsFetchRecord> implements IInviteTaskRewardsFetchRecordService {
+    @Override
+    public InviteTaskRewardsFetchRecord getLastByUserId(Long id) {
+        LambdaQueryWrapper<InviteTaskRewardsFetchRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(InviteTaskRewardsFetchRecord::getUserId, id);
+        queryWrapper.orderByDesc(InviteTaskRewardsFetchRecord::getCreatedTime);
+        queryWrapper.last("limit 1");
+        return getOne(queryWrapper);
+    }
+
+    @Override
+    public InviteTaskRewardsFetchRecord getByRuleId(String ruleId) {
+        LambdaQueryWrapper<InviteTaskRewardsFetchRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(InviteTaskRewardsFetchRecord::getRuleId, ruleId);
+        queryWrapper.last("limit 1");
+        return getOne(queryWrapper);
+    }
+}

+ 8 - 0
src/main/java/com/xs/core/service/Impl/team/TeamInviteRewardRecordServiceImpl.java

@@ -1,11 +1,15 @@
 package com.xs.core.service.Impl.team;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.xs.core.model.team.entity.TeamInviteRewardRecord;
 import com.xs.core.mapper.team.TeamInviteRewardRecordMapper;
+import com.xs.core.model.team.resp.TeamInviteRewardsResp;
 import com.xs.core.service.team.ITeamInviteRewardRecordService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * <p>
  * 团队邀请奖励领取列表(每邀请一人奖励1000) 服务实现类
@@ -17,4 +21,8 @@ import org.springframework.stereotype.Service;
 @Service
 public class TeamInviteRewardRecordServiceImpl extends ServiceImpl<TeamInviteRewardRecordMapper, TeamInviteRewardRecord> implements ITeamInviteRewardRecordService {
 
+    @Override
+    public List<TeamInviteRewardsResp> getTeamInviteRewardRecordList(Long id) {
+        return getBaseMapper().getTeamInviteRewardsRespList(id);
+    }
 }

+ 153 - 33
src/main/java/com/xs/core/service/Impl/team/TeamShareGoldCoinSettlementServiceImpl.java

@@ -3,18 +3,19 @@ package com.xs.core.service.Impl.team;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.xs.core.common.enums.CoinTransactionCategoryEnum;
+import com.xs.core.common.enums.CoinTransactionTypeEnum;
+import com.xs.core.common.exception.BusinessException;
+import com.xs.core.common.validation.CheckUtils;
 import com.xs.core.model.coin.msg.TeamShareMessage;
-import com.xs.core.model.team.entity.TeamInviteConfig;
-import com.xs.core.model.team.entity.TeamInviteRewardRecord;
-import com.xs.core.model.team.entity.TeamShareRewardRecord;
-import com.xs.core.model.team.resp.TeamUserResp;
-import com.xs.core.model.team.resp.TeamsInfoResp;
-import com.xs.core.model.user.entity.TgUser;
-import com.xs.core.service.team.ITeamInviteConfigService;
-import com.xs.core.service.team.ITeamInviteRewardRecordService;
-import com.xs.core.service.team.ITeamShareRewardRecordService;
-import com.xs.core.service.team.TeamShareService;
-import com.xs.core.service.user.ITgUserService;
+import com.xs.core.model.team.entity.*;
+import com.xs.core.model.team.req.TeamShareReq;
+import com.xs.core.model.team.resp.*;
+import com.xs.core.model.user.entity.User;
+import com.xs.core.service.team.*;
+import com.xs.core.service.user.IUserService;
+import com.xs.core.service.wallet.IUserWalletService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -25,13 +26,17 @@ import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
 
 
 @Service
 @Slf4j
 public class TeamShareGoldCoinSettlementServiceImpl implements TeamShareService {
     @Autowired
-    private ITgUserService userService;
+    private IUserService userService;
 
     @Autowired
     private ITeamInviteConfigService teamInviteConfigService;
@@ -42,13 +47,22 @@ public class TeamShareGoldCoinSettlementServiceImpl implements TeamShareService
     @Autowired
     private ITeamShareRewardRecordService teamShareRewardRecordService;
 
+    @Autowired
+    private IUserWalletService walletService;
+
+    @Autowired
+    private IInviteRewardsRuleService inviteRewardsRuleService;
+
+    @Autowired
+    private IInviteTaskRewardsFetchRecordService inviteRewardsFetchRecordService;
+
     @Override
     @Transactional
     public void teamShareGoldCoinSettlement(TeamShareMessage teamShareMessage) {
         Long userId = teamShareMessage.getUserId();
         //获取用户的上一级邀请人 即a队
-        TgUser inviteA = userService.getInviterByUserId(userId);
-        TgUser inviteB = null;
+        User inviteA = userService.getInviterByUserId(userId);
+        User inviteB = null;
         if (inviteA != null && StrUtil.isNotBlank(inviteA.getReferrerId())) {
             //获取a队的上一级邀请人 即b队
             inviteB = userService.getById(inviteA.getReferrerId());
@@ -104,29 +118,135 @@ public class TeamShareGoldCoinSettlementServiceImpl implements TeamShareService
 
     @Override
     public TeamsInfoResp getMyTeamsInfo(Long id) {
-        List<TeamUserResp> teamUserList = getMyTeamUserList(id, null);
-        TeamsInfoResp teamsInfoResp = new TeamsInfoResp();
-        TeamInviteConfig config = teamInviteConfigService.getTeamInviteConfig();
-        if (CollUtil.isNotEmpty(teamUserList)) {
-            teamsInfoResp.setTotal(teamUserList.size());
-            long countA = teamUserList.stream().filter(t -> "A".equals(t.getTeamType())).count();
-            long countB = teamUserList.stream().filter(t -> "B".equals(t.getTeamType())).count();
-            teamsInfoResp.setTeamACount(countA);
-            teamsInfoResp.setTeamBCount(countB);
-        } else {
-            teamsInfoResp.setTotal(0);
-            teamsInfoResp.setTeamACount(0L);
-            teamsInfoResp.setTeamBCount(0L);
+        CompletableFuture<List<TeamUserResp>> teamUserListFuture = CompletableFuture.supplyAsync(() -> userService.getMyTeamUserList(id, null));
+        CompletableFuture<List<TeamShareRewardRecord>> shareRewardRecordListAFuture = CompletableFuture.supplyAsync(() -> teamShareRewardRecordService.getNoClaimTeamShareRewardRecordByType(id, "A"));
+        CompletableFuture<List<TeamShareRewardRecord>> shareRewardRecordListBFuture = CompletableFuture.supplyAsync(() -> teamShareRewardRecordService.getNoClaimTeamShareRewardRecordByType(id, "B"));
+        CompletableFuture<TeamInviteConfig> configFuture = CompletableFuture.supplyAsync(() -> teamInviteConfigService.getTeamInviteConfig());
+        CompletableFuture<List<TeamInviteRewardsResp>> teamInviteRewardRecordListFuture = CompletableFuture.supplyAsync(() -> teamInviteRewardRecordService.getTeamInviteRewardRecordList(id));
+        CompletableFuture.allOf(teamUserListFuture, shareRewardRecordListAFuture, shareRewardRecordListBFuture, configFuture, teamInviteRewardRecordListFuture);
+        return new TeamsInfoResp(teamInviteRewardRecordListFuture.join(), teamUserListFuture.join(), shareRewardRecordListAFuture.join(), shareRewardRecordListBFuture.join(), configFuture.join());
+    }
+
+    @Override
+    public TeamShareInfoResp getTeamShareUserInfoByType(Long userId, TeamShareReq req) {
+        //获取团队用户信息
+        List<TeamShareUserResp> teamShareUserRespList = teamShareRewardRecordService.getTeamShareUserInfoByType(userId, req.getType());
+        if (CollUtil.isNotEmpty(teamShareUserRespList)) {
+            TeamShareInfoResp teamShareInfoResp = new TeamShareInfoResp();
+            teamShareInfoResp.setUsers(teamShareUserRespList);
+            BigDecimal totalTeamRewardAmount = teamShareUserRespList.stream()
+                    .map(TeamShareUserResp::getTeamRewardAmount)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            teamShareInfoResp.setTotal(totalTeamRewardAmount.setScale(2, RoundingMode.HALF_UP));
+            return teamShareInfoResp;
+        }
+        return null;
+    }
+
+    @Override
+    @Transactional
+    public void claimTeamShareReward(Long id, TeamShareReq req) {
+        List<TeamShareRewardRecord> noClaimTeamShareRewardRecordList = teamShareRewardRecordService.getNoClaimTeamShareRewardRecordByType(id, req.getType());
+        CheckUtils.throwIfEmpty(noClaimTeamShareRewardRecordList, "error.team.share.reward.empty");
+        if (CollUtil.isNotEmpty(noClaimTeamShareRewardRecordList)) {
+            //计算总奖励
+            BigDecimal totalRewardAmount = noClaimTeamShareRewardRecordList.stream()
+                    .map(TeamShareRewardRecord::getRewardAmount)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            //更新用户金币
+            walletService.coinTransaction(CoinTransactionTypeEnum.ADD, CoinTransactionCategoryEnum.TEAM_SHARING, "团队产出收益", "团队产出收益", totalRewardAmount, id);
+            //更新为已领取
+            noClaimTeamShareRewardRecordList.forEach(t -> {
+                t.setClaimStatus(1);
+                t.setClaimTime(LocalDateTime.now());
+            });
+            teamShareRewardRecordService.updateBatchById(noClaimTeamShareRewardRecordList);
         }
-        if (ObjUtil.isNotNull(config)) {
-            teamsInfoResp.setProportionA(config.getTeamAScale());
-            teamsInfoResp.setProportionB(config.getTeamBScale());
+    }
+
+    @Override
+    public List<TeamInviteRewardsResp> getTeamInviteRewardsList(Long id) {
+        //获取团队邀请奖励记录
+        return teamInviteRewardRecordService.getTeamInviteRewardRecordList(id);
+    }
+
+    @Override
+    public TeamInviteAvailableRewardsResp getTeamInviteAvailableRewardsRules(Long id) {
+        //获取团队邀请奖励规则
+        List<InviteRewardsRule> allRules = inviteRewardsRuleService.getAllByOrderByRewardsLevelAsc();
+        InviteTaskRewardsFetchRecord userRewards = inviteRewardsFetchRecordService.getLastByUserId(id);
+        //查询用户邀请的人数
+        Long inviterCount = userService.queryInviterCount(id);
+        List<InviteRewardsRule> collect = allRules.stream()
+                .filter(rule -> rule.getRewardsLevel() > (ObjUtil.isNotNull(userRewards.getRewardsLevel()) ? userRewards.getRewardsLevel() : 0))
+                .toList();
+        return new TeamInviteAvailableRewardsResp(inviterCount, collect);
+    }
+
+    @Override
+    @Transactional
+    public void claimTeamInviteReward(Long id) {
+        //获取团队邀请奖励记录
+        LambdaQueryWrapper<TeamInviteRewardRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(TeamInviteRewardRecord::getReceiverId, id);
+        queryWrapper.eq(TeamInviteRewardRecord::getClaimStatus, 0);
+        List<TeamInviteRewardRecord> list = teamInviteRewardRecordService.list(queryWrapper);
+        CheckUtils.throwIfEmpty(list, "error.team.invite.reward.empty");
+        if (CollUtil.isNotEmpty(list)) {
+            //计算总奖励
+            BigDecimal totalRewardAmount = list.stream()
+                    .map(TeamInviteRewardRecord::getRewardAmount)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            //更新用户金币
+            walletService.coinTransaction(CoinTransactionTypeEnum.ADD, CoinTransactionCategoryEnum.SHARE_REWARDS, "团队邀请收益", "团队邀请收益,邀请一人奖励1000", totalRewardAmount, id);
+            //更新为已领取
+            list.forEach(t -> {
+                t.setClaimStatus(1);
+                t.setClaimTime(LocalDateTime.now());
+            });
+            teamInviteRewardRecordService.updateBatchById(list);
         }
-        return teamsInfoResp;
     }
 
     @Override
-    public List<TeamUserResp> getMyTeamUserList(Long userId, String teamType) {
-        return userService.getMyTeamUserList(userId, teamType);
+    @Transactional
+    public void claimTeamInviteTaskReward(Long id, String ruleId) {
+        //查询用户邀请的人数
+        Long inviterCount = userService.queryInviterCount(id);
+        InviteTaskRewardsFetchRecord exist = inviteRewardsFetchRecordService.getByRuleId(ruleId);
+        CheckUtils.throwIf(ObjUtil.isNotNull(exist), "error.team.invite.reward.task.claim.exists");
+        List<InviteRewardsRule> allRules = inviteRewardsRuleService.getAllByOrderByRewardsLevelAsc();
+        //查询待领取规则
+        InviteRewardsRule claimRule = inviteRewardsRuleService.getById(ruleId);
+        CheckUtils.throwIf(ObjUtil.isNull(claimRule), "error.team.invite.reward.task.claim.not.found");
+        CheckUtils.throwIf(inviterCount < claimRule.getInviteNum(), "error.team.invite.reward.task.claim.not.enough");
+        //查询用户已领取的奖励等级
+        InviteTaskRewardsFetchRecord userRewards = Optional.ofNullable(inviteRewardsFetchRecordService.getLastByUserId(id)).orElse(new InviteTaskRewardsFetchRecord(0, 0));
+        int nextIndex = 0;
+        //按顺序遍历
+        for (int i = 0; i < allRules.size(); i++) {
+            InviteRewardsRule rule = allRules.get(i);
+            if (rule.getId().equals(userRewards.getId())) {
+                //找到下一等级待领取的规则
+                nextIndex = i + 1;
+            }
+        }
+        CheckUtils.throwIf(nextIndex >= allRules.size(), "error.team.invite.reward.task.claim.done");
+        InviteRewardsRule nextRule = allRules.get(nextIndex);
+        CheckUtils.throwIf(claimRule.getRewardsLevel() > nextRule.getRewardsLevel(), "error.team.invite.reward.task.claim.previous.not.claim");
+
+        //添加领取记录
+        InviteTaskRewardsFetchRecord record = new InviteTaskRewardsFetchRecord();
+        record.setUserId(id);
+        record.setRewardsLevel(claimRule.getRewardsLevel());
+        record.setCreatedTime(LocalDateTime.now());
+        record.setRuleId(claimRule.getId());
+        record.setGoldCoinNum(claimRule.getGoldCoinNum());
+        inviteRewardsFetchRecordService.save(record);
+        //更新用户金币
+        walletService.coinTransaction(CoinTransactionTypeEnum.ADD, CoinTransactionCategoryEnum.SHARE_REWARDS, "团队邀请任务奖励", "团队邀请任务奖励", new BigDecimal(claimRule.getGoldCoinNum()), id);
     }
 }

+ 21 - 0
src/main/java/com/xs/core/service/Impl/team/TeamShareRewardRecordServiceImpl.java

@@ -1,11 +1,17 @@
 package com.xs.core.service.Impl.team;
 
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.xs.core.model.team.entity.TeamShareRewardRecord;
 import com.xs.core.mapper.team.TeamShareRewardRecordMapper;
+import com.xs.core.model.team.resp.TeamShareUserResp;
+import com.xs.core.model.team.resp.TeamUserResp;
 import com.xs.core.service.team.ITeamShareRewardRecordService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * <p>
  * 团队产出奖励记录 A B队抽成 服务实现类
@@ -17,4 +23,19 @@ import org.springframework.stereotype.Service;
 @Service
 public class TeamShareRewardRecordServiceImpl extends ServiceImpl<TeamShareRewardRecordMapper, TeamShareRewardRecord> implements ITeamShareRewardRecordService {
 
+    @Override
+    public List<TeamShareUserResp> getTeamShareUserInfoByType(Long userId, String type) {
+        return getBaseMapper().getTeamShareUserInfoByType(userId, type);
+    }
+
+    @Override
+    public List<TeamShareRewardRecord> getNoClaimTeamShareRewardRecordByType(Long userId, String type) {
+        LambdaQueryWrapper<TeamShareRewardRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(TeamShareRewardRecord::getTargetId, userId);
+        queryWrapper.eq(TeamShareRewardRecord::getClaimStatus, 0);
+        if (StrUtil.isNotBlank(type)) {
+            queryWrapper.eq(TeamShareRewardRecord::getTeamType, type);
+        }
+        return list(queryWrapper);
+    }
 }

+ 26 - 15
src/main/java/com/xs/core/service/Impl/user/AppLoginServiceImpl.java

@@ -12,18 +12,20 @@ import com.xs.core.common.constant.ConstantConfig;
 import com.xs.core.common.content.UserContext;
 import com.xs.core.common.enums.DisEnableStatusEnum;
 import com.xs.core.common.validation.CheckUtils;
-import com.xs.core.model.user.entity.TgUser;
+import com.xs.core.model.user.entity.User;
 import com.xs.core.model.user.req.AppLoginReq;
-import com.xs.core.service.team.ITeamInviteRewardRecordService;
+import com.xs.core.model.wallet.entity.UserWallet;
 import com.xs.core.service.team.TeamShareService;
 import com.xs.core.service.user.AppLoginService;
-import com.xs.core.service.user.ITgUserService;
+import com.xs.core.service.user.IUserService;
+import com.xs.core.service.wallet.IUserWalletService;
 import com.xs.core.utils.InviteCodeGenerator;
 import com.xs.core.utils.SpringWebUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
@@ -34,15 +36,17 @@ import java.util.concurrent.CompletableFuture;
 @AllArgsConstructor
 @Slf4j
 public class AppLoginServiceImpl implements AppLoginService {
-    private final ITgUserService userService;
+    private final IUserService userService;
 
     private final TeamShareService teamShareService;
 
+    private final IUserWalletService walletService;
+
     @Override
     public SaTokenInfo tgLogin(AppLoginReq loginReq) {
-        LambdaQueryWrapper<TgUser> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(TgUser::getTgId, loginReq.getId());
-        TgUser user = userService.getOne(queryWrapper);
+        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(User::getTgId, loginReq.getId());
+        User user = userService.getOne(queryWrapper);
         if (ObjUtil.isNull(user)) {
             user = registerToSys(loginReq);
         }
@@ -52,8 +56,10 @@ public class AppLoginServiceImpl implements AppLoginService {
     }
 
     @Override
-    public TgUser registerToSys(AppLoginReq loginReq) {
-        TgUser user = TgUser.builder()
+    @Transactional
+    public User registerToSys(AppLoginReq loginReq) {
+        User user = User.builder()
+                .id(IdWorker.getId())
                 .tgAccount(loginReq.getUsername())
                 .tgId(loginReq.getId())
                 .avatar(StrUtil.isNotBlank(loginReq.getPhotoUrl()) ? URLDecoder.decode(loginReq.getPhotoUrl(), StandardCharsets.UTF_8) : "")
@@ -68,11 +74,11 @@ public class AppLoginServiceImpl implements AppLoginService {
         if (StrUtil.isNotBlank(loginReq.getAge_limit())) {
             user.setAgeLimit(Integer.parseInt(loginReq.getAge_limit()));
         }
-        TgUser inviteUser;
+        User inviteUser;
         if (StrUtil.isNotBlank(loginReq.getShare_code())) {
             //如邀请码不为空 则写入邀请人信息
-            LambdaQueryWrapper<TgUser> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(TgUser::getInviteCode, loginReq.getShare_code());
+            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(User::getInviteCode, loginReq.getShare_code());
             inviteUser = userService.getOne(queryWrapper);
             if (ObjUtil.isNotNull(inviteUser)) {
                 user.setReferrerId(String.valueOf(inviteUser.getId()));
@@ -82,7 +88,12 @@ public class AppLoginServiceImpl implements AppLoginService {
         } else {
             inviteUser = null;
         }
+        //初始化钱包信息
+        UserWallet userWallet = new UserWallet();
+        userWallet.setUserId(user.getId());
+        userWallet.setUpdatedTime(LocalDateTime.now());
         userService.save(user);
+        walletService.save(userWallet);
         CompletableFuture.runAsync(() -> {
             //注册成功后发放邀请奖励
             if (ObjUtil.isNotNull(inviteUser)) {
@@ -97,11 +108,11 @@ public class AppLoginServiceImpl implements AppLoginService {
      *
      * @param user 用户信息
      */
-    private void checkUserStatus(TgUser user) {
-        CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE.getValue(), user.getDisableFlag(), "此账号已被禁用,如有疑问,请联系管理员");
+    private void checkUserStatus(User user) {
+        CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE.getValue(), user.getDisableFlag(), "response.user.disable");
     }
 
-    private SaTokenInfo login(TgUser user) {
+    private SaTokenInfo login(User user) {
         Long userId = user.getId();
         user.setLoginTime(LocalDateTime.now());
         UserContext userContext = new UserContext();

+ 0 - 69
src/main/java/com/xs/core/service/Impl/user/TgUserServiceImpl.java

@@ -1,69 +0,0 @@
-package com.xs.core.service.Impl.user;
-
-import com.xs.core.common.enums.CoinTransactionCategoryEnum;
-import com.xs.core.common.enums.CoinTransactionTypeEnum;
-import com.xs.core.model.coin.entity.UserCoinTransaction;
-import com.xs.core.model.team.resp.TeamUserResp;
-import com.xs.core.model.user.entity.TgUser;
-import com.xs.core.mapper.user.TgUserMapper;
-import com.xs.core.service.coin.IUserCoinTransactionService;
-import com.xs.core.service.user.ITgUserService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-/**
- * <p>
- * tg用户 服务实现类
- * </p>
- *
- * @author xudm
- * @since 2024-12-18
- */
-@Service
-public class TgUserServiceImpl extends ServiceImpl<TgUserMapper, TgUser> implements ITgUserService {
-    @Autowired
-    private IUserCoinTransactionService userCoinTransactionService;
-
-    @Override
-    @Transactional
-    public void coinTransaction(CoinTransactionTypeEnum type, CoinTransactionCategoryEnum category, String reason, String detail, BigDecimal amount, Long userId) {
-        TgUser user = getById(userId);
-        // 记录交易记录
-        UserCoinTransaction record = new UserCoinTransaction();
-        record.setUserId(userId);
-        // 更新用户金币数量
-        if (type.equals(CoinTransactionTypeEnum.ADD)) {
-            // 增加金币
-            BigDecimal added = user.getGoldCoinAmount().add(amount);
-            user.setGoldCoinAmount(added);
-            BigDecimal his = user.getGoldCoinTotalHis().add(amount);
-            user.setGoldCoinTotalHis(his);
-        } else if (type.equals(CoinTransactionTypeEnum.SUBTRACT)) {
-            // 减少金币
-            user.setGoldCoinAmount(user.getGoldCoinAmount().subtract(amount));
-        }
-        record.setAmount(amount);
-        record.setBalance(user.getGoldCoinAmount());
-        record.setTransactionType(type.getCode());
-        record.setTransactionCategory(category.getCode());
-        record.setTransactionReason(reason);
-        record.setTransactionDetail(detail);
-        updateById(user);
-        userCoinTransactionService.save(record);
-    }
-
-    @Override
-    public TgUser getInviterByUserId(Long userId) {
-        return getBaseMapper().getInviterByUserId(userId);
-    }
-
-    @Override
-    public List<TeamUserResp> getMyTeamUserList(Long userId, String teamType) {
-        return getBaseMapper().getMyTeamUserList(userId, teamType);
-    }
-}

+ 60 - 0
src/main/java/com/xs/core/service/Impl/user/UserServiceImpl.java

@@ -0,0 +1,60 @@
+package com.xs.core.service.Impl.user;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.xs.core.common.content.UserContextHolder;
+import com.xs.core.common.validation.CheckUtils;
+import com.xs.core.model.team.resp.TeamUserResp;
+import com.xs.core.model.user.entity.User;
+import com.xs.core.mapper.user.UserMapper;
+import com.xs.core.service.user.IUserService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.xs.core.utils.InviteCodeGenerator;
+import org.springframework.stereotype.Service;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * <p>
+ * tg用户 服务实现类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-22
+ */
+@Service
+public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
+
+    @Override
+    public User getInviterByUserId(Long userId) {
+        return getBaseMapper().getInviterByUserId(userId);
+    }
+
+    @Override
+    public List<TeamUserResp> getMyTeamUserList(Long userId, String teamType) {
+        return getBaseMapper().getMyTeamUserList(userId, teamType);
+    }
+
+    @Override
+    public String getInviteCode() {
+        Long userId = UserContextHolder.getContext().getId();
+        User user = getById(userId);
+        CheckUtils.throwIfNull(user, "response.unauthorized");
+        String inviteCode = user.getInviteCode();
+        if (StrUtil.isBlank(inviteCode)) {
+            inviteCode = InviteCodeGenerator.generateInviteCode();
+            user.setInviteCode(inviteCode);
+            updateById(user);
+            return inviteCode;
+        }
+        return inviteCode;
+    }
+
+    @Override
+    public Long queryInviterCount(Long id) {
+        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(User::getReferrerId, id);
+        return count(queryWrapper);
+    }
+}

+ 93 - 0
src/main/java/com/xs/core/service/Impl/wallet/UserWalletServiceImpl.java

@@ -0,0 +1,93 @@
+package com.xs.core.service.Impl.wallet;
+
+import cn.hutool.core.util.ObjUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.xs.core.common.enums.CoinTransactionCategoryEnum;
+import com.xs.core.common.enums.CoinTransactionTypeEnum;
+import com.xs.core.model.coin.entity.UserCoinTransaction;
+import com.xs.core.model.user.entity.User;
+import com.xs.core.model.wallet.entity.UserWallet;
+import com.xs.core.mapper.wallet.UserWalletMapper;
+import com.xs.core.service.coin.IUserCoinTransactionService;
+import com.xs.core.service.wallet.IUserWalletService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * <p>
+ * 用户钱包 服务实现类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-22
+ */
+@Service
+public class UserWalletServiceImpl extends ServiceImpl<UserWalletMapper, UserWallet> implements IUserWalletService {
+    @Autowired
+    private IUserCoinTransactionService userCoinTransactionService;
+
+    @Override
+    public UserWallet getWalletByUserId(Long userId) {
+        LambdaQueryWrapper<UserWallet> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(UserWallet::getUserId, userId);
+        return getOne(queryWrapper);
+    }
+
+    @Override
+    @Transactional
+    public void coinTransaction(CoinTransactionTypeEnum type, CoinTransactionCategoryEnum category, String reason, String detail, BigDecimal amount, Long userId) {
+        // 记录交易记录
+        UserCoinTransaction record = new UserCoinTransaction();
+        UserWallet userWallet = getWalletByUserId(userId);
+        if (ObjUtil.isNull(userWallet)) {
+            //如果钱包不存在,则创建
+            userWallet = new UserWallet();
+            userWallet.setUserId(userId);
+            userWallet.setAvailableBalance(BigDecimal.ZERO);
+            userWallet.setTotalBalance(BigDecimal.ZERO);
+            userWallet.setMoneyBalance(BigDecimal.ZERO);
+            userWallet.setUpdatedTime(LocalDateTime.now());
+        }
+        record.setUserId(userId);
+        // 更新用户钱包金币数量
+        if (type.equals(CoinTransactionTypeEnum.ADD)) {
+            // 增加金币
+            BigDecimal added = userWallet.getAvailableBalance().add(amount);
+            userWallet.setAvailableBalance(added);
+            BigDecimal total = userWallet.getTotalBalance().add(amount);
+            userWallet.setTotalBalance(total);
+        } else if (type.equals(CoinTransactionTypeEnum.SUBTRACT)) {
+            // 减少金币
+            userWallet.setAvailableBalance(userWallet.getAvailableBalance().subtract(amount));
+        }
+        record.setAmount(amount);
+        record.setBalance(userWallet.getTotalBalance());
+        record.setTransactionType(type.getCode());
+        record.setTransactionCategory(category.getCode());
+        record.setTransactionReason(reason);
+        record.setTransactionDetail(detail);
+        saveOrUpdate(userWallet);
+        userCoinTransactionService.save(record);
+    }
+
+    @Override
+    public UserWallet getWalletInfoByUserId(Long id) {
+        LambdaQueryWrapper<UserWallet> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(UserWallet::getUserId, id);
+        return getOne(queryWrapper);
+    }
+
+    @Override
+    public List<UserCoinTransaction> getCoinTransactionByType(Long id, CoinTransactionCategoryEnum category) {
+        LambdaQueryWrapper<UserCoinTransaction> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(UserCoinTransaction::getUserId, id);
+        queryWrapper.eq(UserCoinTransaction::getTransactionCategory, category.getCode());
+        return userCoinTransactionService.list(queryWrapper);
+    }
+}

+ 93 - 0
src/main/java/com/xs/core/service/Impl/wallet/WalletServiceImpl.java

@@ -0,0 +1,93 @@
+package com.xs.core.service.Impl.wallet;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import com.xs.core.common.content.UserContext;
+import com.xs.core.common.content.UserContextHolder;
+import com.xs.core.common.enums.CoinTransactionCategoryEnum;
+import com.xs.core.common.enums.CoinTransactionTypeEnum;
+import com.xs.core.model.coin.entity.UserCoinSpeedUpgradesRecord;
+import com.xs.core.model.coin.entity.UserCoinTransaction;
+import com.xs.core.model.wallet.entity.UserWallet;
+import com.xs.core.model.wallet.resp.WalletInfoResp;
+import com.xs.core.service.coin.IUserCoinSpeedUpgradesRecordService;
+import com.xs.core.service.wallet.IUserWalletService;
+import com.xs.core.service.wallet.WalletService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+@Service
+public class WalletServiceImpl implements WalletService {
+    @Autowired
+    private IUserWalletService walletService;
+
+    @Autowired
+    private IUserCoinSpeedUpgradesRecordService speedUpgradesRecordService;
+
+    @Override
+    public WalletInfoResp getWalletInfo() {
+        UserContext context = UserContextHolder.getContext();
+        //查询用户钱包信息
+        CompletableFuture<UserWallet> userWalletFuture = CompletableFuture.supplyAsync(() -> walletService.getWalletByUserId(context.getId()));
+        //查询用户交易信息
+        CompletableFuture<BigDecimal> totalProfitPerHourFuture = CompletableFuture.supplyAsync(() -> {
+            List<UserCoinTransaction> totalProfitPerHour = walletService.getCoinTransactionByType(context.getId(), CoinTransactionCategoryEnum.PRODUCT);
+            return assistCalculate(totalProfitPerHour);
+        });
+        //邀请奖励
+        CompletableFuture<BigDecimal> shareRewardsFuture = CompletableFuture.supplyAsync(() -> {
+            List<UserCoinTransaction> shareRewards = walletService.getCoinTransactionByType(context.getId(), CoinTransactionCategoryEnum.SHARE_REWARDS);
+            return assistCalculate(shareRewards);
+        });
+        //任务奖励
+        CompletableFuture<BigDecimal> taskRewardsFuture = CompletableFuture.supplyAsync(() -> {
+            List<UserCoinTransaction> taskRewards = walletService.getCoinTransactionByType(context.getId(), CoinTransactionCategoryEnum.TASK_REWARDS);
+            return assistCalculate(taskRewards);
+        });
+        //团队奖励
+        CompletableFuture<BigDecimal> teamSharingFuture = CompletableFuture.supplyAsync(() -> {
+            List<UserCoinTransaction> teamSharing = walletService.getCoinTransactionByType(context.getId(), CoinTransactionCategoryEnum.TEAM_SHARING);
+            return assistCalculate(teamSharing);
+        });
+        //空投
+        CompletableFuture<BigDecimal> airdropsFuture = CompletableFuture.supplyAsync(() -> {
+            List<UserCoinTransaction> teamSharing = walletService.getCoinTransactionByType(context.getId(), CoinTransactionCategoryEnum.RESOURCE_PLACEMENT);
+            return assistCalculate(teamSharing);
+        });
+        //查询用户升级信息
+        CompletableFuture<UserCoinSpeedUpgradesRecord> speedUpgradesRecordFuture = CompletableFuture.supplyAsync(() -> speedUpgradesRecordService.getSpeedUpgradesRecordByUserId(context.getId()));
+        CompletableFuture.allOf(userWalletFuture, totalProfitPerHourFuture, shareRewardsFuture, taskRewardsFuture, teamSharingFuture, airdropsFuture);
+
+        return new WalletInfoResp(userWalletFuture.join(), totalProfitPerHourFuture.join(), shareRewardsFuture.join(), taskRewardsFuture.join(), teamSharingFuture.join(), airdropsFuture.join(), speedUpgradesRecordFuture.join());
+    }
+
+    /**
+     * 辅助计算
+     *
+     * @param transactions 交易记录
+     */
+    private BigDecimal assistCalculate(List<UserCoinTransaction> transactions) {
+        if (CollUtil.isNotEmpty(transactions)) {
+            return transactions.stream().filter(trans -> ObjUtil.isNotNull(trans.getAmount()))
+                    .map(transaction -> {
+                        if (Objects.equals(transaction.getTransactionType(), CoinTransactionTypeEnum.ADD.getCode())) {
+                            // 增加
+                            return transaction.getAmount();
+                        } else if (Objects.equals(transaction.getTransactionType(), CoinTransactionTypeEnum.SUBTRACT.getCode())) {
+                            // 消耗(负值)
+                            return transaction.getAmount().negate();
+                        } else {
+                            // 未知类型,当作0处理
+                            return BigDecimal.ZERO;
+                        }
+                    })
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+        }
+        return BigDecimal.ZERO;
+    }
+}

+ 15 - 4
src/main/java/com/xs/core/service/competition/CompetitionService.java

@@ -1,5 +1,6 @@
 package com.xs.core.service.competition;
 
+import com.xs.core.model.user.resp.TeamCompetitionResp;
 import com.xs.core.model.user.resp.UserCompetitionResp;
 
 import java.util.List;
@@ -9,11 +10,21 @@ import java.util.List;
  */
 public interface CompetitionService {
     /**
-     * 金币排行榜
+     * 个人金币排行榜
      *
-     * @param limit    限制
-     * @param invitees 最小邀请人数
+     * @param limit 限制
      * @return 用户金币排行信息
      */
-    List<UserCompetitionResp> goldCoinCompetitionRanking(int limit, int invitees);
+    List<UserCompetitionResp> individualGoldCoinCompetitionRanking(int limit);
+
+    /**
+     * 团队邀请排行榜
+     *
+     * @param limit
+     * @param invitee
+     * @return
+     */
+    List<TeamCompetitionResp> teamInviteCompetitionRanking(int limit, int invitee);
+
+    UserCompetitionResp getIndividualGoldCoinCompetitionInfo(Long userId);
 }

+ 19 - 0
src/main/java/com/xs/core/service/team/IInviteRewardsRuleService.java

@@ -0,0 +1,19 @@
+package com.xs.core.service.team;
+
+import com.xs.core.model.team.entity.InviteRewardsRule;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励规则 服务类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+public interface IInviteRewardsRuleService extends IService<InviteRewardsRule> {
+
+    List<InviteRewardsRule> getAllByOrderByRewardsLevelAsc();
+}

+ 23 - 0
src/main/java/com/xs/core/service/team/IInviteTaskRewardsFetchRecordService.java

@@ -0,0 +1,23 @@
+package com.xs.core.service.team;
+
+import com.xs.core.model.team.entity.InviteTaskRewardsFetchRecord;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 邀请用户任务金币奖励领取记录 服务类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-23
+ */
+public interface IInviteTaskRewardsFetchRecordService extends IService<InviteTaskRewardsFetchRecord> {
+    /**
+     * 获取最后一条记录
+     *
+     * @param id 用户id
+     */
+    InviteTaskRewardsFetchRecord getLastByUserId(Long id);
+
+    InviteTaskRewardsFetchRecord getByRuleId(String ruleId);
+}

+ 1 - 0
src/main/java/com/xs/core/service/team/ITeamInviteConfigService.java

@@ -1,5 +1,6 @@
 package com.xs.core.service.team;
 
+import com.xs.core.model.team.entity.InviteRewardsRule;
 import com.xs.core.model.team.entity.TeamInviteConfig;
 import com.baomidou.mybatisplus.extension.service.IService;
 

+ 9 - 0
src/main/java/com/xs/core/service/team/ITeamInviteRewardRecordService.java

@@ -2,6 +2,9 @@ package com.xs.core.service.team;
 
 import com.xs.core.model.team.entity.TeamInviteRewardRecord;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.xs.core.model.team.resp.TeamInviteRewardsResp;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +16,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ITeamInviteRewardRecordService extends IService<TeamInviteRewardRecord> {
 
+    /**
+     * 获取团队邀请奖励列表 固定奖励 邀请1人奖励1000金币
+     *
+     * @param id 用户id
+     */
+    List<TeamInviteRewardsResp> getTeamInviteRewardRecordList(Long id);
 }

+ 21 - 0
src/main/java/com/xs/core/service/team/ITeamShareRewardRecordService.java

@@ -2,6 +2,10 @@ package com.xs.core.service.team;
 
 import com.xs.core.model.team.entity.TeamShareRewardRecord;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.xs.core.model.team.resp.TeamShareUserResp;
+import com.xs.core.model.team.resp.TeamUserResp;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +17,21 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ITeamShareRewardRecordService extends IService<TeamShareRewardRecord> {
 
+    /**
+     * 获取团队产出用户信息 待领取
+     *
+     * @param userId 用户id
+     * @param type   团队类型
+     */
+    List<TeamShareUserResp> getTeamShareUserInfoByType(Long userId, String type);
+
+    /**
+     * 获取未领取的团队产出奖励记录
+     *
+     * @param userId
+     * @param type
+     * @return
+     */
+    List<TeamShareRewardRecord> getNoClaimTeamShareRewardRecordByType(Long userId, String type);
+
 }

+ 40 - 12
src/main/java/com/xs/core/service/team/TeamShareService.java

@@ -1,9 +1,8 @@
 package com.xs.core.service.team;
 
 import com.xs.core.model.coin.msg.TeamShareMessage;
-import com.xs.core.model.team.resp.TeamUserResp;
-import com.xs.core.model.team.resp.TeamsInfoResp;
-import com.xs.core.model.user.entity.TgUser;
+import com.xs.core.model.team.req.TeamShareReq;
+import com.xs.core.model.team.resp.*;
 
 import java.util.List;
 
@@ -28,19 +27,48 @@ public interface TeamShareService {
     void teamInviteGoldCoinRewardGeneral(Long inviterId, Long inviteeId);
 
     /**
-     * 获取我的团队用户列表
+     * 获取我的团队信息
      *
-     * @param userId   用户id
-     * @param teamType 团队类型
-     * @return 我的团队用户列表
+     * @param id 用户id
      */
-    List<TeamUserResp> getMyTeamUserList(Long userId, String teamType);
+    TeamsInfoResp getMyTeamsInfo(Long id);
 
     /**
-     * 获取我的团队信息
+     * 获取团队产出用户信息
      *
-     * @param id
-     * @return
+     * @param id  用户id
+     * @param req 请求体
      */
-    TeamsInfoResp getMyTeamsInfo(Long id);
+    TeamShareInfoResp getTeamShareUserInfoByType(Long id, TeamShareReq req);
+
+    /**
+     * 收取团队产出奖励
+     *
+     * @param id  用户id
+     * @param req 请求体
+     */
+    void claimTeamShareReward(Long id, TeamShareReq req);
+
+    /**
+     * 获取团队邀请奖励列表 固定奖励 邀请1人奖励1000金币
+     *
+     * @param id 用户id
+     */
+    List<TeamInviteRewardsResp> getTeamInviteRewardsList(Long id);
+
+    /**
+     * 收取团队邀请奖励 固定奖励 邀请1人奖励1000金币
+     *
+     * @param id 用户id
+     */
+    void claimTeamInviteReward(Long id);
+
+    TeamInviteAvailableRewardsResp getTeamInviteAvailableRewardsRules(Long id);
+
+    /**
+     * 领取团队邀请任务奖励
+     *
+     * @param id 用户id
+     */
+    void claimTeamInviteTaskReward(Long id, String ruleId);
 }

+ 2 - 2
src/main/java/com/xs/core/service/user/AppLoginService.java

@@ -1,7 +1,7 @@
 package com.xs.core.service.user;
 
 import cn.dev33.satoken.stp.SaTokenInfo;
-import com.xs.core.model.user.entity.TgUser;
+import com.xs.core.model.user.entity.User;
 import com.xs.core.model.user.req.AppLoginReq;
 
 public interface AppLoginService {
@@ -19,5 +19,5 @@ public interface AppLoginService {
      * @param loginReq
      * @return
      */
-    TgUser registerToSys(AppLoginReq loginReq);
+    User registerToSys(AppLoginReq loginReq);
 }

+ 18 - 14
src/main/java/com/xs/core/service/user/ITgUserService.java → src/main/java/com/xs/core/service/user/IUserService.java

@@ -3,7 +3,7 @@ package com.xs.core.service.user;
 import com.xs.core.common.enums.CoinTransactionCategoryEnum;
 import com.xs.core.common.enums.CoinTransactionTypeEnum;
 import com.xs.core.model.team.resp.TeamUserResp;
-import com.xs.core.model.user.entity.TgUser;
+import com.xs.core.model.user.entity.User;
 import com.baomidou.mybatisplus.extension.service.IService;
 
 import java.math.BigDecimal;
@@ -15,28 +15,32 @@ import java.util.List;
  * </p>
  *
  * @author xudm
- * @since 2024-12-18
+ * @since 2024-12-22
  */
-public interface ITgUserService extends IService<TgUser> {
+public interface IUserService extends IService<User> {
+
     /**
-     * 用户金币交易
+     * 获取用户的上一级邀请人
      *
-     * @param type
-     * @param category
-     * @param reason
-     * @param detail
-     * @param amount
      * @param userId
+     * @return
      */
-    void coinTransaction(CoinTransactionTypeEnum type, CoinTransactionCategoryEnum category, String reason, String detail, BigDecimal amount, Long userId);
+    User getInviterByUserId(Long userId);
+
+    List<TeamUserResp> getMyTeamUserList(Long userId, String teamType);
 
     /**
-     * 获取用户的上一级邀请人
+     * 获取用户的邀请码
      *
-     * @param userId
      * @return
      */
-    TgUser getInviterByUserId(Long userId);
+    String getInviteCode();
 
-    List<TeamUserResp> getMyTeamUserList(Long userId, String teamType);
+    /**
+     * 查询用户的邀请人数
+     *
+     * @param id
+     * @return
+     */
+    Long queryInviterCount(Long id);
 }

+ 49 - 0
src/main/java/com/xs/core/service/wallet/IUserWalletService.java

@@ -0,0 +1,49 @@
+package com.xs.core.service.wallet;
+
+import com.xs.core.common.enums.CoinTransactionCategoryEnum;
+import com.xs.core.common.enums.CoinTransactionTypeEnum;
+import com.xs.core.model.coin.entity.UserCoinTransaction;
+import com.xs.core.model.wallet.entity.UserWallet;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * <p>
+ * 用户钱包 服务类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-22
+ */
+public interface IUserWalletService extends IService<UserWallet> {
+    UserWallet getWalletByUserId(Long userId);
+
+    /**
+     * 用户金币交易
+     *
+     * @param type     交易类型 增加/减少
+     * @param category 交易类型
+     * @param reason   交易原因
+     * @param detail   交易详情
+     * @param amount   交易数量
+     * @param userId   用户id
+     */
+    void coinTransaction(CoinTransactionTypeEnum type, CoinTransactionCategoryEnum category, String reason, String detail, BigDecimal amount, Long userId);
+
+    /**
+     * 获取钱包信息
+     *
+     * @param id 用户id
+     */
+    UserWallet getWalletInfoByUserId(Long id);
+
+    /**
+     * 获取用户交易记录
+     *
+     * @param id       用户id
+     * @param category 交易类型
+     */
+    List<UserCoinTransaction> getCoinTransactionByType(Long id, CoinTransactionCategoryEnum category);
+}

+ 15 - 0
src/main/java/com/xs/core/service/wallet/WalletService.java

@@ -0,0 +1,15 @@
+package com.xs.core.service.wallet;
+
+import com.xs.core.model.wallet.resp.WalletInfoResp;
+
+/**
+ * 钱包服务
+ */
+public interface WalletService {
+    /**
+     * 获取钱包信息
+     *
+     * @return
+     */
+    WalletInfoResp getWalletInfo();
+}

+ 2 - 2
src/main/java/com/xs/core/utils/MyGenerator.java

@@ -25,7 +25,7 @@ import java.util.Map;
 public class MyGenerator {
 
     //数据库连接信息
-    static String url = "jdbc:mysql://192.168.241.130:3306/continew?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai",
+    static String url = "jdbc:mysql://192.168.241.131:3306/continew?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai",
             username = "continew",
             password = "continew123456";
 
@@ -57,7 +57,7 @@ public class MyGenerator {
 
     public static void main(String[] args) {
         // 项目名 例如app  web
-        generatorByBusinessModule("team", new String[]{"b_team_invite_reward_record"});
+        generatorByBusinessModule("team", new String[]{"b_invite_rewards_rule", "b_invite_task_rewards_fetch_record"});
     }
 
     /**

+ 4 - 4
src/main/resources/application-dev.yml

@@ -1,7 +1,7 @@
 spring:
   data:
     redis:
-      host: 192.168.241.130
+      host: 192.168.241.131
       port: 6379
       password: xudm5200
       database: 6
@@ -17,7 +17,7 @@ spring:
           min-idle: 2
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://192.168.241.130:3306/continew?useUnicode=true&characterEncoding=utf-8&useSSL=false
+    url: jdbc:mysql://192.168.241.131:3306/continew?useUnicode=true&characterEncoding=utf-8&useSSL=false
     username: continew
     password: continew123456
     hikari:
@@ -38,7 +38,7 @@ spring:
       initialization-fail-timeout: 60000
   rabbitmq:
     virtual-host: /
-    host: 192.168.241.130
+    host: 192.168.241.131
     port: 5672
     username: admin
     password: admin
@@ -135,7 +135,7 @@ sa-token:
 
 # 配置请求是否加密
 encryption:
-  isEncryption: true
+  isEncryption: false
   requestPrivateKey: 14FDF4948E60F856524FBB1175E83716558B8CBF71A68318875F506B9C6D2A4A
   requestPublicKey: 04F36CD10986CE214D0E5C540C30E0552DC8499B64E5B2709245D03BF2CADAA0CCA3C2BC2C8DB511012A50FAA1E43FCD4B8ABC521418EAB2D96F0075AD940EB25F
   responsePrivateKey: 00C83135E19EBD958593091F42A3442DE3D03D975A5DBD4CE19F85C9FBF2D364B7

+ 1 - 1
src/main/resources/application-prod.yml

@@ -135,7 +135,7 @@ sa-token:
 
 # 配置请求是否加密
 encryption:
-  isEncryption: true
+  isEncryption: false
   requestPrivateKey: 14FDF4948E60F856524FBB1175E83716558B8CBF71A68318875F506B9C6D2A4A
   requestPublicKey: 04F36CD10986CE214D0E5C540C30E0552DC8499B64E5B2709245D03BF2CADAA0CCA3C2BC2C8DB511012A50FAA1E43FCD4B8ABC521418EAB2D96F0075AD940EB25F
   responsePrivateKey: 00C83135E19EBD958593091F42A3442DE3D03D975A5DBD4CE19F85C9FBF2D364B7

+ 17 - 0
src/main/resources/mapper/InviteRewardsRuleMapper.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.xs.core.mapper.team.InviteRewardsRuleMapper">
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.xs.core.model.team.entity.InviteRewardsRule">
+        <id column="id" property="id"/>
+        <result column="rewards_name" property="rewardsName"/>
+        <result column="rewards_describe" property="rewardsDescribe"/>
+        <result column="invite_num" property="inviteNum"/>
+        <result column="gold_coin_num" property="goldCoinNum"/>
+        <result column="rewards_level" property="rewardsLevel"/>
+        <result column="created_by" property="createdBy"/>
+        <result column="created_time" property="createdTime"/>
+        <result column="updated_by" property="updatedBy"/>
+        <result column="updated_time" property="updatedTime"/>
+    </resultMap>
+</mapper>

+ 13 - 0
src/main/resources/mapper/InviteTaskRewardsFetchRecordMapper.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.xs.core.mapper.team.InviteTaskRewardsFetchRecordMapper">
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.xs.core.model.team.entity.InviteTaskRewardsFetchRecord">
+        <id column="id" property="id"/>
+        <result column="user_id" property="userId"/>
+        <result column="rule_id" property="ruleId"/>
+        <result column="rewards_level" property="rewardsLevel"/>
+        <result column="gold_coin_num" property="goldCoinNum"/>
+        <result column="created_time" property="createdTime"/>
+    </resultMap>
+</mapper>

+ 25 - 0
src/main/resources/mapper/TeamInviteRewardRecordMapper.xml

@@ -11,4 +11,29 @@
         <result column="claim_time" property="claimTime"/>
         <result column="create_time" property="createTime"/>
     </resultMap>
+
+    <resultMap id="TeamInviteRewardsRespResultMap" type="com.xs.core.model.team.resp.TeamInviteRewardsResp">
+        <result column="tg_account" property="tgAccount"/>
+        <result column="first_name" property="firstName"/>
+        <result column="last_name" property="lastName"/>
+        <result column="nickname" property="nickname"/>
+        <result column="real_name" property="realName"/>
+        <result column="avatar" property="avatar"/>
+        <result column="reward_amount" property="inviteRewardAmount"/>
+    </resultMap>
+
+    <select id="getTeamInviteRewardsRespList" resultMap="TeamInviteRewardsRespResultMap">
+        select t1.reward_amount,
+               t1.claim_status,
+               t2.nickname,
+               t2.avatar,
+               t2.tg_account,
+               t2.first_name,
+               t2.last_name,
+               t2.real_name
+        from b_team_invite_reward_record t1
+                 left join b_user t2 on t1.invitee_id = t2.id
+        where t1.receiver_id = #{id}
+          and t1.claim_status = 0
+    </select>
 </mapper>

+ 34 - 0
src/main/resources/mapper/TeamShareRewardRecordMapper.xml

@@ -15,4 +15,38 @@
         <result column="claim_time" property="claimTime"/>
         <result column="create_time" property="createTime"/>
     </resultMap>
+
+
+    <resultMap id="TeamUserRespResultMap" type="com.xs.core.model.team.resp.TeamShareUserResp">
+        <result column="tg_id" property="tgId"/>
+        <result column="tg_account" property="tgAccount"/>
+        <result column="first_name" property="firstName"/>
+        <result column="last_name" property="lastName"/>
+        <result column="nickname" property="nickname"/>
+        <result column="real_name" property="realName"/>
+        <result column="avatar" property="avatar"/>
+        <result column="team_type" property="teamType"/>
+        <result column="gold_coin_yield_total" property="userYieldTotal"/>
+        <result column="reward_amount" property="teamRewardAmount"/>
+    </resultMap>
+
+
+    <select id="getTeamShareUserInfoByType" resultMap="TeamUserRespResultMap">
+        select t.gold_coin_yield_total,
+        t.reward_amount,
+        u.tg_id,
+        u.tg_account,
+        u.first_name,
+        u.last_name,
+        u.nickname,
+        u.real_name,
+        u.avatar
+        from b_team_share_reward_record t
+        inner join b_user u on u.id = t.invitee_id
+        where t.target_id = #{userId} and t.claim_status = 0
+        <if test="type != null and type != ''">
+            and t.team_type = #{type}
+        </if>
+        order by t.create_time desc, t.gold_coin_yield_total desc;
+    </select>
 </mapper>

+ 76 - 45
src/main/resources/mapper/TgUserMapper.xml → src/main/resources/mapper/UserMapper.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.xs.core.mapper.user.TgUserMapper">
+<mapper namespace="com.xs.core.mapper.user.UserMapper">
     <!-- 通用查询映射结果 -->
-    <resultMap id="BaseResultMap" type="com.xs.core.model.user.entity.TgUser">
+    <resultMap id="BaseResultMap" type="com.xs.core.model.user.entity.User">
         <id column="id" property="id"/>
         <result column="tg_id" property="tgId"/>
         <result column="tg_account" property="tgAccount"/>
@@ -29,11 +29,7 @@
         <result column="sex" property="sex"/>
         <result column="invite_code" property="inviteCode"/>
         <result column="referrer_id" property="referrerId"/>
-        <result column="airdrop_coin" property="airdropCoin"/>
-        <result column="gold_coin_amount" property="goldCoinAmount"/>
-        <result column="gold_coin_total_his" property="goldCoinTotalHis"/>
         <result column="online_time" property="onlineTime"/>
-        <result column="user_amount" property="userAmount"/>
         <result column="created_time" property="createdTime"/>
         <result column="updated_time" property="updatedTime"/>
     </resultMap>
@@ -46,7 +42,6 @@
         <result column="nickname" property="nickname"/>
         <result column="real_name" property="realName"/>
         <result column="avatar" property="avatar"/>
-        <result column="passenger_flow_way" property="passengerFlowWay"/>
         <result column="team_type" property="teamType"/>
     </resultMap>
 
@@ -60,12 +55,23 @@
         <result column="nickname" property="nickname"/>
         <result column="real_name" property="realName"/>
         <result column="avatar" property="avatar"/>
-        <result column="referrer_id" property="referrerId"/>
-        <result column="airdrop_coin" property="airdropCoin"/>
-        <result column="gold_coin_amount" property="goldCoinAmount"/>
-        <result column="gold_coin_total_his" property="goldCoinTotalHis"/>
+        <result column="available_balance" property="goldCoinAmount"/>
+        <result column="total_balance" property="goldCoinTotalHis"/>
     </resultMap>
 
+    <resultMap id="TeamCompetitionResultMap" type="com.xs.core.model.user.resp.TeamCompetitionResp">
+        <result column="ranking" property="ranking"/>
+        <result column="tg_id" property="tgId"/>
+        <result column="tg_account" property="tgAccount"/>
+        <result column="first_name" property="firstName"/>
+        <result column="last_name" property="lastName"/>
+        <result column="nickname" property="nickname"/>
+        <result column="real_name" property="realName"/>
+        <result column="avatar" property="avatar"/>
+        <result column="invited_users_count" property="invitedUsersCount"/>
+    </resultMap>
+
+
     <select id="getMyTeamUserList" resultMap="TeamUserRespResultMap">
         WITH RECURSIVE team_hierarchy AS (
         -- A队:直接被你邀请的用户
@@ -77,11 +83,10 @@
         nickname,
         real_name,
         avatar,
-        passenger_flow_way,
         referrer_id,
         'A' as team_type,
         1 as level
-        FROM b_tg_user
+        FROM b_user
         WHERE referrer_id = #{userId}
 
         UNION ALL
@@ -95,11 +100,10 @@
         u.nickname,
         u.real_name,
         u.avatar,
-        u.passenger_flow_way,
         u.referrer_id,
         'B' as team_type,
         h.level + 1
-        FROM b_tg_user u
+        FROM b_user u
         JOIN team_hierarchy h ON u.referrer_id = h.id
         -- 只获取A队成员邀请的用户 即b队的用户
         WHERE h.level = 1 )
@@ -112,7 +116,6 @@
         nickname,
         real_name,
         avatar,
-        passenger_flow_way,
         referrer_id,
         team_type
         FROM team_hierarchy h
@@ -139,7 +142,7 @@
         created_time,
         'A' as team_type,
         1 as level
-        FROM b_tg_user
+        FROM b_user
         WHERE referrer_id = #{userId}
 
         UNION ALL
@@ -158,7 +161,7 @@
         u.created_time,
         'B' as team_type,
         h.level + 1
-        FROM b_tg_user u
+        FROM b_user u
         JOIN team_hierarchy h ON u.referrer_id = h.id
         WHERE h.level = 1 -- 只获取A队成员邀请的用户 即b队的用户
         )
@@ -172,40 +175,68 @@
         </where>
     </select>
 
-    <select id="goldCoinCompetitionRanking" resultMap="UserCompetitionResultMap">
+
+    <select id="individualGoldCoinCompetitionRanking" resultMap="UserCompetitionResultMap">
+        select t.uid,
+               t.available_balance,
+               t.total_balance,
+               t.tg_id,
+               t.tg_account,
+               t.first_name,
+               t.last_name,
+               t.nickname,
+               t.avatar,
+               row_number() over (order by t.available_balance desc ) as ranking
+        from (select u.id as uid,
+                     u.tg_id,
+                     u.first_name,
+                     u.last_name,
+                     u.nickname,
+                     u.tg_account,
+                     u.avatar,
+                     buw.*
+              from b_user u
+                       inner join b_user_wallet buw on buw.user_id = u.id) t
+        limit #{limit};
+    </select>
+
+    <select id="getInviterByUserId" resultMap="BaseResultMap">
+        select iu.*
+        from b_user u
+                 inner join b_user iu on u.referrer_id = iu.id
+        where u.id = #{userId};
+    </select>
+
+    <select id="teamInviteCompetitionRanking" resultMap="TeamCompetitionResultMap">
         WITH invite_count AS (
             -- 统计每个用户的邀请人数
             SELECT referrer_id,
                    COUNT(*) as invited_users_count
-            FROM b_tg_user
+            FROM b_user
             WHERE referrer_id IS NOT NULL
             GROUP BY referrer_id
-            HAVING COUNT(*) >= #{invitees})
-
-        SELECT u.tg_id,
-               u.tg_account,
-               u.first_name,
-               u.last_name,
-               u.airdrop_coin,
-               u.nickname,
-               u.real_name,
-               u.gold_coin_amount,
-               u.referrer_id,
-               u.gold_coin_total_his,
-               u.avatar,
-               -- 添加排名
-               ROW_NUMBER() OVER (ORDER BY u.gold_coin_total_his DESC) as ranking
-        FROM b_tg_user u
-                 INNER JOIN invite_count ic ON u.id = ic.referrer_id
-        WHERE u.disable_flag = 0 -- 排除被禁用的用户
-        ORDER BY u.gold_coin_total_his desc
-        limit #{limit};
+            HAVING COUNT(*) >= #{invitee})
+        select u.*,
+               ic.invited_users_count                                    as guests,
+               row_number() over (order by ic.invited_users_count desc ) as ranking
+        from b_user u
+                 inner join invite_count ic on ic.referrer_id = u.id
+        limit #{limit}
     </select>
 
-    <select id="getInviterByUserId" resultMap="BaseResultMap">
-        select iu.*
-        from b_tg_user u
-                 inner join b_tg_user iu on u.referrer_id = iu.id
-        where u.id = #{userId};
+    <select id="getIndividualGoldCoinCompetitionInfo" resultMap="UserCompetitionResultMap">
+        WITH RankedWallet AS (SELECT user_id,
+                                     available_balance,
+                                     row_number() OVER (ORDER BY available_balance DESC) AS ranking
+                              FROM b_user_wallet bu),
+
+             RankedWalletUser as (SELECT user_id,
+                                         available_balance,
+                                         ranking
+                                  FROM RankedWallet
+                                  where user_id = #{userId})
+        select u.tg_id, u.first_name, u.last_name, u.tg_account, u.avatar, ru.available_balance, ru.ranking
+        from b_user u
+                 inner join RankedWalletUser ru on ru.user_id = u.id
     </select>
 </mapper>

+ 28 - 0
src/main/resources/messages/messages_en.properties

@@ -0,0 +1,28 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+response.validation.failed=输入验证失败
+validation.team.type=团队类型不能为空
+error.team.share.reward.empty=没有可领取的团队奖励
+error.team.invite.reward.empty=没有可领取的邀请奖励
+

+ 24 - 0
src/main/resources/messages/messages_fa.properties

@@ -0,0 +1,24 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+validation.team.type=团队类型不能为空

+ 24 - 0
src/main/resources/messages/messages_ja.properties

@@ -0,0 +1,24 @@
+response.success=cessfasfa
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+validation.team.type=团队类型不能为空

+ 24 - 0
src/main/resources/messages/messages_ko.properties

@@ -0,0 +1,24 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+validation.team.type=团队类型不能为空

+ 24 - 0
src/main/resources/messages/messages_pt.properties

@@ -0,0 +1,24 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+validation.team.type=团队类型不能为空

+ 23 - 0
src/main/resources/messages/messages_ru.properties

@@ -0,0 +1,23 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} with {1} [{2}] does not exist
+error.exists={0} with {1} [{2}] already exists
+validation.team.type=团队类型不能为空

+ 24 - 0
src/main/resources/messages/messages_th.properties

@@ -0,0 +1,24 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+validation.team.type=团队类型不能为空

+ 25 - 0
src/main/resources/messages/messages_vi.properties

@@ -0,0 +1,25 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+validation.team.type=团队类型不能为空
+error.team.share.reward.empty=没有可领取的团队奖励345

+ 34 - 0
src/main/resources/messages/messages_zh_CN.properties

@@ -0,0 +1,34 @@
+response.success=操作成功
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+response.validation.failed=输入验证失败
+validation.team.type=团队类型不能为空
+validation.team.claim.ruleId=规则id不能为空
+error.team.share.reward.empty=没有可领取的团队奖励
+error.team.invite.reward.empty=没有可领取的邀请奖励
+error.team.invite.reward.task.claim.done=邀请任务已完成
+error.team.invite.reward.task.claim.exists=该等级的邀请奖励已领取
+error.team.invite.reward.task.claim.previous.not.claim=上一等级的邀请奖励未领取
+error.team.invite.reward.task.claim.not.enough=当前邀请任务未完成,邀请好友数不足
+error.team.invite.reward.task.claim.not.found=邀请奖励规则不存在
+

+ 24 - 0
src/main/resources/messages/messages_zh_TW.properties

@@ -0,0 +1,24 @@
+response.success=操作成功001
+response.server.error=服务器错误
+response.not.found=找不到资源
+response.unsupported.media.type=不支持的媒体类型
+response.request.too.large=请求体过大
+response.bad.request=请求参数错误
+response.forbidden=没有权限
+response.unauthorized=用户未登录或用户信息为空
+response.failed=操作失败
+response.expected.file.missing=预期的文件缺失
+request.api.not.exists=请求的接口不存在
+response.error.encrypt=加解密错误
+no.gold.claimed=没有待领取的金币
+response.user.disable=此账号已被禁用,如有疑问,请联系管理员
+error.gold.product.record.not.exists=金币产出速率升级规则不存在
+error.gold.product.not.start=用户未开启金币产出
+error.gold.product.end=金币产出结束或未开启金币产出,加成已失效
+error.gold.product.coin.not.claimed=待领取的金币未领取,不能重复开启
+error.gold.product.exists=金币产出中,不能重复开启
+error.gold.boost.temporary.exists=当前生产批次已存在临时速率,暂不允许升级
+error.gold.insufficient=金币不足
+error.not.exists={0} 为 [{1}] 的 {2} 记录已不存在
+error.exists={0} 为 [{1}] 的 {2} 记录已存在
+validation.team.type=团队类型不能为空