邀请码 列表和统计功能完善
This commit is contained in:
parent
88ef879b22
commit
815f42db27
@ -12,6 +12,8 @@ import com.red.circle.other.app.dto.cmd.activity.InviteUserActivityLimitCmd;
|
|||||||
import com.red.circle.other.app.service.activity.InviteUserActivityService;
|
import com.red.circle.other.app.service.activity.InviteUserActivityService;
|
||||||
import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd;
|
import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd;
|
||||||
import com.red.circle.other.app.dto.clientobject.activity.InviteCodeCO;
|
import com.red.circle.other.app.dto.clientobject.activity.InviteCodeCO;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.InviteIncomeStatisticsCO;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.ValidInviteUserCO;
|
||||||
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -173,4 +175,32 @@ public class InviteUserActivityRestController {
|
|||||||
inviteUserActivityService.bindInviteCode(cmd);
|
inviteUserActivityService.bindInviteCode(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请收益统计.
|
||||||
|
*
|
||||||
|
* @eo.name 邀请收益统计
|
||||||
|
* @eo.url /income/statistics
|
||||||
|
* @eo.method get
|
||||||
|
* @eo.request-type formdata
|
||||||
|
*/
|
||||||
|
@GetMapping("/income/statistics")
|
||||||
|
public InviteIncomeStatisticsCO getIncomeStatistics(AppExtCommand cmd) {
|
||||||
|
InviteIncomeStatisticsCO statistics = inviteUserActivityService.getIncomeStatistics(cmd);
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有效邀请用户列表.
|
||||||
|
*
|
||||||
|
* @eo.name 有效邀请用户列表
|
||||||
|
* @eo.url /valid/invite/users
|
||||||
|
* @eo.method get
|
||||||
|
* @eo.request-type formdata
|
||||||
|
*/
|
||||||
|
@GetMapping("/valid/invite/users")
|
||||||
|
public List<ValidInviteUserCO> getValidInviteUsers(AppExtCommand cmd) {
|
||||||
|
List<ValidInviteUserCO> users = inviteUserActivityService.getValidInviteUsers(cmd);
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
|||||||
import com.red.circle.other.domain.model.user.UserProfile;
|
import com.red.circle.other.domain.model.user.UserProfile;
|
||||||
import com.red.circle.other.infra.database.rds.entity.user.user.UserInviteUser;
|
import com.red.circle.other.infra.database.rds.entity.user.user.UserInviteUser;
|
||||||
import com.red.circle.other.infra.database.rds.service.user.user.UserInviteUserService;
|
import com.red.circle.other.infra.database.rds.service.user.user.UserInviteUserService;
|
||||||
|
import com.red.circle.other.infra.enums.user.InviteTypeEnum;
|
||||||
import com.red.circle.tool.core.date.TimestampUtils;
|
import com.red.circle.tool.core.date.TimestampUtils;
|
||||||
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -40,45 +41,81 @@ public class BindInviteCodeExe {
|
|||||||
public void execute(BindInviteCodeCmd cmd) {
|
public void execute(BindInviteCodeCmd cmd) {
|
||||||
Long userId = cmd.getReqUserId();
|
Long userId = cmd.getReqUserId();
|
||||||
String inviteCode = cmd.getInviteCode();
|
String inviteCode = cmd.getInviteCode();
|
||||||
|
String inviteType = cmd.getInviteType();
|
||||||
String sysOrigin = cmd.getReqSysOrigin().getOrigin();
|
String sysOrigin = cmd.getReqSysOrigin().getOrigin();
|
||||||
|
|
||||||
log.info("开始绑定邀请码, userId={}, inviteCode={}", userId, inviteCode);
|
log.info("开始绑定邀请码, userId={}, inviteCode={}, inviteType={}", userId, inviteCode, inviteType);
|
||||||
|
|
||||||
// 1. 校验用户是否存在
|
// 1. 校验inviteType是否合法
|
||||||
|
if (!InviteTypeEnum.LOTTERY.getCode().equals(inviteType) &&
|
||||||
|
!InviteTypeEnum.REGISTER.getCode().equals(inviteType)) {
|
||||||
|
throw new RuntimeException("Invalid invitation type, must be LOTTERY or REGISTER");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 校验用户是否存在
|
||||||
UserProfile userProfile = userProfileGateway.getByUserId(userId);
|
UserProfile userProfile = userProfileGateway.getByUserId(userId);
|
||||||
if (userProfile == null) {
|
if (userProfile == null) {
|
||||||
throw new RuntimeException("The user does not exist.");
|
throw new RuntimeException("The user does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 校验是否已绑定过邀请人
|
// 4. 校验是否已绑定过邀请人(userId=邀请人, inviteUserId=被邀请人)
|
||||||
List<UserInviteUser> existingInvite = userInviteUserService.query().eq(UserInviteUser::getUserId, userId).list();
|
// 当前用户作为被邀请人,查询 inviteUserId=当前用户
|
||||||
|
List<UserInviteUser> existingInvite = userInviteUserService.query()
|
||||||
|
.eq(UserInviteUser::getInviteUserId, userId) // 当前用户作为被邀请人
|
||||||
|
.list();
|
||||||
if (existingInvite != null && !existingInvite.isEmpty()) {
|
if (existingInvite != null && !existingInvite.isEmpty()) {
|
||||||
throw new RuntimeException("The inviter has been bound before and cannot be bound again");
|
throw new RuntimeException("The inviter has been bound before and cannot be bound again");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 根据邀请码查找邀请人
|
// 5. 根据邀请码查找邀请人
|
||||||
UserProfile inviterProfile = inviteCodeService.getUserProfileByInviteCode(inviteCode);
|
UserProfile inviterProfile = inviteCodeService.getUserProfileByInviteCode(inviteCode);
|
||||||
if (inviterProfile == null) {
|
if (inviterProfile == null) {
|
||||||
throw new RuntimeException("The invitation code is invalid and the corresponding user was not found");
|
throw new RuntimeException("The invitation code is invalid and the corresponding user was not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 校验不能邀请自己
|
// 6. 校验不能邀请自己
|
||||||
if (Objects.equals(userId, inviterProfile.getId())) {
|
if (Objects.equals(userId, inviterProfile.getId())) {
|
||||||
throw new RuntimeException("You cannot bind your own invitation code");
|
throw new RuntimeException("You cannot bind your own invitation code");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 保存邀请关系
|
// 再次校验:如果当前用户已经绑定了其他邀请人,则不能再绑定
|
||||||
|
// 这里是双重校验,确保数据一致性
|
||||||
|
List<UserInviteUser> doubleCheck = userInviteUserService.query()
|
||||||
|
.eq(UserInviteUser::getInviteUserId, userId)
|
||||||
|
.list();
|
||||||
|
if (doubleCheck != null && !doubleCheck.isEmpty()) {
|
||||||
|
Long existingInviterId = doubleCheck.get(0).getUserId();
|
||||||
|
log.warn("用户已绑定邀请人,不能重复绑定, userId={}, existingInviterId={}, newInviterId={}",
|
||||||
|
userId, existingInviterId, inviterProfile.getId());
|
||||||
|
throw new RuntimeException("You have already bound an inviter and cannot bind again");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验双向绑定:如果邀请人已经被当前用户邀请过,则不能再绑定
|
||||||
|
// 即:如果 inviterProfile 已经是当前用户的被邀请人,则不能让 inviterProfile 成为当前用户的邀请人
|
||||||
|
// 查询:userId=当前用户, inviteUserId=邀请人(检查当前用户是否已经邀请过这个邀请人)
|
||||||
|
List<UserInviteUser> reverseInvite = userInviteUserService.query()
|
||||||
|
.eq(UserInviteUser::getUserId, userId) // 当前用户作为邀请人
|
||||||
|
.eq(UserInviteUser::getInviteUserId, inviterProfile.getId()) // 邀请人作为被邀请人
|
||||||
|
.list();
|
||||||
|
if (reverseInvite != null && !reverseInvite.isEmpty()) {
|
||||||
|
log.warn("不能双向绑定,该用户已被当前用户邀请过, userId={}, inviterUserId={}",
|
||||||
|
userId, inviterProfile.getId());
|
||||||
|
throw new RuntimeException("Cannot bind bidirectionally, this user has already been invited by you");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 保存邀请关系(userId=邀请人, inviteUserId=被邀请人)
|
||||||
UserInviteUser inviteUser = new UserInviteUser();
|
UserInviteUser inviteUser = new UserInviteUser();
|
||||||
inviteUser.setId(IdWorkerUtils.getId());
|
inviteUser.setId(IdWorkerUtils.getId());
|
||||||
inviteUser.setSysOrigin(sysOrigin);
|
inviteUser.setSysOrigin(sysOrigin);
|
||||||
inviteUser.setUserId(userId);
|
inviteUser.setUserId(inviterProfile.getId()); // 邀请人
|
||||||
inviteUser.setInviteUserId(inviterProfile.getId());
|
inviteUser.setInviteUserId(userId); // 被邀请人(当前用户)
|
||||||
|
inviteUser.setInviteType(inviteType); // 使用传入的inviteType
|
||||||
inviteUser.setCreateTime(TimestampUtils.now());
|
inviteUser.setCreateTime(TimestampUtils.now());
|
||||||
inviteUser.setUpdateTime(TimestampUtils.now());
|
inviteUser.setUpdateTime(TimestampUtils.now());
|
||||||
|
|
||||||
userInviteUserService.save(inviteUser);
|
userInviteUserService.save(inviteUser);
|
||||||
|
|
||||||
log.info("绑定邀请码成功, userId={}, inviterUserId={}, inviteCode={}",
|
log.info("绑定邀请码成功, userId={}, inviterUserId={}, inviteCode={}, inviteType={}",
|
||||||
userId, inviterProfile.getId(), inviteCode);
|
userId, inviterProfile.getId(), inviteCode, inviteType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,163 @@
|
|||||||
|
package com.red.circle.other.app.command.activity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.InviteIncomeStatisticsCO;
|
||||||
|
import com.red.circle.other.infra.database.rds.dao.user.user.InviteUserRechargeCommissionDAO;
|
||||||
|
import com.red.circle.other.infra.database.rds.dao.user.user.UserInviteUserDAO;
|
||||||
|
import com.red.circle.other.infra.database.rds.entity.user.user.InviteUserRechargeCommission;
|
||||||
|
import com.red.circle.other.infra.database.rds.entity.user.user.UserInviteUser;
|
||||||
|
import com.red.circle.other.infra.enums.user.InviteTypeEnum;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取邀请收益统计执行器
|
||||||
|
*
|
||||||
|
* 表结构说明:
|
||||||
|
* user_invite_user: userId(邀请人), inviteUserId(被邀请人)
|
||||||
|
* user_invite_user_recharge_commission: userId(被邀请人-充值人), inviteUserId(邀请人-获得佣金人)
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-22
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GetIncomeStatisticsExe {
|
||||||
|
|
||||||
|
private final UserInviteUserDAO userInviteUserDAO;
|
||||||
|
private final InviteUserRechargeCommissionDAO inviteUserRechargeCommissionDAO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行获取邀请收益统计
|
||||||
|
*
|
||||||
|
* @param cmd 命令
|
||||||
|
* @return 收益统计
|
||||||
|
*/
|
||||||
|
public InviteIncomeStatisticsCO execute(AppExtCommand cmd) {
|
||||||
|
Long userId = cmd.getReqUserId();
|
||||||
|
|
||||||
|
// 查询当前用户邀请的所有用户(userId=邀请人)
|
||||||
|
List<UserInviteUser> allInvites = userInviteUserDAO.selectList(
|
||||||
|
new LambdaQueryWrapper<UserInviteUser>()
|
||||||
|
.eq(UserInviteUser::getUserId, userId) // userId是邀请人
|
||||||
|
);
|
||||||
|
|
||||||
|
// 筛选有效邀请
|
||||||
|
List<Long> validInvitedUserIds = getValidInvitedUserIds(allInvites);
|
||||||
|
|
||||||
|
// 计算总收益
|
||||||
|
BigDecimal totalIncome = calculateTotalIncome(userId, validInvitedUserIds);
|
||||||
|
|
||||||
|
// 计算今日收益
|
||||||
|
BigDecimal todayIncome = calculateTodayIncome(userId, validInvitedUserIds);
|
||||||
|
|
||||||
|
InviteIncomeStatisticsCO statistics = new InviteIncomeStatisticsCO();
|
||||||
|
statistics.setTotalIncome(totalIncome);
|
||||||
|
statistics.setTodayIncome(todayIncome);
|
||||||
|
statistics.setValidInviteCount(validInvitedUserIds.size());
|
||||||
|
|
||||||
|
log.info("邀请收益统计, userId={}, totalIncome={}, todayIncome={}, validCount={}",
|
||||||
|
userId, totalIncome, todayIncome, validInvitedUserIds.size());
|
||||||
|
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有效邀请用户ID列表
|
||||||
|
*/
|
||||||
|
private List<Long> getValidInvitedUserIds(List<UserInviteUser> allInvites) {
|
||||||
|
return allInvites.stream()
|
||||||
|
.filter(this::isValidInvite)
|
||||||
|
.map(UserInviteUser::getInviteUserId) // inviteUserId是被邀请人
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为有效邀请
|
||||||
|
*/
|
||||||
|
private boolean isValidInvite(UserInviteUser invite) {
|
||||||
|
String inviteType = invite.getInviteType();
|
||||||
|
|
||||||
|
// LOTTERY类型直接算有效
|
||||||
|
if (InviteTypeEnum.LOTTERY.getCode().equals(inviteType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REGISTER类型需要检查充值记录
|
||||||
|
if (InviteTypeEnum.REGISTER.getCode().equals(inviteType)) {
|
||||||
|
// userId=邀请人, inviteUserId=被邀请人
|
||||||
|
return hasValidRecharge(invite.getInviteUserId(), invite.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有有效充值记录(commission > 0)
|
||||||
|
* @param invitedUserId 被邀请人 ID(充值的人)
|
||||||
|
* @param inviterUserId 邀请人 ID(获得佣金的人)
|
||||||
|
*/
|
||||||
|
private boolean hasValidRecharge(Long invitedUserId, Long inviterUserId) {
|
||||||
|
Long count = inviteUserRechargeCommissionDAO.selectCount(
|
||||||
|
new LambdaQueryWrapper<InviteUserRechargeCommission>()
|
||||||
|
.eq(InviteUserRechargeCommission::getUserId, invitedUserId) // 被邀请人
|
||||||
|
.eq(InviteUserRechargeCommission::getInviteUserId, inviterUserId) // 邀请人
|
||||||
|
.gt(InviteUserRechargeCommission::getCommission, BigDecimal.ZERO)
|
||||||
|
);
|
||||||
|
return count != null && count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算总收益
|
||||||
|
*/
|
||||||
|
private BigDecimal calculateTotalIncome(Long inviterUserId, List<Long> validInvitedUserIds) {
|
||||||
|
if (validInvitedUserIds.isEmpty()) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<InviteUserRechargeCommission> commissions = inviteUserRechargeCommissionDAO.selectList(
|
||||||
|
new LambdaQueryWrapper<InviteUserRechargeCommission>()
|
||||||
|
.eq(InviteUserRechargeCommission::getInviteUserId, inviterUserId)
|
||||||
|
.in(InviteUserRechargeCommission::getUserId, validInvitedUserIds)
|
||||||
|
);
|
||||||
|
|
||||||
|
return commissions.stream()
|
||||||
|
.map(InviteUserRechargeCommission::getCommission)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算今日收益
|
||||||
|
*/
|
||||||
|
private BigDecimal calculateTodayIncome(Long inviterUserId, List<Long> validInvitedUserIds) {
|
||||||
|
if (validInvitedUserIds.isEmpty()) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
|
||||||
|
LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
|
||||||
|
|
||||||
|
List<InviteUserRechargeCommission> todayCommissions = inviteUserRechargeCommissionDAO.selectList(
|
||||||
|
new LambdaQueryWrapper<InviteUserRechargeCommission>()
|
||||||
|
.eq(InviteUserRechargeCommission::getInviteUserId, inviterUserId)
|
||||||
|
.in(InviteUserRechargeCommission::getUserId, validInvitedUserIds)
|
||||||
|
.ge(InviteUserRechargeCommission::getCreateTime, Timestamp.valueOf(todayStart))
|
||||||
|
.le(InviteUserRechargeCommission::getCreateTime, Timestamp.valueOf(todayEnd))
|
||||||
|
);
|
||||||
|
|
||||||
|
return todayCommissions.stream()
|
||||||
|
.map(InviteUserRechargeCommission::getCommission)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,179 @@
|
|||||||
|
package com.red.circle.other.app.command.activity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.ValidInviteUserCO;
|
||||||
|
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||||
|
import com.red.circle.other.domain.model.user.UserProfile;
|
||||||
|
import com.red.circle.other.infra.database.rds.dao.user.user.InviteUserRechargeCommissionDAO;
|
||||||
|
import com.red.circle.other.infra.database.rds.dao.user.user.UserInviteUserDAO;
|
||||||
|
import com.red.circle.other.infra.database.rds.entity.user.user.InviteUserRechargeCommission;
|
||||||
|
import com.red.circle.other.infra.database.rds.entity.user.user.UserInviteUser;
|
||||||
|
import com.red.circle.other.infra.enums.user.InviteTypeEnum;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有效邀请用户列表执行器
|
||||||
|
*
|
||||||
|
* 表结构说明:
|
||||||
|
* user_invite_user: userId(邀请人), inviteUserId(被邀请人)
|
||||||
|
* user_invite_user_recharge_commission: userId(被邀请人-充值人), inviteUserId(邀请人-获得佣金人)
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-22
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GetValidInviteUsersExe {
|
||||||
|
|
||||||
|
private final UserInviteUserDAO userInviteUserDAO;
|
||||||
|
private final InviteUserRechargeCommissionDAO inviteUserRechargeCommissionDAO;
|
||||||
|
private final UserProfileGateway userProfileGateway;
|
||||||
|
|
||||||
|
// 邀请关系有效期(天数)
|
||||||
|
private static final int VALID_DAYS = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行获取有效邀请用户列表
|
||||||
|
*
|
||||||
|
* @param cmd 命令
|
||||||
|
* @return 有效邀请用户列表
|
||||||
|
*/
|
||||||
|
public List<ValidInviteUserCO> execute(AppExtCommand cmd) {
|
||||||
|
Long inviterUserId = cmd.getReqUserId(); // 邀请人ID
|
||||||
|
|
||||||
|
// 查询当前用户邀请的所有用户(userId=邀请人)
|
||||||
|
List<UserInviteUser> allInvites = userInviteUserDAO.selectList(
|
||||||
|
new LambdaQueryWrapper<UserInviteUser>()
|
||||||
|
.eq(UserInviteUser::getUserId, inviterUserId) // userId是邀请人
|
||||||
|
.orderByDesc(UserInviteUser::getCreateTime)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (allInvites.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选有效邀请
|
||||||
|
List<UserInviteUser> validInvites = allInvites.stream()
|
||||||
|
.filter(this::isValidInvite)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (validInvites.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取被邀请用户ID列表(inviteUserId)
|
||||||
|
List<Long> invitedUserIds = validInvites.stream()
|
||||||
|
.map(UserInviteUser::getInviteUserId) // inviteUserId是被邀请人
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 批量查询用户信息
|
||||||
|
Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(new HashSet<>(invitedUserIds));
|
||||||
|
|
||||||
|
// 批量查询佣金信息(inviteUserId=邀请人,userId=被邀请人)
|
||||||
|
List<InviteUserRechargeCommission> commissions = inviteUserRechargeCommissionDAO.selectList(
|
||||||
|
new LambdaQueryWrapper<InviteUserRechargeCommission>()
|
||||||
|
.eq(InviteUserRechargeCommission::getInviteUserId, inviterUserId) // 邀请人
|
||||||
|
.in(InviteUserRechargeCommission::getUserId, invitedUserIds) // 被邀请人
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<Long, BigDecimal> commissionMap = commissions.stream()
|
||||||
|
.collect(Collectors.groupingBy(
|
||||||
|
InviteUserRechargeCommission::getUserId, // 被邀请人ID
|
||||||
|
Collectors.reducing(BigDecimal.ZERO,
|
||||||
|
InviteUserRechargeCommission::getCommission,
|
||||||
|
BigDecimal::add)
|
||||||
|
));
|
||||||
|
|
||||||
|
// 组装返回数据
|
||||||
|
List<ValidInviteUserCO> result = new ArrayList<>();
|
||||||
|
for (UserInviteUser invite : validInvites) {
|
||||||
|
ValidInviteUserCO userCO = buildValidInviteUserCO(invite, userProfileMap, commissionMap);
|
||||||
|
result.add(userCO);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("有效邀请用户列表, inviterUserId={}, validCount={}", inviterUserId, result.size());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为有效邀请
|
||||||
|
*/
|
||||||
|
private boolean isValidInvite(UserInviteUser invite) {
|
||||||
|
String inviteType = invite.getInviteType();
|
||||||
|
|
||||||
|
// LOTTERY类型直接算有效
|
||||||
|
if (InviteTypeEnum.LOTTERY.getCode().equals(inviteType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REGISTER类型需要检查充值记录
|
||||||
|
if (InviteTypeEnum.REGISTER.getCode().equals(inviteType)) {
|
||||||
|
// userId=邀请人, inviteUserId=被邀请人
|
||||||
|
return hasValidRecharge(invite.getInviteUserId(), invite.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有有效充值记录(commission > 0)
|
||||||
|
* @param invitedUserId 被邀请人ID(充值的人)
|
||||||
|
* @param inviterUserId 邀请人ID(获得佣金的人)
|
||||||
|
*/
|
||||||
|
private boolean hasValidRecharge(Long invitedUserId, Long inviterUserId) {
|
||||||
|
long count = inviteUserRechargeCommissionDAO.selectCount(
|
||||||
|
new LambdaQueryWrapper<InviteUserRechargeCommission>()
|
||||||
|
.eq(InviteUserRechargeCommission::getUserId, invitedUserId) // 被邀请人
|
||||||
|
.eq(InviteUserRechargeCommission::getInviteUserId, inviterUserId) // 邀请人
|
||||||
|
.gt(InviteUserRechargeCommission::getCommission, BigDecimal.ZERO)
|
||||||
|
);
|
||||||
|
return count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建有效邀请用户CO
|
||||||
|
*/
|
||||||
|
private ValidInviteUserCO buildValidInviteUserCO(
|
||||||
|
UserInviteUser invite,
|
||||||
|
Map<Long, UserProfile> userProfileMap,
|
||||||
|
Map<Long, BigDecimal> commissionMap) {
|
||||||
|
|
||||||
|
Long invitedUserId = invite.getInviteUserId(); // 被邀请人ID
|
||||||
|
|
||||||
|
ValidInviteUserCO userCO = new ValidInviteUserCO();
|
||||||
|
userCO.setInvitedUserId(invitedUserId);
|
||||||
|
userCO.setInviteType(invite.getInviteType());
|
||||||
|
userCO.setInviteTime(invite.getCreateTime());
|
||||||
|
|
||||||
|
// 设置用户账号
|
||||||
|
UserProfile userProfile = userProfileMap.get(invitedUserId);
|
||||||
|
if (userProfile != null) {
|
||||||
|
userCO.setInvitedUserAccount(userProfile.getAccount());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算剩余天数
|
||||||
|
LocalDateTime inviteTime = invite.getCreateTime().toLocalDateTime();
|
||||||
|
LocalDateTime expireTime = inviteTime.plusDays(VALID_DAYS);
|
||||||
|
long remainingDays = ChronoUnit.DAYS.between(LocalDateTime.now(), expireTime);
|
||||||
|
userCO.setRemainingDays(Math.max(0, (int) remainingDays));
|
||||||
|
|
||||||
|
// 设置提供的收益
|
||||||
|
BigDecimal income = commissionMap.getOrDefault(invitedUserId, BigDecimal.ZERO);
|
||||||
|
userCO.setProvideIncome(income);
|
||||||
|
|
||||||
|
return userCO;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,8 +18,12 @@ import com.red.circle.other.app.dto.clientobject.activity.InviteUserCO;
|
|||||||
import com.red.circle.other.app.dto.clientobject.activity.InviteUserCommissionDetailsCO;
|
import com.red.circle.other.app.dto.clientobject.activity.InviteUserCommissionDetailsCO;
|
||||||
import com.red.circle.other.app.command.activity.GetMyInviteCodeExe;
|
import com.red.circle.other.app.command.activity.GetMyInviteCodeExe;
|
||||||
import com.red.circle.other.app.command.activity.BindInviteCodeExe;
|
import com.red.circle.other.app.command.activity.BindInviteCodeExe;
|
||||||
|
import com.red.circle.other.app.command.activity.GetIncomeStatisticsExe;
|
||||||
|
import com.red.circle.other.app.command.activity.GetValidInviteUsersExe;
|
||||||
import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd;
|
import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd;
|
||||||
import com.red.circle.other.app.dto.clientobject.activity.InviteCodeCO;
|
import com.red.circle.other.app.dto.clientobject.activity.InviteCodeCO;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.InviteIncomeStatisticsCO;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.ValidInviteUserCO;
|
||||||
import com.red.circle.other.app.dto.cmd.activity.InviteUserActivityLimitCmd;
|
import com.red.circle.other.app.dto.cmd.activity.InviteUserActivityLimitCmd;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -47,6 +51,8 @@ public class InviteUserActivityServiceImpl implements InviteUserActivityService
|
|||||||
private final InviteActivityExtractedListCmdExe inviteActivityExtractedListCmdExe;
|
private final InviteActivityExtractedListCmdExe inviteActivityExtractedListCmdExe;
|
||||||
private final GetMyInviteCodeExe getMyInviteCodeExe;
|
private final GetMyInviteCodeExe getMyInviteCodeExe;
|
||||||
private final BindInviteCodeExe bindInviteCodeExe;
|
private final BindInviteCodeExe bindInviteCodeExe;
|
||||||
|
private final GetIncomeStatisticsExe getIncomeStatisticsExe;
|
||||||
|
private final GetValidInviteUsersExe getValidInviteUsersExe;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InviteUserActivitySummaryCO getSummary(AppExtCommand cmd) {
|
public InviteUserActivitySummaryCO getSummary(AppExtCommand cmd) {
|
||||||
@ -98,4 +104,14 @@ public class InviteUserActivityServiceImpl implements InviteUserActivityService
|
|||||||
public void bindInviteCode(BindInviteCodeCmd cmd) {
|
public void bindInviteCode(BindInviteCodeCmd cmd) {
|
||||||
bindInviteCodeExe.execute(cmd);
|
bindInviteCodeExe.execute(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InviteIncomeStatisticsCO getIncomeStatistics(AppExtCommand cmd) {
|
||||||
|
return getIncomeStatisticsExe.execute(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ValidInviteUserCO> getValidInviteUsers(AppExtCommand cmd) {
|
||||||
|
return getValidInviteUsersExe.execute(cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.red.circle.other.app.dto.clientobject.activity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请收益统计CO
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class InviteIncomeStatisticsCO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总收益
|
||||||
|
*/
|
||||||
|
private BigDecimal totalIncome;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日收益
|
||||||
|
*/
|
||||||
|
private BigDecimal todayIncome;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有效邀请用户数量
|
||||||
|
*/
|
||||||
|
private Integer validInviteCount;
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.red.circle.other.app.dto.clientobject.activity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有效邀请用户CO
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ValidInviteUserCO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被邀请用户ID
|
||||||
|
*/
|
||||||
|
private Long invitedUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被邀请用户账号
|
||||||
|
*/
|
||||||
|
private String invitedUserAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请类型:LOTTERY-抽奖, REGISTER-注册
|
||||||
|
*/
|
||||||
|
private String inviteType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 剩余天数
|
||||||
|
*/
|
||||||
|
private Integer remainingDays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供的收益
|
||||||
|
*/
|
||||||
|
private BigDecimal provideIncome;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请时间
|
||||||
|
*/
|
||||||
|
private Timestamp inviteTime;
|
||||||
|
}
|
||||||
@ -18,4 +18,10 @@ public class BindInviteCodeCmd extends AppExtCommand {
|
|||||||
|
|
||||||
@NotBlank(message = "邀请码不能为空")
|
@NotBlank(message = "邀请码不能为空")
|
||||||
private String inviteCode;
|
private String inviteCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请类型:LOTTERY-抽奖类型, REGISTER-注册类型
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "邀请类型不能为空")
|
||||||
|
private String inviteType;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import com.red.circle.other.app.dto.clientobject.activity.InviteUserCommissionDe
|
|||||||
import com.red.circle.other.app.dto.cmd.activity.InviteUserActivityLimitCmd;
|
import com.red.circle.other.app.dto.cmd.activity.InviteUserActivityLimitCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd;
|
import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd;
|
||||||
import com.red.circle.other.app.dto.clientobject.activity.InviteCodeCO;
|
import com.red.circle.other.app.dto.clientobject.activity.InviteCodeCO;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.InviteIncomeStatisticsCO;
|
||||||
|
import com.red.circle.other.app.dto.clientobject.activity.ValidInviteUserCO;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,4 +72,14 @@ public interface InviteUserActivityService {
|
|||||||
* 绑定邀请码.
|
* 绑定邀请码.
|
||||||
*/
|
*/
|
||||||
void bindInviteCode(BindInviteCodeCmd cmd);
|
void bindInviteCode(BindInviteCodeCmd cmd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请收益统计.
|
||||||
|
*/
|
||||||
|
InviteIncomeStatisticsCO getIncomeStatistics(AppExtCommand cmd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有效邀请用户列表.
|
||||||
|
*/
|
||||||
|
List<ValidInviteUserCO> getValidInviteUsers(AppExtCommand cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,4 +46,10 @@ public class UserInviteUser extends TimestampBaseEntity {
|
|||||||
*/
|
*/
|
||||||
@TableField("invite_user_id")
|
@TableField("invite_user_id")
|
||||||
private Long inviteUserId;
|
private Long inviteUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请类型:LOTTERY-抽奖类型, REGISTER-注册类型
|
||||||
|
*/
|
||||||
|
@TableField("invite_type")
|
||||||
|
private String inviteType;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.red.circle.other.infra.enums.user;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请类型枚举
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-22
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum InviteTypeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽奖类型 - 直接算有效邀请
|
||||||
|
*/
|
||||||
|
LOTTERY("LOTTERY", "抽奖类型"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册类型 - 需要充值才算有效邀请
|
||||||
|
*/
|
||||||
|
REGISTER("REGISTER", "注册类型");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
public static InviteTypeEnum fromCode(String code) {
|
||||||
|
for (InviteTypeEnum type : values()) {
|
||||||
|
if (type.code.equals(code)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user