|
@@ -0,0 +1,364 @@
|
|
|
|
|
+package com.etotem.cfc.task;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
|
+import com.etotem.cfc.entity.DistributionRelation;
|
|
|
|
|
+import com.etotem.cfc.entity.ProfitPoolOrder;
|
|
|
|
|
+import com.etotem.cfc.entity.TeamAwardRecord;
|
|
|
|
|
+import com.etotem.cfc.entity.TeamShareRecord;
|
|
|
|
|
+import com.etotem.cfc.entity.TeamStarSnapshot;
|
|
|
|
|
+import com.etotem.cfc.mapper.DistributionRelationMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.ProfitPoolOrderMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.TeamAwardRecordMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.TeamProfitStatsMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.TeamShareRecordMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.TeamStarSnapshotMapper;
|
|
|
|
|
+import com.etotem.cfc.service.StarScoreCalculator;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import java.util.Calendar;
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 团队分润定时任务(§12-T4 + §8)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>规格:{@code docs/superpowers/specs/2026-11-07-referral-team-profit-sharing.md}
|
|
|
|
|
+ * v0.7 §8.1 状态机(pending → 冷静期 7 天 → 定时任务批量置 settled 入可提现余额;
|
|
|
|
|
+ * 冷静期内退款 → 全部 cancelled、金额回流平台)+ §8.2 冷静期与退款(7 天退款全退、
|
|
|
|
|
+ * 7 天后自动 settled)+ §4.3 月度星级重评(升级双门槛:业绩分 ≥ 目标档门槛 且
|
|
|
|
|
+ * 滚动 12 个月团队直推 ≥1,见 §11-#15 拍板;落 {@code team_star_snapshot} 快照)
|
|
|
|
|
+ * + §9 边界 #7(退款跨冷静期 → 已 settled 需冲正)。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>批次入口</b>:</p>
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li>{@link #settleDuePendingRecords()} 每日 03:00 冷静期满结算</li>
|
|
|
|
|
+ * <li>{@link #reevaluateMonthlyStars()} 每月 1 日 00:30 星级重评落快照</li>
|
|
|
|
|
+ * <li>{@link #cancelTeamRecordsForRefund(long)} 冷静期内退款冲销(退款服务回调)</li>
|
|
|
|
|
+ * <li>{@link #reverseSettledBalanceForCrossCoolingRefund(long)} 跨冷静期退款冲正(§9#7)</li>
|
|
|
|
|
+ * </ul>
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Component
|
|
|
|
|
+public class TeamProfitCronTask {
|
|
|
|
|
+
|
|
|
|
|
+ /** 冷静期 7 天(§8.2,与退款窗口对齐)。 */
|
|
|
|
|
+ public static final int COOLING_PERIOD_DAYS = 7;
|
|
|
|
|
+
|
|
|
|
|
+ /** 月度星级重评 cron:每月 1 日 00:30(§4.3 重评周期)。 */
|
|
|
|
|
+ public static final String MONTHLY_STAR_REEVAL_CRON = "0 30 0 1 * ?";
|
|
|
|
|
+
|
|
|
|
|
+ /** 冷静期结算 cron:每日 03:00(低峰)。 */
|
|
|
|
|
+ public static final String SETTLE_CRON = "0 0 3 * * ?";
|
|
|
|
|
+
|
|
|
|
|
+ private static final long DAY_MS = 24L * 60 * 60 * 1000;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TeamShareRecordMapper teamShareRecordMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TeamAwardRecordMapper teamAwardRecordMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ProfitPoolOrderMapper profitPoolOrderMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TeamStarSnapshotMapper teamStarSnapshotMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TeamProfitStatsMapper teamProfitStatsMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private DistributionRelationMapper distributionRelationMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private StarScoreCalculator starScoreCalculator;
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== §8.2 冷静期常量与判定 ====================
|
|
|
|
|
+
|
|
|
|
|
+ /** 冷静期天数(默认 7,§8.2 与退款窗口对齐)。 */
|
|
|
|
|
+ public int coolingPeriodDays() {
|
|
|
|
|
+ return COOLING_PERIOD_DAYS;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 支付时刻距今是否仍在冷静期内(now < paidAt + 7 天)。 */
|
|
|
|
|
+ public boolean isWithinCoolingPeriod(long paidAtEpochMs, long nowEpochMs) {
|
|
|
|
|
+ return nowEpochMs < paidAtEpochMs + (long) COOLING_PERIOD_DAYS * DAY_MS;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== §8.1 冷静期满 → 批量结算入可提现余额 ====================
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 冷静期满批量结算:支付满 7 天的 pending 分润(team_share_record / team_award_record)
|
|
|
|
|
+ * 置为 settled,入「可提现余额」(货币账本,余额由 settled 记录聚合派生);同步推进
|
|
|
|
|
+ * profit_pool_order 主单状态(§8.1 状态机)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 处理条数(team_share + team_award)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Scheduled(cron = SETTLE_CRON)
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public int settleDuePendingRecords() {
|
|
|
|
|
+ Date cutoff = new Date(System.currentTimeMillis() - (long) COOLING_PERIOD_DAYS * DAY_MS);
|
|
|
|
|
+ int processed = 0;
|
|
|
|
|
+ if (teamShareRecordMapper != null) {
|
|
|
|
|
+ List<TeamShareRecord> shares = teamShareRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamShareRecord>()
|
|
|
|
|
+ .eq(TeamShareRecord::getStatus, "pending")
|
|
|
|
|
+ .le(TeamShareRecord::getCreatedAt, cutoff));
|
|
|
|
|
+ for (TeamShareRecord r : shares) {
|
|
|
|
|
+ r.setStatus("settled");
|
|
|
|
|
+ teamShareRecordMapper.updateById(r);
|
|
|
|
|
+ }
|
|
|
|
|
+ processed += shares.size();
|
|
|
|
|
+ log.info("团队分润冷静期结算: team_share_record 结算 {} 条", shares.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (teamAwardRecordMapper != null) {
|
|
|
|
|
+ List<TeamAwardRecord> awards = teamAwardRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamAwardRecord>()
|
|
|
|
|
+ .eq(TeamAwardRecord::getStatus, "pending")
|
|
|
|
|
+ .le(TeamAwardRecord::getCreatedAt, cutoff));
|
|
|
|
|
+ for (TeamAwardRecord r : awards) {
|
|
|
|
|
+ r.setStatus("settled");
|
|
|
|
|
+ teamAwardRecordMapper.updateById(r);
|
|
|
|
|
+ }
|
|
|
|
|
+ processed += awards.size();
|
|
|
|
|
+ log.info("团队分润冷静期结算: team_award_record 结算 {} 条", awards.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (profitPoolOrderMapper != null) {
|
|
|
|
|
+ List<ProfitPoolOrder> pools = profitPoolOrderMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ProfitPoolOrder>()
|
|
|
|
|
+ .eq(ProfitPoolOrder::getStatus, "pending")
|
|
|
|
|
+ .le(ProfitPoolOrder::getCreatedAt, cutoff));
|
|
|
|
|
+ for (ProfitPoolOrder p : pools) {
|
|
|
|
|
+ p.setStatus("settled");
|
|
|
|
|
+ p.setSettledAt(new Date());
|
|
|
|
|
+ profitPoolOrderMapper.updateById(p);
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("团队分润冷静期结算: profit_pool_order 主单同步 {} 条", pools.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("团队分润冷静期结算完成: 处理 {} 条", processed);
|
|
|
|
|
+ return processed;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== §8.2 冷静期内退款:全部 cancelled、资金不发放 ====================
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 冷静期内订单退款:对应 team_share_record / team_award_record 全部置为 cancelled
|
|
|
|
|
+ * (资金不发放,金额回流平台见 {@link #refundRefluxAmountCent(long)}),
|
|
|
|
|
+ * profit_pool_order 主单同步 cancelled(§8.1/§8.2)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 取消条数(team_share + team_award)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public int cancelTeamRecordsForRefund(long orderId) {
|
|
|
|
|
+ int cancelled = 0;
|
|
|
|
|
+ if (teamShareRecordMapper != null) {
|
|
|
|
|
+ List<TeamShareRecord> shares = teamShareRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamShareRecord>()
|
|
|
|
|
+ .eq(TeamShareRecord::getOrderId, orderId)
|
|
|
|
|
+ .ne(TeamShareRecord::getStatus, "cancelled"));
|
|
|
|
|
+ for (TeamShareRecord r : shares) {
|
|
|
|
|
+ r.setStatus("cancelled");
|
|
|
|
|
+ teamShareRecordMapper.updateById(r);
|
|
|
|
|
+ }
|
|
|
|
|
+ cancelled += shares.size();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (teamAwardRecordMapper != null) {
|
|
|
|
|
+ List<TeamAwardRecord> awards = teamAwardRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamAwardRecord>()
|
|
|
|
|
+ .eq(TeamAwardRecord::getOrderId, orderId)
|
|
|
|
|
+ .ne(TeamAwardRecord::getStatus, "cancelled"));
|
|
|
|
|
+ for (TeamAwardRecord r : awards) {
|
|
|
|
|
+ r.setStatus("cancelled");
|
|
|
|
|
+ teamAwardRecordMapper.updateById(r);
|
|
|
|
|
+ }
|
|
|
|
|
+ cancelled += awards.size();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (profitPoolOrderMapper != null) {
|
|
|
|
|
+ List<ProfitPoolOrder> pools = profitPoolOrderMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ProfitPoolOrder>()
|
|
|
|
|
+ .eq(ProfitPoolOrder::getOrderId, orderId)
|
|
|
|
|
+ .ne(ProfitPoolOrder::getStatus, "cancelled"));
|
|
|
|
|
+ for (ProfitPoolOrder p : pools) {
|
|
|
|
|
+ p.setStatus("cancelled");
|
|
|
|
|
+ profitPoolOrderMapper.updateById(p);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("订单 {} 冷静期内退款: 取消分润记录 {} 条", orderId, cancelled);
|
|
|
|
|
+ return cancelled;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== §9 边界 #7 退款跨冷静期:已 settled 冲正 ====================
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 退款跨冷静期(已 settled)冲正(§9 边界 #7):把订单对应已 settled / pending 的
|
|
|
|
|
+ * team_share_record / team_award_record 全部置 cancelled —— settled 余额由记录聚合派生,
|
|
|
|
|
+ * 取消即等价于「cancelled + 余额扣减」,资金不再可提现,回流平台。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 冲正操作是否成功完成
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public boolean reverseSettledBalanceForCrossCoolingRefund(long orderId) {
|
|
|
|
|
+ int reversed = cancelTeamRecordsForRefund(orderId);
|
|
|
|
|
+ log.info("订单 {} 跨冷静期退款冲正: 冲销 {} 条分润记录(已 settled 余额扣减)", orderId, reversed);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 取消/冲正后分润金额回流平台(分,≥0):订单对应 team_share_record + team_award_record
|
|
|
|
|
+ * 原分润金额合计(含本次置 cancelled 的记录)。
|
|
|
|
|
+ */
|
|
|
|
|
+ public long refundRefluxAmountCent(long orderId) {
|
|
|
|
|
+ long reflux = 0L;
|
|
|
|
|
+ if (teamShareRecordMapper != null) {
|
|
|
|
|
+ List<TeamShareRecord> shares = teamShareRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamShareRecord>()
|
|
|
|
|
+ .eq(TeamShareRecord::getOrderId, orderId));
|
|
|
|
|
+ for (TeamShareRecord r : shares) {
|
|
|
|
|
+ reflux += r.getShareCent() == null ? 0L : r.getShareCent();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (teamAwardRecordMapper != null) {
|
|
|
|
|
+ List<TeamAwardRecord> awards = teamAwardRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamAwardRecord>()
|
|
|
|
|
+ .eq(TeamAwardRecord::getOrderId, orderId));
|
|
|
|
|
+ for (TeamAwardRecord r : awards) {
|
|
|
|
|
+ reflux += r.getAwardCent() == null ? 0L : r.getAwardCent();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return reflux;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== §4.3 月度星级重评:调度与快照 ====================
|
|
|
|
|
+
|
|
|
|
|
+ /** 月度重评 cron 表达式(每月 1 日 00:30)。 */
|
|
|
|
|
+ public String monthlyStarReevalCron() {
|
|
|
|
|
+ return MONTHLY_STAR_REEVAL_CRON;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 快照周期 YYYYMM(对应 §7.6 team_star_snapshot.period,唯一键 uk_user_period)。 */
|
|
|
|
|
+ public String snapshotPeriod(int year, int month) {
|
|
|
|
|
+ return String.format("%04d%02d", year, month);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 月度星级重评批次:对团队树全部成员重评并落 {@code team_star_snapshot} 快照。
|
|
|
|
|
+ * 每月 1 日 00:30 执行(§4.3 重评周期)。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Scheduled(cron = MONTHLY_STAR_REEVAL_CRON)
|
|
|
|
|
+ public void reevaluateMonthlyStars() {
|
|
|
|
|
+ if (distributionRelationMapper == null || teamStarSnapshotMapper == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Calendar now = Calendar.getInstance();
|
|
|
|
|
+ int year = now.get(Calendar.YEAR);
|
|
|
|
|
+ int month = now.get(Calendar.MONTH) + 1;
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<Object> userIds = distributionRelationMapper.selectObjs(
|
|
|
|
|
+ new QueryWrapper<DistributionRelation>().select("DISTINCT user_id"));
|
|
|
|
|
+ int done = 0;
|
|
|
|
|
+ for (Object id : userIds) {
|
|
|
|
|
+ if (id == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ long userId = ((Number) id).longValue();
|
|
|
|
|
+ int star = snapshotMonthlyStar(userId, year, month);
|
|
|
|
|
+ done++;
|
|
|
|
|
+ log.info("月度星级重评: userId={} period={} 星级={}", userId, snapshotPeriod(year, month), star);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("月度星级重评失败(单用户跳过): userId={} period={}: {}", id,
|
|
|
|
|
+ snapshotPeriod(year, month), e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("月度星级重评完成: 共 {} 人", done);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("月度星级重评批次执行失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 单用户月度星级重评:按滚动 12 个月业绩分 + 升级双门槛重评并落快照。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 本期星级(1-6)
|
|
|
|
|
+ */
|
|
|
|
|
+ public int snapshotMonthlyStar(long userId, int year, int month) {
|
|
|
|
|
+ String period = snapshotPeriod(year, month);
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.clear();
|
|
|
|
|
+ cal.set(year, month - 1, 1, 0, 0, 0);
|
|
|
|
|
+ Date monthStart = cal.getTime();
|
|
|
|
|
+ cal.add(Calendar.MONTH, -12);
|
|
|
|
|
+ Date windowStart = cal.getTime();
|
|
|
|
|
+
|
|
|
|
|
+ long directCent = 0L;
|
|
|
|
|
+ long teamCent = 0L;
|
|
|
|
|
+ long referralCent = 0L;
|
|
|
|
|
+ int directValidNewRefs = 0;
|
|
|
|
|
+ if (teamProfitStatsMapper != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ directCent = teamProfitStatsMapper.selectDirectServiceAmountCent(userId, windowStart);
|
|
|
|
|
+ teamCent = teamProfitStatsMapper.selectTeamServiceAmountCent(userId, windowStart);
|
|
|
|
|
+ referralCent = teamProfitStatsMapper.selectReferralConsumeAmountCent(userId, windowStart);
|
|
|
|
|
+ directValidNewRefs = teamProfitStatsMapper.countDirectValidNewRefs(userId, windowStart);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("月度星级重评统计查询失败(userId={}): {}", userId, e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // §4.3 业绩分(金额 分 → 元口径)与升级双门槛
|
|
|
|
|
+ long score = 0L;
|
|
|
|
|
+ int newStar = StarScoreCalculator.MIN_STAR;
|
|
|
|
|
+ int currentStar = StarScoreCalculator.MIN_STAR;
|
|
|
|
|
+ if (starScoreCalculator != null) {
|
|
|
|
|
+ score = starScoreCalculator.calculateScore(directCent / 100L, teamCent / 100L, referralCent / 100L);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (teamStarSnapshotMapper != null) {
|
|
|
|
|
+ // 上一期星级(无历史则入团基线一星)
|
|
|
|
|
+ TeamStarSnapshot prev = teamStarSnapshotMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamStarSnapshot>()
|
|
|
|
|
+ .eq(TeamStarSnapshot::getUserId, userId)
|
|
|
|
|
+ .orderByDesc(TeamStarSnapshot::getPeriod)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (prev != null && prev.getStarLevel() != null) {
|
|
|
|
|
+ currentStar = prev.getStarLevel();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (starScoreCalculator != null) {
|
|
|
|
|
+ newStar = starScoreCalculator.reEvaluateStar(score, directValidNewRefs, currentStar);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 落快照(幂等 upsert:uk_user_period)
|
|
|
|
|
+ TeamStarSnapshot snap = teamStarSnapshotMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<TeamStarSnapshot>()
|
|
|
|
|
+ .eq(TeamStarSnapshot::getUserId, userId)
|
|
|
|
|
+ .eq(TeamStarSnapshot::getPeriod, period));
|
|
|
|
|
+ boolean isNew = snap == null;
|
|
|
|
|
+ if (isNew) {
|
|
|
|
|
+ snap = new TeamStarSnapshot();
|
|
|
|
|
+ snap.setCreatedAt(new Date());
|
|
|
|
|
+ }
|
|
|
|
|
+ snap.setUserId(userId);
|
|
|
|
|
+ snap.setStarLevel(newStar);
|
|
|
|
|
+ snap.setScore(score);
|
|
|
|
|
+ snap.setDirectRevCent(directCent);
|
|
|
|
|
+ snap.setTeamRevCent(teamCent);
|
|
|
|
|
+ snap.setReferralConsumeCent(referralCent);
|
|
|
|
|
+ snap.setPeriod(period);
|
|
|
|
|
+ if (isNew) {
|
|
|
|
|
+ teamStarSnapshotMapper.insert(snap);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ teamStarSnapshotMapper.updateById(snap);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (starScoreCalculator != null) {
|
|
|
|
|
+ newStar = starScoreCalculator.reEvaluateStar(score, directValidNewRefs, StarScoreCalculator.MIN_STAR);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("月度星级重评(userId={}, period={}): score={}, 直推={}, 星级 {}→{}",
|
|
|
|
|
+ userId, period, score, directValidNewRefs, currentStar, newStar);
|
|
|
|
|
+ return newStar;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|