From 5b42ea88403e2df452d0d8d1db3474206e317fef Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Mon, 20 Oct 2025 11:01:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=BD=E5=A5=96=E6=96=B0=E5=A2=9E=E8=BF=9E?= =?UTF-8?q?=E6=8A=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asserts/lottery/LotteryErrorCode.java | 5 + .../LotteryActivityRestController.java | 15 + .../command/activity/LotteryMultiDrawExe.java | 426 ++++++++++++++++++ .../LotteryActivityRestServiceImpl.java | 9 + .../activity/LotteryMultiDrawResultCO.java | 65 +++ .../dto/cmd/activity/LotteryMultiDrawCmd.java | 38 ++ .../activity/LotteryActivityRestService.java | 7 + .../rds/entity/activity/LotteryActivity.java | 33 +- .../rds/entity/activity/LotteryPrize.java | 13 +- .../rds/entity/activity/LotteryRecord.java | 12 + 10 files changed, 620 insertions(+), 3 deletions(-) create mode 100644 rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryMultiDrawExe.java create mode 100644 rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/activity/LotteryMultiDrawResultCO.java create mode 100644 rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryMultiDrawCmd.java diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/lottery/LotteryErrorCode.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/lottery/LotteryErrorCode.java index 37ffd533..0f7b3b9f 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/lottery/LotteryErrorCode.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/lottery/LotteryErrorCode.java @@ -157,6 +157,11 @@ public enum LotteryErrorCode implements IResponseErrorCode { */ USER_DRAW_COUNT_UPDATE_FAILED(9729, "Failed to update user draw count"), + /** + * 活动未启用连抽功能. + */ + MULTI_DRAW_NOT_ENABLED(9730, "Multi-draw feature is not enabled for this activity"), + ; private final Integer code; diff --git a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java index 91286ef9..258a142d 100644 --- a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java +++ b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java @@ -6,9 +6,11 @@ import com.red.circle.framework.web.controller.BaseController; import com.red.circle.other.app.dto.clientobject.activity.LotteryActivityCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryActivityDetailCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryDrawResultCO; +import com.red.circle.other.app.dto.clientobject.activity.LotteryMultiDrawResultCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryRecordCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryTicketCO; 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.LotteryRecordQryCmd; import com.red.circle.other.app.service.activity.LotteryActivityRestService; @@ -82,6 +84,19 @@ public class LotteryActivityRestController extends BaseController { return lotteryActivityRestService.draw(cmd); } + /** + * 执行连抽. + * + * @eo.name 执行连抽. + * @eo.url /multi-draw + * @eo.method post + * @eo.request-type json + */ + @PostMapping("/multi-draw") + public LotteryMultiDrawResultCO multiDraw(@RequestBody @Validated LotteryMultiDrawCmd cmd) { + return lotteryActivityRestService.multiDraw(cmd); + } + /** * 查询我的中奖记录. * diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryMultiDrawExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryMultiDrawExe.java new file mode 100644 index 00000000..1ea99d46 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryMultiDrawExe.java @@ -0,0 +1,426 @@ +package com.red.circle.other.app.command.activity; + +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.other.app.dto.clientobject.activity.LotteryDrawResultCO; +import com.red.circle.other.app.dto.clientobject.activity.LotteryMultiDrawResultCO; +import com.red.circle.other.app.dto.clientobject.activity.LotteryPrizeCO; +import com.red.circle.other.app.dto.cmd.activity.LotteryMultiDrawCmd; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryActivity; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryPrize; +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.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.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.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * 执行连抽. + * + * @author system + * @since 2025-10-20 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class LotteryMultiDrawExe { + + private final LotteryActivityService lotteryActivityService; + private final LotteryPrizeService lotteryPrizeService; + private final LotteryRecordService lotteryRecordService; + private final LotteryTicketService lotteryTicketService; + private final LotteryTicketRecordService lotteryTicketRecordService; + private final LotteryUserCountService lotteryUserCountService; + + @Transactional(rollbackFor = Exception.class) + public LotteryMultiDrawResultCO execute(LotteryMultiDrawCmd cmd) { + Long userId = cmd.requiredReqUserId(); + Long activityId = cmd.getActivityId(); + Integer drawCount = cmd.getDrawCount(); + Timestamp now = TimestampUtils.now(); + + // 1. 校验活动 + LotteryActivity activity = validateActivity(activityId, now); + + // 2. 校验是否启用连抽 + ResponseAssert.isTrue(LotteryErrorCode.MULTI_DRAW_NOT_ENABLED, + activity.getEnableMultiDraw() != null && activity.getEnableMultiDraw() == 1); + + // 3. 校验抽奖次数限制(需要剩余足够次数) + validateDrawCount(userId, activityId, activity, drawCount); + + // 4. 校验并扣除抽奖券(连抽消耗更少) + Integer ticketCost = activity.getMultiDrawTicketCost() != null + ? activity.getMultiDrawTicketCost() + : drawCount * activity.getTicketCost(); + + if (activity.getNeedTicket() == 1) { + deductTicket(userId, ticketCost); + } + + // 5. 生成批次号 + String batchNo = generateBatchNo(); + + // 6. 执行连抽 + List results = new ArrayList<>(); + boolean guaranteeTriggered = false; + + // 前N-1次正常抽奖(使用连抽概率) + for (int i = 0; i < drawCount - 1; i++) { + LotteryPrize prize = drawPrizeWithMultiProbability(activityId); + + // 扣减库存 + if (prize != null) { + deductPrizeStock(prize.getId()); + } + + // 保存记录 + LotteryRecord record = saveDrawRecord(userId, activityId, prize, now, batchNo, 2); + results.add(convertToResultCO(record, prize)); + } + + // 最后一次:检查是否需要保底 + BigDecimal totalAmount = calculateTotalAmount(results); + BigDecimal guaranteeMinAmount = activity.getMultiDrawGuaranteeMinAmount() != null + ? activity.getMultiDrawGuaranteeMinAmount() + : BigDecimal.ZERO; + + LotteryPrize lastPrize; + if (totalAmount.compareTo(guaranteeMinAmount) < 0) { + // 触发保底 + guaranteeTriggered = true; + lastPrize = getGuaranteePrize(activityId, activity.getMultiDrawGuaranteePrizeId()); + log.info("Multi-draw guarantee triggered, userId: {}, activityId: {}", userId, activityId); + } else { + // 正常抽奖 + lastPrize = drawPrizeWithMultiProbability(activityId); + } + + // 扣减库存 + if (lastPrize != null) { + deductPrizeStock(lastPrize.getId()); + } + + // 保存最后一次记录 + LotteryRecord lastRecord = saveDrawRecord(userId, activityId, lastPrize, now, batchNo, 2); + results.add(convertToResultCO(lastRecord, lastPrize)); + + // 7. 更新用户抽奖次数(+drawCount次) + updateUserDrawCount(userId, activityId, drawCount); + + // 8. 组装返回结果 + return buildMultiDrawResult(batchNo, results, guaranteeTriggered); + } + + /** + * 校验活动状态和时间. + */ + private LotteryActivity validateActivity(Long activityId, Timestamp now) { + LotteryActivity activity = lotteryActivityService.getById(activityId); + ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_NOT_FOUND, activity != null); + ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_CLOSED, activity.getStatus() == 1); + ResponseAssert.isTrue(LotteryErrorCode.NOT_IN_ACTIVITY_TIME, + now.after(activity.getStartTime()) && now.before(activity.getEndTime())); + + return activity; + } + + /** + * 校验抽奖次数限制. + */ + private void validateDrawCount(Long userId, Long activityId, LotteryActivity activity, Integer drawCount) { + if (activity.getDrawCountLimit() == null || activity.getDrawCountLimit() <= 0) { + return; // 无限制 + } + + LotteryUserCount userCount = lotteryUserCountService.query() + .eq(LotteryUserCount::getUserId, userId) + .eq(LotteryUserCount::getActivityId, activityId) + .eq(LotteryUserCount::getDrawDate, LocalDate.now()) + .getOne(); + + int currentDrawCount = userCount != null ? userCount.getDrawCount() : 0; + ResponseAssert.isTrue(LotteryErrorCode.DRAW_COUNT_EXHAUSTED, + currentDrawCount + drawCount <= activity.getDrawCountLimit()); + } + + /** + * 扣除抽奖券. + */ + 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 LotteryPrize drawPrizeWithMultiProbability(Long activityId) { + List prizes = lotteryPrizeService.query() + .eq(LotteryPrize::getActivityId, activityId) + .gt(LotteryPrize::getRemainingStock, 0) + .orderByAsc(LotteryPrize::getSortOrder) + .list(); + + if (prizes.isEmpty()) { + return null; + } + + // 使用连抽概率(如果配置了) + BigDecimal totalProbability = prizes.stream() + .map(prize -> prize.getMultiDrawProbability() != null + ? prize.getMultiDrawProbability() + : prize.getProbability()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + double randomValue = ThreadLocalRandom.current().nextDouble() * totalProbability.doubleValue(); + double cumulative = 0.0; + + for (LotteryPrize prize : prizes) { + BigDecimal probability = prize.getMultiDrawProbability() != null + ? prize.getMultiDrawProbability() + : prize.getProbability(); + cumulative += probability.doubleValue(); + if (randomValue <= cumulative) { + return prize; + } + } + + return prizes.get(prizes.size() - 1); + } + + /** + * 获取保底奖品. + */ + private LotteryPrize getGuaranteePrize(Long activityId, Long guaranteePrizeId) { + if (guaranteePrizeId != null) { + LotteryPrize prize = lotteryPrizeService.getById(guaranteePrizeId); + if (prize != null && prize.getRemainingStock() > 0) { + return prize; + } + } + + // 如果没有配置保底奖品ID,查找可作为保底的奖品 + return lotteryPrizeService.query() + .eq(LotteryPrize::getActivityId, activityId) + .eq(LotteryPrize::getIsGuaranteePrize, 1) + .gt(LotteryPrize::getRemainingStock, 0) + .orderByAsc(LotteryPrize::getPrizeValue) + .last("LIMIT 1") + .getOne(); + } + + /** + * 扣减奖品库存. + */ + private boolean deductPrizeStock(Long prizeId) { + return lotteryPrizeService.update() + .eq(LotteryPrize::getId, prizeId) + .gt(LotteryPrize::getRemainingStock, 0) + .setSql("remaining_stock = remaining_stock - 1") + .execute(); + } + + /** + * 保存抽奖记录. + */ + 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); + } else { + record.setIsWin(0); + } + + lotteryRecordService.save(record); + return record; + } + + /** + * 更新用户抽奖次数. + */ + private void updateUserDrawCount(Long userId, Long activityId, Integer drawCount) { + LocalDate today = LocalDate.now(); + + boolean updated = lotteryUserCountService.update() + .eq(LotteryUserCount::getUserId, userId) + .eq(LotteryUserCount::getActivityId, activityId) + .eq(LotteryUserCount::getDrawDate, today) + .setSql("draw_count = draw_count + " + drawCount) + .execute(); + + if (!updated) { + try { + LotteryUserCount userCount = new LotteryUserCount(); + userCount.setUserId(userId); + userCount.setActivityId(activityId); + userCount.setDrawDate(today); + userCount.setDrawCount(drawCount); + lotteryUserCountService.save(userCount); + } catch (Exception e) { + lotteryUserCountService.update() + .eq(LotteryUserCount::getUserId, userId) + .eq(LotteryUserCount::getActivityId, activityId) + .eq(LotteryUserCount::getDrawDate, today) + .setSql("draw_count = draw_count + " + drawCount) + .execute(); + } + } + } + + /** + * 计算前N次的总金额. + */ + private BigDecimal calculateTotalAmount(List results) { + return results.stream() + .filter(LotteryDrawResultCO::getIsWin) + .map(r -> r.getPrize() != null ? r.getPrize().getPrizeValue() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + /** + * 转换为结果CO. + */ + private LotteryDrawResultCO convertToResultCO(LotteryRecord record, LotteryPrize prize) { + LotteryDrawResultCO result = new LotteryDrawResultCO(); + result.setRecordNo(record.getRecordNo()); + result.setIsWin(record.getIsWin() == 1); + result.setDrawTime(record.getDrawTime()); + + if (prize != null) { + LotteryPrizeCO prizeCO = new LotteryPrizeCO(); + BeanUtils.copyProperties(prize, prizeCO); + result.setPrize(prizeCO); + } + + return result; + } + + /** + * 构建连抽结果. + */ + private LotteryMultiDrawResultCO buildMultiDrawResult(String batchNo, + List results, + boolean guaranteeTriggered) { + LotteryMultiDrawResultCO multiResult = new LotteryMultiDrawResultCO(); + multiResult.setBatchNo(batchNo); + multiResult.setDrawType(2); + multiResult.setResults(results); + + // 汇总信息 + LotteryMultiDrawResultCO.MultiDrawSummary summary = new LotteryMultiDrawResultCO.MultiDrawSummary(); + + BigDecimal totalAmount = results.stream() + .filter(LotteryDrawResultCO::getIsWin) + .map(r -> r.getPrize() != null ? r.getPrize().getPrizeValue() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + summary.setTotalAmount(totalAmount); + + summary.setGuaranteeTriggered(guaranteeTriggered); + + long winCount = results.stream() + .filter(LotteryDrawResultCO::getIsWin) + .count(); + summary.setWinCount((int) winCount); + + long bigPrizeCount = results.stream() + .filter(LotteryDrawResultCO::getIsWin) + .filter(r -> r.getPrize() != null && r.getPrize().getPrizeValue().compareTo(new BigDecimal("0.5")) >= 0) + .count(); + summary.setBigPrizeCount((int) bigPrizeCount); + + multiResult.setSummary(summary); + + return multiResult; + } + + /** + * 生成批次号. + */ + private String generateBatchNo() { + return "BATCH" + System.currentTimeMillis() + + String.format("%06d", ThreadLocalRandom.current().nextInt(1000000)); + } + + /** + * 生成记录编号. + */ + private String generateRecordNo() { + return "LT" + System.currentTimeMillis() + + String.format("%06d", ThreadLocalRandom.current().nextInt(1000000)); + } + +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java index 40cce94e..003238b9 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java @@ -5,15 +5,18 @@ import com.red.circle.framework.dto.PageResult; import com.red.circle.other.app.command.activity.LotteryActivityDetailQryExe; import com.red.circle.other.app.command.activity.LotteryActivityListQryExe; import com.red.circle.other.app.command.activity.LotteryDrawExe; +import com.red.circle.other.app.command.activity.LotteryMultiDrawExe; import com.red.circle.other.app.command.activity.LotteryRecordQryExe; import com.red.circle.other.app.command.activity.LotteryTicketCountQryExe; import com.red.circle.other.app.command.activity.LotteryTicketQryExe; import com.red.circle.other.app.dto.clientobject.activity.LotteryActivityCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryActivityDetailCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryDrawResultCO; +import com.red.circle.other.app.dto.clientobject.activity.LotteryMultiDrawResultCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryRecordCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryTicketCO; 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.LotteryRecordQryCmd; import java.util.List; import lombok.RequiredArgsConstructor; @@ -32,6 +35,7 @@ public class LotteryActivityRestServiceImpl implements LotteryActivityRestServic private final LotteryActivityListQryExe lotteryActivityListQryExe; private final LotteryActivityDetailQryExe lotteryActivityDetailQryExe; private final LotteryDrawExe lotteryDrawExe; + private final LotteryMultiDrawExe lotteryMultiDrawExe; private final LotteryRecordQryExe lotteryRecordQryExe; private final LotteryTicketQryExe lotteryTicketQryExe; private final LotteryTicketCountQryExe lotteryTicketCountQryExe; @@ -51,6 +55,11 @@ public class LotteryActivityRestServiceImpl implements LotteryActivityRestServic return lotteryDrawExe.execute(cmd); } + @Override + public LotteryMultiDrawResultCO multiDraw(LotteryMultiDrawCmd cmd) { + return lotteryMultiDrawExe.execute(cmd); + } + @Override public PageResult listMyRecords(LotteryRecordQryCmd cmd) { return lotteryRecordQryExe.execute(cmd); diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/activity/LotteryMultiDrawResultCO.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/activity/LotteryMultiDrawResultCO.java new file mode 100644 index 00000000..45e4eee1 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/activity/LotteryMultiDrawResultCO.java @@ -0,0 +1,65 @@ +package com.red.circle.other.app.dto.clientobject.activity; + +import com.red.circle.framework.dto.ClientObject; +import java.math.BigDecimal; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 连抽结果CO. + * + * @author system + * @since 2025-10-20 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class LotteryMultiDrawResultCO extends ClientObject { + + /** + * 批次号. + */ + private String batchNo; + + /** + * 抽奖类型:1-单抽,2-十连抽. + */ + private Integer drawType; + + /** + * 抽奖结果列表. + */ + private List results; + + /** + * 汇总信息. + */ + private MultiDrawSummary summary; + + @Data + @Accessors(chain = true) + public static class MultiDrawSummary { + /** + * 本次总获得金额. + */ + private BigDecimal totalAmount; + + /** + * 是否触发保底. + */ + private Boolean guaranteeTriggered; + + /** + * 大奖数量(≥0.5美元). + */ + private Integer bigPrizeCount; + + /** + * 中奖次数. + */ + private Integer winCount; + } + +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryMultiDrawCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryMultiDrawCmd.java new file mode 100644 index 00000000..1c7ddb2f --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryMultiDrawCmd.java @@ -0,0 +1,38 @@ +package com.red.circle.other.app.dto.cmd.activity; + +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 执行连抽Cmd. + * + * @author system + * @since 2025-10-20 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class LotteryMultiDrawCmd extends AppExtCommand { + + /** + * 活动ID. + * + * @eo.required + */ + @NotNull(message = "activityId required.") + private Long activityId; + + /** + * 连抽次数. + * + * @eo.required + */ + @NotNull(message = "drawCount required.") + @Min(value = 1, message = "drawCount must >= 1") + @Max(value = 10, message = "drawCount must <= 10") + private Integer drawCount; + +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java index c4be0c39..6d2d352f 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java @@ -5,9 +5,11 @@ import com.red.circle.framework.dto.PageResult; import com.red.circle.other.app.dto.clientobject.activity.LotteryActivityCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryActivityDetailCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryDrawResultCO; +import com.red.circle.other.app.dto.clientobject.activity.LotteryMultiDrawResultCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryRecordCO; import com.red.circle.other.app.dto.clientobject.activity.LotteryTicketCO; 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.LotteryRecordQryCmd; import java.util.List; @@ -34,6 +36,11 @@ public interface LotteryActivityRestService { */ LotteryDrawResultCO draw(LotteryDrawCmd cmd); + /** + * 执行连抽. + */ + LotteryMultiDrawResultCO multiDraw(LotteryMultiDrawCmd cmd); + /** * 查询我的中奖记录. */ 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 f532eefa..0f38bec3 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 @@ -3,12 +3,11 @@ 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.io.Serializable; +import java.math.BigDecimal; import java.sql.Timestamp; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** @@ -115,4 +114,34 @@ public class LotteryActivity implements Serializable { private Timestamp updateTime; + /** + * 是否启用连抽:0-否,1-是. + */ + @TableField("enable_multi_draw") + private Integer enableMultiDraw; + + /** + * 连抽次数. + */ + @TableField("multi_draw_count") + private Integer multiDrawCount; + + /** + * 连抽消耗券数(优惠). + */ + @TableField("multi_draw_ticket_cost") + private Integer multiDrawTicketCost; + + /** + * 连抽保底奖品ID. + */ + @TableField("multi_draw_guarantee_prize_id") + private Long multiDrawGuaranteePrizeId; + + /** + * 连抽保底最低金额. + */ + @TableField("multi_draw_guarantee_min_amount") + private BigDecimal multiDrawGuaranteeMinAmount; + } 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 522f0947..99486dea 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 @@ -9,7 +9,6 @@ import java.math.BigDecimal; import java.sql.Timestamp; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** @@ -100,6 +99,18 @@ public class LotteryPrize implements Serializable { @TableField("probability") private BigDecimal probability; + /** + * 连抽时的概率(可以比单抽高). + */ + @TableField("multi_draw_probability") + private BigDecimal multiDrawProbability; + + /** + * 是否可作为保底奖品:0-否,1-是. + */ + @TableField("is_guarantee_prize") + private Integer isGuaranteePrize; + /** * 排序(九宫格位置). */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryRecord.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryRecord.java index 578d5475..1ae95e75 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryRecord.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/activity/LotteryRecord.java @@ -91,6 +91,18 @@ public class LotteryRecord implements Serializable { @TableField("deliver_time") private Timestamp deliverTime; + /** + * 抽奖批次号(连抽时相同). + */ + @TableField("draw_batch_no") + private String drawBatchNo; + + /** + * 抽奖类型:1-单抽,2-十连抽. + */ + @TableField("draw_type") + private Integer drawType; + /** * 备注. */