用户充值接口完善

This commit is contained in:
tianfeng 2025-12-30 11:23:10 +08:00
parent 70a8340e22
commit 2b8b26906a
17 changed files with 700 additions and 3 deletions

View File

@ -13,6 +13,8 @@ import com.red.circle.other.app.dto.clientobject.activity.LotteryTicketCO;
import com.red.circle.other.app.dto.clientobject.activity.LotteryWinnerHistoryCO;
import com.red.circle.other.app.dto.clientobject.activity.LotteryWithdrawAmountCO;
import com.red.circle.other.app.dto.clientobject.activity.LotteryWithdrawCO;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRechargeRankCO;
import com.red.circle.other.app.dto.clientobject.activity.UserActivityRechargeCO;
import com.red.circle.other.app.dto.cmd.activity.LotteryDrawCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryMultiDrawCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryRankQryCmd;
@ -22,7 +24,10 @@ import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawQryCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryExchangeGoldCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryTransferCmd;
import com.red.circle.other.app.dto.cmd.activity.ActivityRechargeRankCmd;
import com.red.circle.other.app.dto.cmd.activity.UserActivityRechargeCmd;
import com.red.circle.other.app.service.activity.LotteryActivityRestService;
import com.red.circle.other.app.service.activity.ActivityRechargeService;
import java.util.List;
import lombok.RequiredArgsConstructor;
@ -49,6 +54,7 @@ import org.springframework.web.bind.annotation.*;
public class LotteryActivityRestController extends BaseController {
private final LotteryActivityRestService lotteryActivityRestService;
private final ActivityRechargeService activityRechargeService;
/**
* 获取有效活动列表.
@ -246,4 +252,30 @@ public class LotteryActivityRestController extends BaseController {
lotteryActivityRestService.transfer(cmd);
}
/**
* 查询活动充值排行榜.
*
* @eo.name 查询活动充值排行榜.
* @eo.url /recharge-rank
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/recharge-rank")
public ActivityRechargeRankCO getRechargeRank(ActivityRechargeRankCmd cmd) {
return activityRechargeService.getRechargeRank(cmd);
}
/**
* 查询我的活动充值信息.
*
* @eo.name 查询我的活动充值信息.
* @eo.url /my-recharge
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/my-recharge")
public UserActivityRechargeCO getMyRecharge(UserActivityRechargeCmd cmd) {
return activityRechargeService.getUserRecharge(cmd);
}
}

View File

@ -0,0 +1,105 @@
package com.red.circle.other.app.command.activity;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRechargeRankCO;
import com.red.circle.other.app.dto.clientobject.activity.UserRechargeRankCO;
import com.red.circle.other.app.dto.cmd.activity.ActivityRechargeRankCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.rds.dao.activity.UserActivityRechargeDAO;
import com.red.circle.other.infra.database.rds.dto.activity.ActivityRechargeRankDTO;
import com.red.circle.other.inner.model.dto.user.OwnSpecialIdDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* 活动充值排行榜查询执行器
*/
@Component
@RequiredArgsConstructor
public class ActivityRechargeRankExe {
private final UserActivityRechargeDAO userActivityRechargeDAO;
private final UserProfileGateway userProfileGateway;
private final UserProfileAppConvertor userProfileAppConvertor;
public ActivityRechargeRankCO execute(ActivityRechargeRankCmd cmd) {
Page<ActivityRechargeRankDTO> page = new Page<>(cmd.getPageNo(), cmd.getPageSize());
IPage<ActivityRechargeRankDTO> rankPage = userActivityRechargeDAO.pageRechargeRank(page, cmd.getActivityId());
ActivityRechargeRankCO result = new ActivityRechargeRankCO();
if (CollectionUtils.isEmpty(rankPage.getRecords())) {
result.setTopList(Collections.emptyList());
result.setCurrentUser(buildCurrentUserRank(cmd.getActivityId(), cmd.getReqUserId()));
return result;
}
List<ActivityRechargeRankDTO> records = rankPage.getRecords();
Set<Long> userIds = records.stream()
.map(ActivityRechargeRankDTO::getUserId)
.collect(Collectors.toSet());
Map<Long, UserProfileDTO> userProfileMap = userProfileAppConvertor.toMapUserProfileDTO(
userProfileGateway.mapByUserIds(userIds)
);
List<UserRechargeRankCO> topList = new ArrayList<>();
int rank = (int) ((cmd.getPageNo() - 1) * cmd.getPageSize() + 1);
for (ActivityRechargeRankDTO dto : records) {
UserProfileDTO userProfile = userProfileMap.get(dto.getUserId());
if (Objects.isNull(userProfile)) {
continue;
}
topList.add(new UserRechargeRankCO()
.setRank(rank++)
.setUserId(dto.getUserId())
.setAccount(userProfile.getAccount())
.setUserAvatar(userProfile.getUserAvatar())
.setUserName(userProfile.getUserNickname())
.setSpecialAccount(Optional.ofNullable(userProfile.getOwnSpecialId())
.map(OwnSpecialIdDTO::getAccount)
.orElse(null))
.setAmount("***"));
}
result.setTopList(topList);
result.setCurrentUser(buildCurrentUserRank(cmd.getActivityId(), cmd.getReqUserId()));
return result;
}
private UserRechargeRankCO buildCurrentUserRank(Long activityId, Long userId) {
BigDecimal totalAmount = userActivityRechargeDAO.getUserTotalAmount(activityId, userId);
Integer rank = userActivityRechargeDAO.getUserRank(activityId, userId);
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
userProfileGateway.getByUserId(userId)
);
if (Objects.isNull(userProfile)) {
return null;
}
return new UserRechargeRankCO()
.setRank(rank)
.setUserId(userId)
.setAccount(userProfile.getAccount())
.setUserAvatar(userProfile.getUserAvatar())
.setUserName(userProfile.getUserNickname())
.setSpecialAccount(Optional.ofNullable(userProfile.getOwnSpecialId())
.map(OwnSpecialIdDTO::getAccount)
.orElse(null))
.setAmount(Optional.ofNullable(totalAmount)
.map(BigDecimal::toPlainString)
.orElse("0"));
}
}

