Browse Source

feat(cf): 推荐绑定物化推荐树 + 团队规模全层级统计 + 阶梯比例刷新(替代 promotion_tier_config)

Sisyphus 2 weeks ago
parent
commit
5df4691f7b

+ 2 - 10
cfc-backend/src/main/java/com/etotem/cfc/entity/PromotionTier.java

@@ -1,7 +1,6 @@
 package com.etotem.cfc.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 lombok.Data;
@@ -20,21 +19,14 @@ public class PromotionTier implements Serializable {
 
     private String tier;
 
-    @TableField("team_size_1st")
-    private Integer teamSize1st;
-
-    @TableField("team_size_2nd")
-    private Integer teamSize2nd;
-
-    @TableField("team_size_3rd")
-    private Integer teamSize3rd;
-
     private Integer totalTeamSize;
 
     private Integer totalReferralEarnings;
 
     private Integer totalShareEarnings;
 
+    private Integer ratePercent;
+
     private Date promotedAt;
 
     private Date lastChangeAt;

+ 4 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/PromotionTierConfig.java

@@ -7,6 +7,10 @@ import lombok.Data;
 import java.io.Serializable;
 import java.util.Date;
 
+/**
+ * 已废弃,改用 cf_rate_tier
+ */
+@Deprecated
 @Data
 @TableName("promotion_tier_config")
 public class PromotionTierConfig implements Serializable {

+ 6 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/CommissionService.java

@@ -72,6 +72,9 @@ public class CommissionService {
     @Resource
     private PromotionTierConfigMapper configMapper;
 
+    @Resource
+    private CfReferralService cfReferralService;
+
     // ==================== 工具方法 ====================
 
     /**
@@ -129,6 +132,9 @@ public class CommissionService {
         user.setReferrerId(referrer.getId());
         userMapper.updateById(user);
 
+        // 物化推荐树(全层级)
+        try { cfReferralService.materializeReferralTree(userId); } catch (Exception e) { log.warn("推荐树物化失败 userId={}", userId, e); }
+
         // 更新推荐人直接推荐人数
         referrer.setDirectCount(referrer.getDirectCount() != null ? referrer.getDirectCount() + 1 : 1);
         userMapper.updateById(referrer);

+ 12 - 42
cfc-backend/src/main/java/com/etotem/cfc/service/PromotionTierEvalService.java

@@ -1,10 +1,8 @@
 package com.etotem.cfc.service;
 
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.entity.CfRateTier;
 import com.etotem.cfc.entity.PromotionTier;
-import com.etotem.cfc.entity.PromotionTierConfig;
 import com.etotem.cfc.entity.User;
-import com.etotem.cfc.mapper.PromotionTierConfigMapper;
 import com.etotem.cfc.mapper.PromotionTierMapper;
 import com.etotem.cfc.mapper.UserMapper;
 import org.slf4j.Logger;
@@ -27,15 +25,15 @@ public class PromotionTierEvalService {
     @Resource
     private PromotionTierMapper tierMapper;
 
-    @Resource
-    private PromotionTierConfigMapper configMapper;
-
     @Resource
     private PromotionTierService promotionTierService;
 
     @Resource
     private UserMapper userMapper;
 
+    @Resource
+    private CfReferralService cfReferralService;
+
     @Resource
     private WechatService wechatService;
 
@@ -43,45 +41,17 @@ public class PromotionTierEvalService {
     private String promotionTierTemplateId;
 
     /**
-     * 获取所有启用且按sortOrder desc排序的等级配置
-     */
-    public List<PromotionTierConfig> getEnabledConfigs() {
-        return configMapper.selectList(
-            new LambdaQueryWrapper<PromotionTierConfig>()
-                .eq(PromotionTierConfig::getEnabled, 1)
-                .orderByDesc(PromotionTierConfig::getSortOrder)
-        );
-    }
-
-    /**
-     * 根据用户的 PromotionTier 数据,计算应该属于哪个等级
-     * 遍历配置,从高到低找到第一个满足所有条件的等级
-     * @return 目标等级代码,如 "R2",如果没有任何配置匹配返回 "R0"
+     * 根据全层级团队规模计算应属等级(读 cf_rate_tier)
+     * @return 目标等级代码,如 "R2",无匹配返回 "R0"
      */
     public String evaluateTier(Long userId) {
-        PromotionTier tier = promotionTierService.getCurrentTier(userId);
-        List<PromotionTierConfig> configs = getEnabledConfigs();
-
-        int teamSize = tier != null && tier.getTotalTeamSize() != null ? tier.getTotalTeamSize() : 0;
-        int referralEarnings = tier != null && tier.getTotalReferralEarnings() != null ? tier.getTotalReferralEarnings() : 0;
-        int shareEarnings = tier != null && tier.getTotalShareEarnings() != null ? tier.getTotalShareEarnings() : 0;
-
-        // 从高等级到低等级遍历,找到第一个满足所有条件的
-        // configs 已按 sortOrder desc 排序(高等级在前)
-        for (PromotionTierConfig config : configs) {
-            boolean teamSizeOk = teamSize >= config.getMinTeamSize();
-            boolean referralOk = config.getMinReferralEarnings() == null
-                || config.getMinReferralEarnings() == 0
-                || referralEarnings >= config.getMinReferralEarnings();
-            boolean shareOk = config.getMinShareEarnings() == null
-                || config.getMinShareEarnings() == 0
-                || shareEarnings >= config.getMinShareEarnings();
-
-            if (teamSizeOk && referralOk && shareOk) {
-                return config.getTierCode();
-            }
+        try {
+            int teamSize = cfReferralService.getTotalTeamSize(userId);
+            CfRateTier tier = cfReferralService.matchRateTier(teamSize);
+            return tier == null ? "R0" : tier.getTierName();
+        } catch (Exception e) {
+            return "R0";
         }
-        return "R0";
     }
 
     /**

+ 34 - 21
cfc-backend/src/main/java/com/etotem/cfc/service/PromotionTierService.java

@@ -1,12 +1,15 @@
 package com.etotem.cfc.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.entity.CfRateTier;
 import com.etotem.cfc.entity.PromotionTier;
 import com.etotem.cfc.entity.PromotionTierChangeLog;
 import com.etotem.cfc.entity.PromotionTierConfig;
 import com.etotem.cfc.mapper.PromotionTierChangeLogMapper;
 import com.etotem.cfc.mapper.PromotionTierConfigMapper;
 import com.etotem.cfc.mapper.PromotionTierMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
@@ -16,6 +19,8 @@ import java.util.List;
 @Service
 public class PromotionTierService {
 
+    private static final Logger log = LoggerFactory.getLogger(PromotionTierService.class);
+
     @Resource
     private PromotionTierMapper tierMapper;
 
@@ -25,6 +30,9 @@ public class PromotionTierService {
     @Resource
     private PromotionTierConfigMapper tierConfigMapper;
 
+    @Resource
+    private CfReferralService cfReferralService;
+
     /**
      * 获取用户当前推广等级
      */
@@ -59,17 +67,14 @@ public class PromotionTierService {
     }
 
     /**
-     * 更新团队规模(当有新推荐人加入时调用)
+     * 团队规模 +delta(新推荐绑定后对所有祖先增量)。level 参数已废弃(改为全层级)。
      */
-    public void updateTeamSize(Long userId, int level, int delta) {
+    public void updateTeamSize(Long userId, int delta) {
         PromotionTier tier = getCurrentTier(userId);
         if (tier == null) {
             tier = new PromotionTier();
             tier.setUserId(userId);
             tier.setTier("R0");
-            tier.setTeamSize1st(0);
-            tier.setTeamSize2nd(0);
-            tier.setTeamSize3rd(0);
             tier.setTotalTeamSize(0);
             tier.setTotalReferralEarnings(0);
             tier.setTotalShareEarnings(0);
@@ -77,22 +82,33 @@ public class PromotionTierService {
             tier.setUpdatedAt(new Date());
             tierMapper.insert(tier);
         }
-
-        if (level == 1) {
-            tier.setTeamSize1st(tier.getTeamSize1st() + delta);
-        } else if (level == 2) {
-            tier.setTeamSize2nd(tier.getTeamSize2nd() + delta);
-        } else if (level == 3) {
-            tier.setTeamSize3rd(tier.getTeamSize3rd() + delta);
-        }
-        tier.setTotalTeamSize(
-                (tier.getTeamSize1st() != null ? tier.getTeamSize1st() : 0) +
-                (tier.getTeamSize2nd() != null ? tier.getTeamSize2nd() : 0) +
-                (tier.getTeamSize3rd() != null ? tier.getTeamSize3rd() : 0)
-        );
+        tier.setTotalTeamSize((tier.getTotalTeamSize() == null ? 0 : tier.getTotalTeamSize()) + delta);
         tier.setLastChangeAt(new Date());
         tier.setUpdatedAt(new Date());
         tierMapper.updateById(tier);
+        refreshRate(userId);
+    }
+
+    /**
+     * 按全层级团队规模刷新等级与返佣比例(读 cf_rate_tier)
+     */
+    public void refreshRate(Long userId) {
+        try {
+            int teamSize = cfReferralService.getTotalTeamSize(userId);
+            CfRateTier tier = cfReferralService.matchRateTier(teamSize);
+            PromotionTier pt = getCurrentTier(userId);
+            if (pt == null) return;
+            if (tier != null) {
+                pt.setTier(tier.getTierName());
+                pt.setRatePercent(tier.getRatePercent());
+            } else {
+                pt.setRatePercent(0);
+            }
+            pt.setUpdatedAt(new Date());
+            tierMapper.updateById(pt);
+        } catch (Exception e) {
+            log.warn("refreshRate失败 userId={}", userId, e);
+        }
     }
 
     /**
@@ -153,9 +169,6 @@ public class PromotionTierService {
             tier = new PromotionTier();
             tier.setUserId(userId);
             tier.setTier(tierCode);
-            tier.setTeamSize1st(0);
-            tier.setTeamSize2nd(0);
-            tier.setTeamSize3rd(0);
             tier.setTotalTeamSize(0);
             tier.setTotalReferralEarnings(0);
             tier.setTotalShareEarnings(0);

+ 16 - 7
cfc-backend/src/main/java/com/etotem/cfc/task/PromotionTierCheckScheduledTask.java

@@ -1,31 +1,40 @@
 package com.etotem.cfc.task;
 
-import com.etotem.cfc.service.PromotionTierEvalService;
+import com.etotem.cfc.entity.PromotionTier;
+import com.etotem.cfc.mapper.PromotionTierMapper;
+import com.etotem.cfc.service.PromotionTierService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
+import java.util.List;
 
 @Slf4j
 @Component
 public class PromotionTierCheckScheduledTask {
 
     @Resource
-    private PromotionTierEvalService evalService;
+    private PromotionTierService promotionTierService;
+
+    @Resource
+    private PromotionTierMapper promotionTierMapper;
 
     /**
      * 每天 1:00 AM 执行
-     * 遍历所有有推广等级记录的用户,检查是否需要晋级/降级
+     * 遍历所有有推广等级记录的用户,按全层级团队规模刷新等级与返佣比例
      */
     @Scheduled(cron = "0 0 1 * * ?")
     public void checkAndUpdateAllTiers() {
-        log.info("开始执行推广等级定时检查任务");
+        log.info("开始执行推广等级定时刷新任务");
         try {
-            int changed = evalService.evaluateAllUsers();
-            log.info("推广等级定时检查任务执行完成,变更{}个用户等级", changed);
+            List<PromotionTier> allTiers = promotionTierMapper.selectList(null);
+            for (PromotionTier pt : allTiers) {
+                try { promotionTierService.refreshRate(pt.getUserId()); } catch (Exception e) { /* 单用户失败不影响 */ }
+            }
+            log.info("推广等级定时刷新完成,共{}个用户", allTiers.size());
         } catch (Exception e) {
-            log.error("推广等级定时检查任务执行失败", e);
+            log.error("推广等级定时刷新失败", e);
         }
     }
 }