抽奖新增连抽功能
This commit is contained in:
parent
8976ce27df
commit
5b42ea8840
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的中奖记录.
|
||||
*
|
||||
|
||||
@ -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<LotteryDrawResultCO> 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<LotteryTicket> 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<LotteryTicketRecord> 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<LotteryPrize> 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<LotteryDrawResultCO> 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<LotteryDrawResultCO> 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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<LotteryRecordCO> listMyRecords(LotteryRecordQryCmd cmd) {
|
||||
return lotteryRecordQryExe.execute(cmd);
|
||||
|
||||
@ -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<LotteryDrawResultCO> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 查询我的中奖记录.
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 排序(九宫格位置).
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 备注.
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user