View File

@ -0,0 +1,52 @@
package com.red.circle.other.app.command.activity;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.activity.UserActivityRechargeCO;
import com.red.circle.other.app.dto.cmd.activity.UserActivityRechargeCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.rds.service.activity.UserActivityRechargeService;
import com.red.circle.other.inner.model.dto.user.OwnSpecialIdDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Objects;
import java.util.Optional;
/**
* 用户活动充值查询执行器
*/
@Component
@RequiredArgsConstructor
public class UserActivityRechargeExe {
private final UserActivityRechargeService userActivityRechargeService;
private final UserProfileGateway userProfileGateway;
private final UserProfileAppConvertor userProfileAppConvertor;
public UserActivityRechargeCO execute(UserActivityRechargeCmd cmd) {
BigDecimal totalAmount = userActivityRechargeService.getUserTotalAmount(
cmd.getActivityId(),
cmd.getReqUserId()
);
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
userProfileGateway.getByUserId(cmd.getReqUserId())
);
if (Objects.isNull(userProfile)) {
return null;
}
return new UserActivityRechargeCO()
.setUserId(cmd.getReqUserId())
.setAccount(userProfile.getAccount())
.setUserAvatar(userProfile.getUserAvatar())
.setUserName(userProfile.getUserNickname())
.setSpecialAccount(Optional.ofNullable(userProfile.getOwnSpecialId())
.map(OwnSpecialIdDTO::getAccount)
.orElse(null))
.setTotalAmount(Optional.ofNullable(totalAmount).orElse(BigDecimal.ZERO));
}
}

View File

@ -0,0 +1,31 @@
package com.red.circle.other.app.service.activity;
import com.red.circle.other.app.command.activity.ActivityRechargeRankExe;
import com.red.circle.other.app.command.activity.UserActivityRechargeExe;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRechargeRankCO;
import com.red.circle.other.app.dto.clientobject.activity.UserActivityRechargeCO;
import com.red.circle.other.app.dto.cmd.activity.ActivityRechargeRankCmd;
import com.red.circle.other.app.dto.cmd.activity.UserActivityRechargeCmd;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 活动充值服务实现
*/
@Service
@RequiredArgsConstructor
public class ActivityRechargeServiceImpl implements ActivityRechargeService {
private final ActivityRechargeRankExe activityRechargeRankExe;
private final UserActivityRechargeExe userActivityRechargeExe;
@Override
public ActivityRechargeRankCO getRechargeRank(ActivityRechargeRankCmd cmd) {
return activityRechargeRankExe.execute(cmd);
}
@Override
public UserActivityRechargeCO getUserRecharge(UserActivityRechargeCmd cmd) {
return userActivityRechargeExe.execute(cmd);
}
}

View File

@ -0,0 +1,31 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.red.circle.framework.dto.ClientObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serial;
import java.util.List;
/**
* 活动充值排行榜响应
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class ActivityRechargeRankCO extends ClientObject {
@Serial
private static final long serialVersionUID = 1L;
/**
* 排行榜列表
*/
private List<UserRechargeRankCO> topList;
/**
* 当前用户排行信息
*/
private UserRechargeRankCO currentUser;
}

View File

