111
This commit is contained in:
@@ -36,7 +36,7 @@ public class BonusController {
|
||||
|
||||
/**
|
||||
* 领取奖励
|
||||
* body: { type: "new_user"|"referral", referredUserId?, milestone? }
|
||||
* body: { type: "new_user"|"referral"|"indirect_referral", referredUserId?, milestone?, directReferralId? }
|
||||
*/
|
||||
@PostMapping("/claim")
|
||||
public Result<Map<String, Object>> claim(@RequestBody Map<String, Object> params) {
|
||||
@@ -62,6 +62,18 @@ public class BonusController {
|
||||
Long referredUserId = Long.valueOf(referredUserIdObj.toString());
|
||||
int milestone = Integer.parseInt(milestoneObj.toString());
|
||||
return Result.success("领取成功", bonusService.claimReferralBonus(userId, referredUserId, milestone));
|
||||
} else if ("indirect_referral".equals(type)) {
|
||||
Object directReferralIdObj = params.get("directReferralId");
|
||||
Object indirectReferredUserIdObj = params.get("indirectReferredUserId");
|
||||
Object milestoneObj = params.get("milestone");
|
||||
if (directReferralIdObj == null || indirectReferredUserIdObj == null || milestoneObj == null) {
|
||||
return Result.fail("缺少参数");
|
||||
}
|
||||
Long directReferralId = Long.valueOf(directReferralIdObj.toString());
|
||||
Long indirectReferredUserId = Long.valueOf(indirectReferredUserIdObj.toString());
|
||||
int milestone = Integer.parseInt(milestoneObj.toString());
|
||||
return Result.success("领取成功", bonusService.claimIndirectReferralBonus(
|
||||
userId, directReferralId, indirectReferredUserId, milestone));
|
||||
} else {
|
||||
return Result.fail("无效的奖励类型");
|
||||
}
|
||||
|
||||
@@ -22,17 +22,20 @@ import java.util.*;
|
||||
*
|
||||
* 规则:
|
||||
* 1. 首充福利:新用户首次充值完成后可领取100u(一次性)
|
||||
* 2. 推广奖励:被推广人累计充值每满1000u,推广人领100u(多次触发,最高8次/人)
|
||||
* 2. 直接推广奖励:被推广人累计充值每满1000u,推广人领100u(最高8次/人)
|
||||
* 3. 间接推广奖励:被推广人推广的用户充值每满1000u,推广人也领50u(最高8次/人间接上限)
|
||||
*/
|
||||
@Service
|
||||
public class BonusService {
|
||||
|
||||
private static final BigDecimal BONUS_AMOUNT = new BigDecimal("100");
|
||||
private static final BigDecimal INDIRECT_BONUS_AMOUNT = new BigDecimal("50");
|
||||
private static final BigDecimal MILESTONE_UNIT = new BigDecimal("1000");
|
||||
private static final int MAX_MILESTONES = 8;
|
||||
|
||||
private static final String NEW_USER_REMARK = "新人首充福利";
|
||||
private static final String REFERRAL_REMARK_PREFIX = "邀请奖励-";
|
||||
private static final String INDIRECT_REFERRAL_REMARK_PREFIX = "间接推广奖励-";
|
||||
|
||||
@Autowired
|
||||
private AccountFundMapper accountFundMapper;
|
||||
@@ -114,15 +117,60 @@ public class BonusService {
|
||||
.count();
|
||||
referralInfo.put("claimableCount", claimable);
|
||||
|
||||
// === 间接推广奖励 ===
|
||||
// 查询 B 推广的所有人(C, D, E...)
|
||||
LambdaQueryWrapper<User> indirectWrapper = new LambdaQueryWrapper<>();
|
||||
indirectWrapper.eq(User::getReferredBy, referred.getId());
|
||||
List<User> indirectReferred = userMapper.selectList(indirectWrapper);
|
||||
referralInfo.put("indirectRefCount", indirectReferred.size());
|
||||
|
||||
// 计算间接奖励里程碑
|
||||
int indirectClaimable = 0;
|
||||
List<Map<String, Object>> indirectMilestones = new ArrayList<>();
|
||||
for (User ir : indirectReferred) {
|
||||
AccountFund irFund = assetService.getOrCreateFundAccount(ir.getId());
|
||||
BigDecimal irTotalDeposit = irFund.getTotalDeposit() != null
|
||||
? irFund.getTotalDeposit() : BigDecimal.ZERO;
|
||||
int irEarned = irTotalDeposit.divide(MILESTONE_UNIT, 0, RoundingMode.DOWN).intValue();
|
||||
irEarned = Math.min(irEarned, MAX_MILESTONES);
|
||||
|
||||
Set<Integer> irClaimed = getIndirectClaimedMilestones(userId, referred.getId(), ir.getId());
|
||||
|
||||
for (int i = 1; i <= MAX_MILESTONES; i++) {
|
||||
boolean earned = i <= irEarned;
|
||||
boolean iClaimed = irClaimed.contains(i);
|
||||
boolean isClaimable = earned && !iClaimed;
|
||||
if (isClaimable) indirectClaimable++;
|
||||
|
||||
Map<String, Object> im = new HashMap<>();
|
||||
im.put("indirectReferredUserId", ir.getId());
|
||||
im.put("indirectUsername", ir.getUsername());
|
||||
im.put("milestone", i);
|
||||
im.put("amount", INDIRECT_BONUS_AMOUNT);
|
||||
im.put("threshold", MILESTONE_UNIT.multiply(new BigDecimal(i)));
|
||||
im.put("earned", earned);
|
||||
im.put("claimed", iClaimed);
|
||||
im.put("claimable", isClaimable);
|
||||
indirectMilestones.add(im);
|
||||
}
|
||||
}
|
||||
referralInfo.put("indirectClaimableCount", indirectClaimable);
|
||||
referralInfo.put("indirectMilestones", indirectMilestones);
|
||||
|
||||
referralRewards.add(referralInfo);
|
||||
}
|
||||
result.put("referralRewards", referralRewards);
|
||||
|
||||
// 总可领取数量
|
||||
// 总可领取数量(直接 + 间接)
|
||||
int totalClaimable = (Boolean.TRUE.equals(newUserBonus.get("eligible")) ? 1 : 0);
|
||||
totalClaimable += referralRewards.stream()
|
||||
.mapToInt(r -> r.get("claimableCount") instanceof Number
|
||||
? ((Number) r.get("claimableCount")).intValue() : 0)
|
||||
.mapToInt(r -> {
|
||||
int direct = r.get("claimableCount") instanceof Number
|
||||
? ((Number) r.get("claimableCount")).intValue() : 0;
|
||||
int indirect = r.get("indirectClaimableCount") instanceof Number
|
||||
? ((Number) r.get("indirectClaimableCount")).intValue() : 0;
|
||||
return direct + indirect;
|
||||
})
|
||||
.sum();
|
||||
result.put("totalClaimable", totalClaimable);
|
||||
|
||||
@@ -205,6 +253,66 @@ public class BonusService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 领取间接推广奖励
|
||||
* A 推广 B,B 推广 C → A 领取 C 充值里程碑的间接奖励(50 USDT)
|
||||
*
|
||||
* @param referrerId A(领取人)
|
||||
* @param directReferralId B(A 的直接推广人)
|
||||
* @param indirectReferredUserId C(B 的推广人)
|
||||
* @param milestone 里程碑序号
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Map<String, Object> claimIndirectReferralBonus(Long referrerId, Long directReferralId,
|
||||
Long indirectReferredUserId, int milestone) {
|
||||
if (milestone < 1 || milestone > MAX_MILESTONES) {
|
||||
throw new RuntimeException("无效的里程碑");
|
||||
}
|
||||
|
||||
// 验证推广链:A→B→C
|
||||
User directReferral = userMapper.selectById(directReferralId);
|
||||
if (directReferral == null || !referrerId.equals(directReferral.getReferredBy())) {
|
||||
throw new RuntimeException("直接推广关系不存在");
|
||||
}
|
||||
|
||||
User indirectReferred = userMapper.selectById(indirectReferredUserId);
|
||||
if (indirectReferred == null || !directReferralId.equals(indirectReferred.getReferredBy())) {
|
||||
throw new RuntimeException("间接推广关系不存在");
|
||||
}
|
||||
|
||||
// 检查里程碑是否已达到
|
||||
AccountFund irFund = assetService.getOrCreateFundAccount(indirectReferredUserId);
|
||||
BigDecimal irTotalDeposit = irFund.getTotalDeposit() != null
|
||||
? irFund.getTotalDeposit() : BigDecimal.ZERO;
|
||||
int earnedMilestones = irTotalDeposit.divide(MILESTONE_UNIT, 0, RoundingMode.DOWN).intValue();
|
||||
if (milestone > earnedMilestones) {
|
||||
throw new RuntimeException("该里程碑尚未达到");
|
||||
}
|
||||
|
||||
// 检查是否已领取
|
||||
String remark = INDIRECT_REFERRAL_REMARK_PREFIX + directReferralId + "-" + indirectReferredUserId + "-" + milestone;
|
||||
if (hasClaimedByRemark(referrerId, remark)) {
|
||||
throw new RuntimeException("该间接奖励已领取");
|
||||
}
|
||||
|
||||
// 发放奖励
|
||||
AccountFund referrerFund = assetService.getOrCreateFundAccount(referrerId);
|
||||
BigDecimal balanceBefore = referrerFund.getBalance();
|
||||
BigDecimal balanceAfter = balanceBefore.add(INDIRECT_BONUS_AMOUNT);
|
||||
|
||||
updateFundBalance(referrerFund, balanceAfter);
|
||||
assetService.createFlow(referrerId, 1, INDIRECT_BONUS_AMOUNT, balanceBefore, balanceAfter,
|
||||
"USDT", null, remark);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("amount", INDIRECT_BONUS_AMOUNT);
|
||||
result.put("balance", balanceAfter);
|
||||
result.put("directReferralId", directReferralId);
|
||||
result.put("indirectReferredUserId", indirectReferredUserId);
|
||||
result.put("milestone", milestone);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新资金账户余额
|
||||
*/
|
||||
@@ -250,4 +358,28 @@ public class BonusService {
|
||||
}
|
||||
return milestones;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已领取的间接推广里程碑集合
|
||||
*/
|
||||
private Set<Integer> getIndirectClaimedMilestones(Long referrerId, Long directReferralId, Long indirectReferredUserId) {
|
||||
String prefix = INDIRECT_REFERRAL_REMARK_PREFIX + directReferralId + "-" + indirectReferredUserId + "-";
|
||||
LambdaQueryWrapper<AccountFlow> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AccountFlow::getUserId, referrerId)
|
||||
.likeRight(AccountFlow::getRemark, prefix);
|
||||
List<AccountFlow> flows = accountFlowMapper.selectList(wrapper);
|
||||
|
||||
Set<Integer> milestones = new HashSet<>();
|
||||
for (AccountFlow flow : flows) {
|
||||
String remark = flow.getRemark();
|
||||
if (remark != null && remark.startsWith(prefix)) {
|
||||
try {
|
||||
int m = Integer.parseInt(remark.substring(prefix.length()));
|
||||
milestones.add(m);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return milestones;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user