From 5b438237517a123489faf73690ebc4d25680ff36 Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Mon, 20 Oct 2025 12:04:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=BD=E5=A5=96=E6=96=B0=E5=A2=9E=E5=A4=9A?= =?UTF-8?q?=E5=B1=82=E6=A6=82=E7=8E=87=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/command/activity/LotteryDrawExe.java | 376 ++++++++++++++---- .../activity/LotteryUserProgressMapper.java | 14 + .../rds/entity/activity/LotteryActivity.java | 30 ++ .../rds/entity/activity/LotteryPrize.java | 12 + .../entity/activity/LotteryUserProgress.java | 89 +++++ .../activity/LotteryUserProgressService.java | 14 + .../impl/LotteryUserProgressServiceImpl.java | 19 + .../activity/LotteryUserProgressMapper.xml | 7 + 8 files changed, 474 insertions(+), 87 deletions(-) create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/activity/LotteryUserProgressMapper.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryUserProgress.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/LotteryUserProgressService.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/impl/LotteryUserProgressServiceImpl.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/activity/LotteryUserProgressMapper.xml diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryDrawExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryDrawExe.java index b496f665..0511e351 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryDrawExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryDrawExe.java @@ -10,24 +10,29 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryRecord; import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicket; import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicketRecord; import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserCount; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserProgress; import com.red.circle.other.infra.database.rds.service.activity.LotteryActivityService; import com.red.circle.other.infra.database.rds.service.activity.LotteryPrizeService; import com.red.circle.other.infra.database.rds.service.activity.LotteryRecordService; import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketRecordService; import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketService; import com.red.circle.other.infra.database.rds.service.activity.LotteryUserCountService; +import com.red.circle.other.infra.database.rds.service.activity.LotteryUserProgressService; import com.red.circle.other.inner.asserts.lottery.LotteryErrorCode; import com.red.circle.tool.core.date.TimestampUtils; import java.math.BigDecimal; import java.sql.Timestamp; import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.WeekFields; import java.util.ArrayList; import java.util.List; -import java.util.Random; -import java.util.UUID; +import java.util.Locale; import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -48,6 +53,7 @@ public class LotteryDrawExe { private final LotteryTicketService lotteryTicketService; private final LotteryTicketRecordService lotteryTicketRecordService; private final LotteryUserCountService lotteryUserCountService; + private final LotteryUserProgressService lotteryUserProgressService; @Transactional(rollbackFor = Exception.class) public LotteryDrawResultCO execute(LotteryDrawCmd cmd) { @@ -61,30 +67,36 @@ public class LotteryDrawExe { // 2. 校验抽奖次数限制 validateDrawCount(userId, activityId, activity); - // 3. 校验并扣除抽奖券(写死消耗1次) - Integer needTicket = 1; - deductTicket(userId, needTicket); + // 3. 校验并扣除抽奖券(如果需要) + if (activity.getNeedTicket() == 1) { + deductTicket(userId, activity.getTicketCost()); + } - // 4. 执行抽奖算法 - LotteryPrize prize = drawPrize(activityId); + // 4. 获取或创建用户进度(用于动态概率) + LotteryUserProgress progress = getOrCreateUserProgress(userId, activityId); - // 5. 扣减奖品库存(使用悲观锁防止超卖) + // 5. 执行抽奖算法(动态概率) + LotteryPrize prize = drawPrizeWithDynamicProbability(activityId, activity, progress); + + // 6. 扣减奖品库存(使用悲观锁防止超卖) if (prize != null) { boolean deductSuccess = deductPrizeStock(prize.getId()); if (!deductSuccess) { - // 库存扣减失败,视为未中奖 log.warn("Prize stock deduction failed, prizeId: {}", prize.getId()); prize = null; } } - // 6. 保存抽奖记录 - LotteryRecord record = saveDrawRecord(userId, activityId, prize, now); + // 7. 保存抽奖记录 + LotteryRecord record = saveDrawRecord(userId, activityId, prize, now, null, 1); - // 7. 更新用户抽奖次数 + // 8. 更新用户抽奖次数 updateUserDrawCount(userId, activityId); - // 8. 组装返回结果 + // 9. 更新用户进度(累加金额和次数) + updateUserProgress(progress, prize); + + // 10. 组装返回结果 return convertToResultCO(record, prize); } @@ -121,67 +133,69 @@ public class LotteryDrawExe { } /** - * 扣除抽奖券(优化:使用批量处理,减少数据库交互). + * 获取或创建用户进度. */ - private void deductTicket(Long userId, Integer ticketCost) { - // 查询可用抽奖券(按过期时间排序,先用快过期的) - List tickets = lotteryTicketService.query() - .eq(LotteryTicket::getUserId, userId) - .eq(LotteryTicket::getStatus, 1) - .gt(LotteryTicket::getRemainingCount, 0) - .orderByAsc(LotteryTicket::getExpireTime) - .list(); - - // 校验总数量 - int totalRemaining = tickets.stream() - .mapToInt(LotteryTicket::getRemainingCount) - .sum(); - ResponseAssert.isTrue(LotteryErrorCode.INSUFFICIENT_TICKETS, totalRemaining >= ticketCost); - - // 批量扣减 - List records = new ArrayList<>(); - int remaining = ticketCost; + private LotteryUserProgress getOrCreateUserProgress(Long userId, Long activityId) { + String weekKey = getWeekKey(); - for (LotteryTicket ticket : tickets) { - if (remaining <= 0) { - break; - } + LotteryUserProgress progress = lotteryUserProgressService.query() + .eq(LotteryUserProgress::getUserId, userId) + .eq(LotteryUserProgress::getActivityId, activityId) + .eq(LotteryUserProgress::getWeekKey, weekKey) + .getOne(); - int deduct = Math.min(remaining, ticket.getRemainingCount()); - - // 使用乐观锁更新(防止并发问题) - boolean updateSuccess = lotteryTicketService.update() - .eq(LotteryTicket::getId, ticket.getId()) - .eq(LotteryTicket::getRemainingCount, ticket.getRemainingCount()) // 乐观锁 - .setSql("used_count = used_count + " + deduct) - .setSql("remaining_count = remaining_count - " + deduct) - .execute(); - - ResponseAssert.isTrue(LotteryErrorCode.INSUFFICIENT_TICKETS, updateSuccess); - - // 记录使用日志 - LotteryTicketRecord ticketRecord = new LotteryTicketRecord(); - ticketRecord.setUserId(userId); - ticketRecord.setTicketId(ticket.getId()); - ticketRecord.setChangeCount(-deduct); - ticketRecord.setChangeType(2); // 消耗 - ticketRecord.setRemark("抽奖消耗"); - records.add(ticketRecord); - - remaining -= deduct; + if (progress == null) { + progress = new LotteryUserProgress(); + progress.setUserId(userId); + progress.setActivityId(activityId); + progress.setWeekKey(weekKey); + progress.setTotalAmount(BigDecimal.ZERO); + progress.setTotalDrawCount(0); + progress.setBigPrizeCount(0); + progress.setCurrentStage("EARLY"); + progress.setLastDrawDate(LocalDate.now()); + lotteryUserProgressService.save(progress); } - // 批量插入日志 - if (!records.isEmpty()) { - lotteryTicketRecordService.saveBatch(records); - } + return progress; } /** - * 抽奖算法 - 权重随机(优化:使用ThreadLocalRandom). + * 动态概率抽奖. */ - private LotteryPrize drawPrize(Long activityId) { - // 查询所有可用奖品 + private LotteryPrize drawPrizeWithDynamicProbability(Long activityId, LotteryActivity activity, LotteryUserProgress progress) { + // 判断是否启用动态概率 + if (activity.getEnableDynamicProbability() == null || activity.getEnableDynamicProbability() != 1) { + // 未启用动态概率,使用普通抽奖 + return drawPrizeNormal(activityId); + } + + // 1. 判断用户所处阶段 + String stage = determinePrizePool(progress, activity); + log.info("User stage: {}, userId: {}, drawCount: {}, totalAmount: {}", + stage, progress.getUserId(), progress.getTotalDrawCount(), progress.getTotalAmount()); + + // 2. 查询对应奖品池的奖品 + List prizes = getPrizesByPool(activityId, stage); + + // 3. 过滤会超过提现门槛的大奖 + prizes = filterOverThresholdPrizes(prizes, progress, activity.getWithdrawThreshold()); + + if (prizes.isEmpty()) { + return null; + } + + // 4. 动态调整概率 + adjustProbability(prizes, progress, activity.getWithdrawThreshold()); + + // 5. 执行抽奖 + return randomSelectByProbability(prizes); + } + + /** + * 普通抽奖(不使用动态概率). + */ + private LotteryPrize drawPrizeNormal(Long activityId) { List prizes = lotteryPrizeService.query() .eq(LotteryPrize::getActivityId, activityId) .gt(LotteryPrize::getRemainingStock, 0) @@ -192,11 +206,132 @@ public class LotteryDrawExe { return null; } + return randomSelectByProbability(prizes); + } + + /** + * 判断奖品池阶段. + */ + private String determinePrizePool(LotteryUserProgress progress, LotteryActivity activity) { + int drawCount = progress.getTotalDrawCount(); + BigDecimal totalAmount = progress.getTotalAmount(); + BigDecimal threshold = activity.getWithdrawThreshold() != null + ? activity.getWithdrawThreshold() + : new BigDecimal("10"); + + // 默认阶段配置 + int earlyMax = activity.getEarlyStageMaxCount() != null ? activity.getEarlyStageMaxCount() : 20; + int middleMax = activity.getMiddleStageMaxCount() != null ? activity.getMiddleStageMaxCount() : 50; + BigDecimal lateTrigger = activity.getLateStageTriggerAmount() != null + ? activity.getLateStageTriggerAmount() + : new BigDecimal("2"); + + // 后期池:抽奖次数多 且 接近门槛 + BigDecimal gap = threshold.subtract(totalAmount); + if (drawCount >= middleMax && gap.compareTo(lateTrigger) <= 0) { + return "LATE"; + } + + // 中期池:抽奖次数中等 + if (drawCount >= earlyMax) { + return "MIDDLE"; + } + + // 前期池 + return "EARLY"; + } + + /** + * 根据奖品池查询奖品. + */ + private List getPrizesByPool(Long activityId, String pool) { + return lotteryPrizeService.query() + .eq(LotteryPrize::getActivityId, activityId) + .gt(LotteryPrize::getRemainingStock, 0) + .and(wrapper -> wrapper + .eq(LotteryPrize::getPrizePool, pool) + .or() + .eq(LotteryPrize::getPrizePool, "ALL") + ) + .orderByAsc(LotteryPrize::getSortOrder) + .list(); + } + + /** + * 过滤会超过门槛的奖品. + */ + private List filterOverThresholdPrizes(List prizes, + LotteryUserProgress progress, + BigDecimal threshold) { + if (threshold == null) { + return prizes; + } + + BigDecimal currentAmount = progress.getTotalAmount(); + + // 如果已经达到门槛,本周不再发放任何奖品 + if (currentAmount.compareTo(threshold) >= 0) { + log.info("User reached threshold, no more prizes this week. userId: {}, amount: {}", + progress.getUserId(), currentAmount); + return new ArrayList<>(); + } + + return prizes.stream() + .filter(prize -> { + // 过滤掉会让用户超过门槛的大奖 + BigDecimal afterAmount = currentAmount.add(prize.getPrizeValue()); + return afterAmount.compareTo(threshold) <= 0; + }) + .collect(Collectors.toList()); + } + + /** + * 动态调整概率. + */ + private void adjustProbability(List prizes, LotteryUserProgress progress, BigDecimal threshold) { + if (threshold == null) { + return; + } + + BigDecimal totalAmount = progress.getTotalAmount(); + BigDecimal gap = threshold.subtract(totalAmount); // 距离门槛还差多少 + + for (LotteryPrize prize : prizes) { + BigDecimal prizeValue = prize.getPrizeValue(); + BigDecimal originalProbability = prize.getProbability(); + + // 如果这个奖品能让用户达到或接近门槛,提升概率 + BigDecimal diff = gap.subtract(prizeValue).abs(); + if (diff.compareTo(new BigDecimal("0.5")) <= 0) { + // 差距在0.5美元内,概率翻倍 + prize.setProbability(originalProbability.multiply(new BigDecimal("2"))); + log.debug("Boost prize probability x2, prizeName: {}, gap: {}", prize.getPrizeName(), gap); + } + + // 如果用户已经很接近门槛(>9美元),大幅提升能凑整的小奖概率 + if (totalAmount.compareTo(new BigDecimal("9")) > 0) { + if (prizeValue.compareTo(gap) <= 0 && prizeValue.compareTo(new BigDecimal("0.5")) >= 0) { + prize.setProbability(originalProbability.multiply(new BigDecimal("3"))); + log.debug("Boost prize probability x3 (near threshold), prizeName: {}", prize.getPrizeName()); + } + } + } + } + + /** + * 按概率随机选择奖品. + */ + private LotteryPrize randomSelectByProbability(List prizes) { // 计算总概率 BigDecimal totalProbability = prizes.stream() .map(LotteryPrize::getProbability) .reduce(BigDecimal.ZERO, BigDecimal::add); + if (totalProbability.compareTo(BigDecimal.ZERO) == 0) { + return null; + } + + // 生成随机数 double randomValue = ThreadLocalRandom.current().nextDouble() * totalProbability.doubleValue(); double cumulative = 0.0; @@ -212,37 +347,87 @@ public class LotteryDrawExe { } /** - * 扣减奖品库存(使用悲观锁防止超卖). + * 扣除抽奖券. + */ + private void deductTicket(Long userId, Integer ticketCost) { + List tickets = lotteryTicketService.query() + .eq(LotteryTicket::getUserId, userId) + .eq(LotteryTicket::getStatus, 1) + .gt(LotteryTicket::getRemainingCount, 0) + .orderByAsc(LotteryTicket::getExpireTime) + .list(); + + int totalRemaining = tickets.stream() + .mapToInt(LotteryTicket::getRemainingCount) + .sum(); + ResponseAssert.isTrue(LotteryErrorCode.INSUFFICIENT_TICKETS, totalRemaining >= ticketCost); + + List records = new ArrayList<>(); + int remaining = ticketCost; + + for (LotteryTicket ticket : tickets) { + if (remaining <= 0) { + break; + } + + int deduct = Math.min(remaining, ticket.getRemainingCount()); + + boolean updateSuccess = lotteryTicketService.update() + .eq(LotteryTicket::getId, ticket.getId()) + .eq(LotteryTicket::getRemainingCount, ticket.getRemainingCount()) + .setSql("used_count = used_count + " + deduct) + .setSql("remaining_count = remaining_count - " + deduct) + .execute(); + + ResponseAssert.isTrue(LotteryErrorCode.INSUFFICIENT_TICKETS, updateSuccess); + + LotteryTicketRecord ticketRecord = new LotteryTicketRecord(); + ticketRecord.setUserId(userId); + ticketRecord.setTicketId(ticket.getId()); + ticketRecord.setChangeCount(-deduct); + ticketRecord.setChangeType(2); + ticketRecord.setRemark("抽奖消耗"); + records.add(ticketRecord); + + remaining -= deduct; + } + + if (!records.isEmpty()) { + lotteryTicketRecordService.saveBatch(records); + } + } + + /** + * 扣减奖品库存. */ private boolean deductPrizeStock(Long prizeId) { - // 使用数据库级别的原子操作 - int affected = lotteryPrizeService.update() + return lotteryPrizeService.update() .eq(LotteryPrize::getId, prizeId) - .gt(LotteryPrize::getRemainingStock, 0) // 必须有库存 + .gt(LotteryPrize::getRemainingStock, 0) .setSql("remaining_stock = remaining_stock - 1") - .execute() - ? 1 : 0; - - return affected > 0; + .execute(); } /** * 保存抽奖记录. */ - private LotteryRecord saveDrawRecord(Long userId, Long activityId, LotteryPrize prize, Timestamp now) { + private LotteryRecord saveDrawRecord(Long userId, Long activityId, LotteryPrize prize, + Timestamp now, String batchNo, Integer drawType) { String recordNo = generateRecordNo(); LotteryRecord record = new LotteryRecord(); record.setRecordNo(recordNo); record.setUserId(userId); record.setActivityId(activityId); record.setDrawTime(now); + record.setDrawBatchNo(batchNo); + record.setDrawType(drawType); if (prize != null) { record.setPrizeId(prize.getId()); record.setPrizeName(prize.getPrizeName()); record.setPrizeType(prize.getPrizeType()); record.setIsWin(1); - record.setPrizeStatus(0); // 待发放 + record.setPrizeStatus(0); } else { record.setIsWin(0); } @@ -252,12 +437,11 @@ public class LotteryDrawExe { } /** - * 更新用户抽奖次数(优化:减少查询). + * 更新用户抽奖次数. */ private void updateUserDrawCount(Long userId, Long activityId) { LocalDate today = LocalDate.now(); - // 先尝试直接更新 boolean updated = lotteryUserCountService.update() .eq(LotteryUserCount::getUserId, userId) .eq(LotteryUserCount::getActivityId, activityId) @@ -265,7 +449,6 @@ public class LotteryDrawExe { .setSql("draw_count = draw_count + 1") .execute(); - // 如果更新失败(记录不存在),则插入 if (!updated) { try { LotteryUserCount userCount = new LotteryUserCount(); @@ -275,7 +458,6 @@ public class LotteryDrawExe { userCount.setDrawCount(1); lotteryUserCountService.save(userCount); } catch (Exception e) { - // 并发情况下可能插入失败,再次更新 lotteryUserCountService.update() .eq(LotteryUserCount::getUserId, userId) .eq(LotteryUserCount::getActivityId, activityId) @@ -287,7 +469,33 @@ public class LotteryDrawExe { } /** - * 生成记录编号(优化:使用更高效的方式). + * 更新用户进度. + */ + private void updateUserProgress(LotteryUserProgress progress, LotteryPrize prize) { + BigDecimal addAmount = prize != null ? prize.getPrizeValue() : BigDecimal.ZERO; + int addBigPrizeCount = (prize != null && prize.getPrizeValue().compareTo(new BigDecimal("0.5")) >= 0) ? 1 : 0; + + lotteryUserProgressService.update() + .eq(LotteryUserProgress::getId, progress.getId()) + .setSql("total_amount = total_amount + " + addAmount) + .setSql("total_draw_count = total_draw_count + 1") + .setSql("big_prize_count = big_prize_count + " + addBigPrizeCount) + .set(LotteryUserProgress::getLastDrawDate, LocalDate.now()) + .execute(); + } + + /** + * 生成周标识. + */ + private String getWeekKey() { + LocalDate now = LocalDate.now(); + int year = now.getYear(); + int weekOfYear = now.get(WeekFields.of(Locale.getDefault()).weekOfYear()); + return String.format("%dW%02d", year, weekOfYear); + } + + /** + * 生成记录编号. */ private String generateRecordNo() { return "LT" + System.currentTimeMillis() + @@ -305,13 +513,7 @@ public class LotteryDrawExe { if (prize != null) { LotteryPrizeCO prizeCO = new LotteryPrizeCO(); - prizeCO.setId(prize.getId()); - prizeCO.setPrizeCode(prize.getPrizeCode()); - prizeCO.setPrizeName(prize.getPrizeName()); - prizeCO.setPrizeType(prize.getPrizeType()); - prizeCO.setPrizeValue(prize.getPrizeValue()); - prizeCO.setPrizeImage(prize.getPrizeImage()); - prizeCO.setPrizeLevel(prize.getPrizeLevel()); + BeanUtils.copyProperties(prize, prizeCO); result.setPrize(prizeCO); } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/activity/LotteryUserProgressMapper.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/activity/LotteryUserProgressMapper.java new file mode 100644 index 00000000..048abcc9 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/activity/LotteryUserProgressMapper.java @@ -0,0 +1,14 @@ +package com.red.circle.other.infra.database.rds.dao.activity; + +import com.red.circle.framework.mybatis.dao.BaseDAO; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserProgress; + +/** + * 用户抽奖进度Mapper. + * + * @author system + * @since 2025-10-20 + */ +public interface LotteryUserProgressMapper extends BaseDAO { + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryActivity.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryActivity.java index 0f38bec3..e3bde15a 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryActivity.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryActivity.java @@ -144,4 +144,34 @@ public class LotteryActivity implements Serializable { @TableField("multi_draw_guarantee_min_amount") private BigDecimal multiDrawGuaranteeMinAmount; + /** + * 是否启用动态概率:0-否,1-是. + */ + @TableField("enable_dynamic_probability") + private Integer enableDynamicProbability; + + /** + * 提现门槛金额. + */ + @TableField("withdraw_threshold") + private BigDecimal withdrawThreshold; + + /** + * 前期池抽奖次数上限. + */ + @TableField("early_stage_max_count") + private Integer earlyStageMaxCount; + + /** + * 中期池抽奖次数上限. + */ + @TableField("middle_stage_max_count") + private Integer middleStageMaxCount; + + /** + * 后期池触发金额(距离门槛). + */ + @TableField("late_stage_trigger_amount") + private BigDecimal lateStageTriggerAmount; + } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryPrize.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryPrize.java index 99486dea..5d089325 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryPrize.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryPrize.java @@ -111,6 +111,18 @@ public class LotteryPrize implements Serializable { @TableField("is_guarantee_prize") private Integer isGuaranteePrize; + /** + * 奖品池:EARLY-前期池,MIDDLE-中期池,LATE-后期池,ALL-全部. + */ + @TableField("prize_pool") + private String prizePool; + + /** + * 每周发放上限(0不限制). + */ + @TableField("week_limit") + private Integer weekLimit; + /** * 排序(九宫格位置). */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryUserProgress.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryUserProgress.java new file mode 100644 index 00000000..ec3b16df --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryUserProgress.java @@ -0,0 +1,89 @@ +package com.red.circle.other.infra.database.rds.entity.activity; + +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 java.io.Serial; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.LocalDate; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + *

+ * 用户抽奖进度表. + *

+ * + * @author system + * @since 2025-10-20 + */ +@Data +@Accessors(chain = true) +@TableName("lottery_user_progress") +public class LotteryUserProgress { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键标识. + */ + @TableId("id") + private Long id; + + /** + * 用户ID. + */ + @TableField("user_id") + private Long userId; + + /** + * 活动ID. + */ + @TableField("activity_id") + private Long activityId; + + /** + * 周标识:2025W42. + */ + @TableField("week_key") + private String weekKey; + + /** + * 本周累计金额. + */ + @TableField("total_amount") + private BigDecimal totalAmount; + + /** + * 本周抽奖次数. + */ + @TableField("total_draw_count") + private Integer totalDrawCount; + + /** + * 本周大奖次数. + */ + @TableField("big_prize_count") + private Integer bigPrizeCount; + + /** + * 当前所处阶段:EARLY-前期,MIDDLE-中期,LATE-后期. + */ + @TableField("current_stage") + private String currentStage; + + /** + * 上次抽奖日期. + */ + @TableField("last_draw_date") + private LocalDate lastDrawDate; + + private Timestamp createTime; + + private Timestamp updateTime; + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/LotteryUserProgressService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/LotteryUserProgressService.java new file mode 100644 index 00000000..57e3da44 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/LotteryUserProgressService.java @@ -0,0 +1,14 @@ +package com.red.circle.other.infra.database.rds.service.activity; + +import com.red.circle.framework.mybatis.service.BaseService; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserProgress; + +/** + * 用户抽奖进度服务. + * + * @author system + * @since 2025-10-20 + */ +public interface LotteryUserProgressService extends BaseService { + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/impl/LotteryUserProgressServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/impl/LotteryUserProgressServiceImpl.java new file mode 100644 index 00000000..a337c062 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/activity/impl/LotteryUserProgressServiceImpl.java @@ -0,0 +1,19 @@ +package com.red.circle.other.infra.database.rds.service.activity.impl; + +import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserProgress; +import com.red.circle.other.infra.database.rds.dao.activity.LotteryUserProgressMapper; +import com.red.circle.other.infra.database.rds.service.activity.LotteryUserProgressService; +import org.springframework.stereotype.Service; + +/** + * 用户抽奖进度服务实现. + * + * @author system + * @since 2025-10-20 + */ +@Service +public class LotteryUserProgressServiceImpl extends BaseServiceImpl + implements LotteryUserProgressService { + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/activity/LotteryUserProgressMapper.xml b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/activity/LotteryUserProgressMapper.xml new file mode 100644 index 00000000..6733dc76 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/activity/LotteryUserProgressMapper.xml @@ -0,0 +1,7 @@ + + + + +