@ -0,0 +1,51 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.framework.dto.ClientObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serial;
import java.math.BigDecimal;
/**
* 用户活动充值信息
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class UserActivityRechargeCO extends ClientObject {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
private String account;
/**
* 用户头像
*/
private String userAvatar;
/**
* 用户昵称
*/
private String userName;
/**
* 靓号
*/
private String specialAccount;
/**
* 累计充值金额
*/
private BigDecimal totalAmount;
}

View File

@ -0,0 +1,55 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.framework.dto.ClientObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serial;
/**
* 用户充值排行信息
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class UserRechargeRankCO extends ClientObject {
@Serial
private static final long serialVersionUID = 1L;
/**
* 排名
*/
private Integer rank;
/**
* 用户ID
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
private String account;
/**
* 用户头像
*/
private String userAvatar;
/**
* 用户昵称
*/
private String userName;
/**
* 靓号
*/
private String specialAccount;
/**
* 充值金额排行榜中隐藏显示为***
*/
private String amount;
}

View File

@ -0,0 +1,28 @@
package com.red.circle.other.app.dto.cmd.activity;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 活动充值排行榜查询命令
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ActivityRechargeRankCmd extends AppExtCommand {
/**
* 活动ID
*/
private Long activityId;
/**
* 页码
*/
private Integer pageNo = 1;
/**
* 每页大小
*/
private Integer pageSize = 20;
}

View File

@ -0,0 +1,18 @@
package com.red.circle.other.app.dto.cmd.activity;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户活动充值查询命令
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class UserActivityRechargeCmd extends AppExtCommand {
/**
* 活动ID
*/
private Long activityId;
}

View File

@ -0,0 +1,28 @@
package com.red.circle.other.app.service.activity;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRechargeRankCO;
import com.red.circle.other.app.dto.clientobject.activity.UserActivityRechargeCO;
import com.red.circle.other.app.dto.cmd.activity.ActivityRechargeRankCmd;
import com.red.circle.other.app.dto.cmd.activity.UserActivityRechargeCmd;
/**
* 活动充值服务
*/
public interface ActivityRechargeService {
/**
* 查询活动充值排行榜
*
* @param cmd 查询命令
* @return 排行榜
*/
ActivityRechargeRankCO getRechargeRank(ActivityRechargeRankCmd cmd);
/**
* 查询用户活动充值信息
*
* @param cmd 查询命令
* @return 充值信息
*/
UserActivityRechargeCO getUserRecharge(UserActivityRechargeCmd cmd);
}

View File

@ -0,0 +1,59 @@
package com.red.circle.other.infra.database.rds.dao.activity;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.dto.activity.ActivityRechargeRankDTO;
import com.red.circle.other.infra.database.rds.entity.activity.UserActivityRecharge;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
/**
* 用户活动充值记录 DAO
*/
public interface UserActivityRechargeDAO extends BaseDAO<UserActivityRecharge> {
/**
* 累加用户活动充值金额
*
* @param userId 用户ID
* @param activityId 活动ID
* @param rechargeDate 充值日期
* @param rechargeType 充值类型
* @param amount 金额
* @return 影响行数
*/
int incrAmount(@Param("userId") Long userId,
@Param("activityId") Long activityId,
@Param("rechargeDate") String rechargeDate,
@Param("rechargeType") String rechargeType,
@Param("amount") BigDecimal amount);
/**
* 按活动查询充值排行榜
*
* @param page 分页参数
* @param activityId 活动ID
* @return 排行榜列表
*/
IPage<ActivityRechargeRankDTO> pageRechargeRank(Page<ActivityRechargeRankDTO> page, @Param("activityId") Long activityId);
/**
* 查询用户在活动中的充值总额
*
* @param activityId 活动ID
* @param userId 用户ID
* @return 充值总额
*/
BigDecimal getUserTotalAmount(@Param("activityId") Long activityId, @Param("userId") Long userId);
/**
* 查询用户在活动中的排名
*
* @param activityId 活动ID
* @param userId 用户ID
* @return 排名从1开始
*/
Integer getUserRank(@Param("activityId") Long activityId, @Param("userId") Long userId);
}

View File

@ -0,0 +1,22 @@
package com.red.circle.other.infra.database.rds.dto.activity;
import lombok.Data;
import java.math.BigDecimal;
/**
* 活动充值排行DTO
*/
@Data
public class ActivityRechargeRankDTO {
/**
* 用户ID
*/
private Long userId;
/**
* 充值总金额
*/
private BigDecimal totalAmount;
}

View File

@ -0,0 +1,57 @@
package com.red.circle.other.infra.database.rds.entity.activity;
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.red.circle.framework.mybatis.entity.TimestampBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serial;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 用户活动充值记录
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user_activity_recharge")
public class UserActivityRecharge extends TimestampBaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键标识
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
* 用户ID
*/
@TableField("user_id")
private Long userId;
/**
* 活动ID
*/
@TableField("activity_id")
private Long activityId;
/**
* 充值日期
*/
@TableField("recharge_date")
private LocalDate rechargeDate;
/**
* 当日充值金额
*/
@TableField("amount")
private BigDecimal amount;
}

View File

@ -0,0 +1,29 @@
package com.red.circle.other.infra.database.rds.service.activity;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import java.math.BigDecimal;
/**
* 用户活动充值记录服务
*/
public interface UserActivityRechargeService {
/**
* 记录用户充值
*
* @param userId 用户ID
* @param amount 充值金额
* @param type 充值类型
*/
void recordRecharge(Long userId, BigDecimal amount, MonthlyRechargeType type);
/**
* 查询用户在活动中的总充值金额
*
* @param userId 用户ID
* @param activityId 活动ID
* @return 总金额
*/
BigDecimal getUserTotalAmount(Long userId, Long activityId);
}

View File

@ -0,0 +1,49 @@
package com.red.circle.other.infra.database.rds.service.activity.impl;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.infra.database.rds.dao.activity.UserActivityRechargeDAO;
import com.red.circle.other.infra.database.rds.service.activity.UserActivityRechargeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 用户活动充值记录服务实现
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserActivityRechargeServiceImpl implements UserActivityRechargeService {
private final UserActivityRechargeDAO userActivityRechargeDAO;
/**
* 固定活动ID
*/
private static final Long FIXED_ACTIVITY_ID = 1L;
@Override
public void recordRecharge(Long userId, BigDecimal amount, MonthlyRechargeType type) {
LocalDate today = LocalDate.now();
String rechargeDate = today.toString();
int result = userActivityRechargeDAO.incrAmount(
userId,
FIXED_ACTIVITY_ID,
rechargeDate,
type.name(),
amount
);
log.info("记录用户活动充值, userId:{}, amount:{}, type:{}, date:{}, result:{}",
userId, amount, type.name(), rechargeDate, result);
}
@Override
public BigDecimal getUserTotalAmount(Long userId, Long activityId) {
return userActivityRechargeDAO.getUserTotalAmount(userId, activityId);
}
}

View File

@ -0,0 +1,47 @@
<?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.red.circle.other.infra.database.rds.dao.activity.UserActivityRechargeDAO">
<update id="incrAmount">
INSERT INTO user_activity_recharge (user_id, activity_id, recharge_date, recharge_type, amount, create_time, update_time)
VALUES (#{userId}, #{activityId}, #{rechargeDate}, #{rechargeType}, #{amount}, NOW(), NOW())
ON DUPLICATE KEY UPDATE
amount = amount + #{amount},
update_time = NOW()
</update>
<select id="pageRechargeRank" resultType="com.red.circle.other.infra.database.rds.dto.activity.ActivityRechargeRankDTO">
SELECT
user_id AS userId,
SUM(amount) AS totalAmount
FROM user_activity_recharge
WHERE activity_id = #{activityId}
GROUP BY user_id
ORDER BY totalAmount DESC
</select>
<select id="getUserTotalAmount" resultType="java.math.BigDecimal">
SELECT IFNULL(SUM(amount), 0)
FROM user_activity_recharge
WHERE activity_id = #{activityId}
AND user_id = #{userId}
</select>
<select id="getUserRank" resultType="java.lang.Integer">
SELECT COUNT(*) + 1
FROM (
SELECT user_id, SUM(amount) AS total_amount
FROM user_activity_recharge
WHERE activity_id = #{activityId}
GROUP BY user_id
) AS ranks
WHERE total_amount > (
SELECT IFNULL(SUM(amount), 0)
FROM user_activity_recharge
WHERE activity_id = #{activityId}
AND user_id = #{userId}
)
</select>
</mapper>

View File

@ -23,6 +23,7 @@ import com.red.circle.other.domain.gateway.PropCouponGateway;
import com.red.circle.other.domain.gateway.game.GameRankingGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.InviteUserGateway;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.propcoupon.PropCoupon;
import com.red.circle.other.domain.ranking.RankingActivityType;
import com.red.circle.other.domain.ranking.RankingCycleType;
@ -87,12 +88,14 @@ public class SpringTest {
private PropsActivityClientService propsActivityClientService;
@Autowired
private LatestMobileDeviceService latestMobileDeviceService;
@Autowired
private UserRegionGateway userRegionGateway;
@Test
public void getDeviceFingerprintByUserId() {
Arrays.asList(1989287386040430594L, 1989287287902359554L, 1989287471839113218L).stream().toList().forEach(id -> {
System.out.println(latestMobileDeviceService.getDeviceFingerprintByUserId(id));
});
boolean eqRegion = userRegionGateway.checkEqRegion(1989287420580524033L, 1957345312961527809L);
}
@Test