抽奖相关接口初步调通

This commit is contained in:
tianfeng 2025-10-20 10:10:33 +08:00
parent 0ee3f41c1b
commit 8976ce27df
68 changed files with 476 additions and 313 deletions

View File

@ -5,6 +5,7 @@ import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.inner.model.cmd.activity.LotteryActivityQryCmd;
import com.red.circle.other.inner.model.dto.activity.LotteryActivityDTO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
@ -20,7 +21,7 @@ public interface LotteryActivityQueryClientApi {
/**
* 分页查询活动列表.
*/
@GetMapping("/listActivities")
@PostMapping("/listActivities")
ResultResponse<PageResult<LotteryActivityDTO>> listActivities(@RequestBody LotteryActivityQryCmd cmd);
}

View File

@ -26,7 +26,7 @@ public interface LotteryRecordClientApi {
@RequestParam("activityId") Long activityId,
@RequestParam(value = "prizeId", required = false) Long prizeId,
@RequestParam(value = "prizeName", required = false) String prizeName,
@RequestParam(value = "prizeType", required = false) Integer prizeType,
@RequestParam(value = "prizeType", required = false) String prizeType,
@RequestParam("isWin") Integer isWin
);

View File

@ -22,7 +22,7 @@ public interface LotteryRecordManageClientApi {
/**
* 分页查询中奖记录.
*/
@GetMapping("/listRecords")
@PostMapping("/listRecords")
ResultResponse<PageResult<LotteryRecordDTO>> listRecords(@RequestBody LotteryRecordQryCmd cmd);
/**

View File

@ -1,13 +1,12 @@
package com.red.circle.other.inner.model.cmd.activity;
import com.red.circle.framework.dto.Command;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.sql.Timestamp;
/**
* 创建/更新活动命令.
*
@ -16,7 +15,7 @@ import lombok.EqualsAndHashCode;
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class LotteryActivitySaveCmd extends Command {
public class LotteryActivitySaveCmd {
/**
* 活动ID更新时需要.
@ -44,13 +43,13 @@ public class LotteryActivitySaveCmd extends Command {
* 开始时间.
*/
@NotNull(message = "开始时间不能为空")
private LocalDateTime startTime;
private Timestamp startTime;
/**
* 结束时间.
*/
@NotNull(message = "结束时间不能为空")
private LocalDateTime endTime;
private Timestamp endTime;
/**
* 抽奖次数限制.

View File

@ -15,7 +15,7 @@ import lombok.EqualsAndHashCode;
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class LotteryPrizeSaveCmd extends Command {
public class LotteryPrizeSaveCmd {
/**
* 奖品ID更新时需要.
@ -43,8 +43,8 @@ public class LotteryPrizeSaveCmd extends Command {
/**
* 奖品类型.
*/
@NotNull(message = "奖品类型不能为空")
private Integer prizeType;
@NotBlank(message = "奖品类型不能为空")
private String prizeType;
/**
* 奖品价值.

View File

@ -53,11 +53,11 @@ public class LotteryRecordQryCmd extends Command {
/**
* 页码.
*/
private Integer pageNo;
private Integer pageNo = 1;
/**
* 页大小.
*/
private Integer pageSize;
private Integer pageSize = 20;
}

View File

@ -2,7 +2,8 @@ package com.red.circle.other.inner.model.cmd.activity;
import com.red.circle.framework.dto.Command;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -41,7 +42,7 @@ public class LotteryTicketGrantCmd extends Command {
/**
* 过期时间.
*/
private LocalDateTime expireTime;
private Timestamp expireTime;
/**
* 备注.

View File

@ -3,7 +3,7 @@ package com.red.circle.other.inner.model.dto.activity;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.sql.Timestamp;
import lombok.Data;
/**
@ -41,12 +41,12 @@ public class LotteryActivityDTO implements Serializable {
/**
* 开始时间.
*/
private LocalDateTime startTime;
private Timestamp startTime;
/**
* 结束时间.
*/
private LocalDateTime endTime;
private Timestamp endTime;
/**
* 状态.
@ -91,11 +91,11 @@ public class LotteryActivityDTO implements Serializable {
/**
* 创建时间.
*/
private LocalDateTime createdAt;
private Timestamp createdAt;
/**
* 更新时间.
*/
private LocalDateTime updatedAt;
private Timestamp updatedAt;
}

View File

@ -41,7 +41,7 @@ public class LotteryPrizeDTO implements Serializable {
/**
* 奖品类型.
*/
private Integer prizeType;
private String prizeType;
/**
* 奖品价值.

View File

@ -48,7 +48,7 @@ public class LotteryActivityBackRestController extends BaseController {
* 更新活动.
*/
@PutMapping("/update")
public void updateActivity(@RequestBody @Validated LotteryActivitySaveCmd cmd) {
public void updateActivity(@RequestBody LotteryActivitySaveCmd cmd) {
lotteryActivityBackService.updateActivity(cmd);
}

View File

@ -1,6 +1,7 @@
package com.red.circle.console.app.service.app.activity;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.inner.endpoint.activity.LotteryActivityManageClient;
import com.red.circle.other.inner.endpoint.activity.LotteryActivityQueryClient;
import com.red.circle.other.inner.endpoint.activity.LotteryPrizeManageClient;
@ -46,7 +47,8 @@ public class LotteryActivityBackServiceImpl implements LotteryActivityBackServic
@Override
public PageResult<LotteryActivityDTO> listActivities(LotteryActivityQryCmd cmd) {
return lotteryActivityQueryClient.listActivities(cmd).getBody();
ResultResponse<PageResult<LotteryActivityDTO>> response = lotteryActivityQueryClient.listActivities(cmd);
return response.getBody();
}
@Override

View File

@ -10,7 +10,8 @@ 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.LotteryRecordQryCmd;
import com.red.circle.other.app.service.activity.LotteryActivityService;
import com.red.circle.other.app.service.activity.LotteryActivityRestService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -40,7 +41,7 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
public class LotteryActivityRestController extends BaseController {
private final LotteryActivityService lotteryActivityService;
private final LotteryActivityRestService lotteryActivityRestService;
/**
* 获取有效活动列表.
@ -52,7 +53,7 @@ public class LotteryActivityRestController extends BaseController {
*/
@GetMapping("/list")
public List<LotteryActivityCO> listValidActivities(AppExtCommand cmd) {
return lotteryActivityService.listValidActivities(cmd);
return lotteryActivityRestService.listValidActivities(cmd);
}
/**
@ -65,7 +66,7 @@ public class LotteryActivityRestController extends BaseController {
*/
@GetMapping("/detail/{activityCode}")
public LotteryActivityDetailCO getActivityDetail(@PathVariable String activityCode, AppExtCommand cmd) {
return lotteryActivityService.getActivityDetail(activityCode, cmd);
return lotteryActivityRestService.getActivityDetail(activityCode, cmd);
}
/**
@ -78,7 +79,7 @@ public class LotteryActivityRestController extends BaseController {
*/
@PostMapping("/draw")
public LotteryDrawResultCO draw(@RequestBody @Validated LotteryDrawCmd cmd) {
return lotteryActivityService.draw(cmd);
return lotteryActivityRestService.draw(cmd);
}
/**
@ -91,33 +92,33 @@ public class LotteryActivityRestController extends BaseController {
*/
@GetMapping("/my-records")
public PageResult<LotteryRecordCO> listMyRecords(LotteryRecordQryCmd cmd) {
return lotteryActivityService.listMyRecords(cmd);
return lotteryActivityRestService.listMyRecords(cmd);
}
/**
* 查询我的抽奖券.
* 查询我的抽奖券列表详细信息.
*
* @eo.name 查询我的抽奖券.
* @eo.name 查询我的抽奖券列表.
* @eo.url /my-tickets
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/my-tickets")
public List<LotteryTicketCO> listMyTickets(AppExtCommand cmd) {
return lotteryActivityService.listMyTickets(cmd);
return lotteryActivityRestService.listMyTickets(cmd);
}
/**
* 获取抽奖券.
* 获取我的剩余抽奖券数.
*
* @eo.name 获取抽奖券.
* @eo.url /ticket-count
* @eo.name 获取我的剩余抽奖券数.
* @eo.url /my-ticket-count
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/ticket-count")
@GetMapping("/my-ticket-count")
public Integer getMyTicketCount(AppExtCommand cmd) {
return lotteryActivityService.getMyTicketCount(cmd);
return lotteryActivityRestService.getMyTicketCount(cmd);
}
}

View File

@ -13,6 +13,7 @@ import com.red.circle.other.infra.database.rds.service.activity.LotteryActivityS
import com.red.circle.other.infra.database.rds.service.activity.LotteryPrizeService;
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 java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@ -39,22 +40,22 @@ public class LotteryActivityDetailQryExe {
Long userId = cmd.requiredReqUserId();
// 查询活动
LotteryActivity activity = lotteryActivityService.lambdaQuery()
LotteryActivity activity = lotteryActivityService.query()
.eq(LotteryActivity::getActivityCode, activityCode)
.one();
ResponseAssert.isTrue(activity != null, "活动不存在");
.getOne();
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_NOT_FOUND, activity != null);
// 查询奖品列表
List<LotteryPrize> prizes = lotteryPrizeService.lambdaQuery()
List<LotteryPrize> prizes = lotteryPrizeService.query()
.eq(LotteryPrize::getActivityId, activity.getId())
.eq(LotteryPrize::getIsDisplay, 1)
.orderByAsc(LotteryPrize::getSortOrder)
.list();
// 查询用户抽奖券数量
Integer ticketCount = 0;
int ticketCount = 0;
if (activity.getNeedTicket() == 1) {
ticketCount = lotteryTicketService.lambdaQuery()
ticketCount = lotteryTicketService.query()
.eq(LotteryTicket::getUserId, userId)
.eq(LotteryTicket::getStatus, 1)
.gt(LotteryTicket::getRemainingCount, 0)
@ -67,11 +68,11 @@ public class LotteryActivityDetailQryExe {
// 查询用户今日抽奖次数
Integer usedCount = 0;
if (activity.getDrawCountLimit() != null && activity.getDrawCountLimit() > 0) {
LotteryUserCount userCount = lotteryUserCountService.lambdaQuery()
LotteryUserCount userCount = lotteryUserCountService.query()
.eq(LotteryUserCount::getUserId, userId)
.eq(LotteryUserCount::getActivityId, activity.getId())
.eq(LotteryUserCount::getDrawDate, LocalDate.now())
.one();
.getOne();
if (userCount != null) {
usedCount = userCount.getDrawCount();
}

View File

@ -6,11 +6,15 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryActivity;
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.LotteryUserCountService;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.red.circle.tool.core.date.TimestampUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@ -29,10 +33,10 @@ public class LotteryActivityListQryExe {
public List<LotteryActivityCO> execute(AppExtCommand cmd) {
Long userId = cmd.requiredReqUserId();
LocalDateTime now = LocalDateTime.now();
Timestamp now = TimestampUtils.now();
// 查询进行中的活动
List<LotteryActivity> activities = lotteryActivityService.lambdaQuery()
List<LotteryActivity> activities = lotteryActivityService.query()
.eq(LotteryActivity::getStatus, 1)
.le(LotteryActivity::getStartTime, now)
.ge(LotteryActivity::getEndTime, now)
@ -48,7 +52,7 @@ public class LotteryActivityListQryExe {
.map(LotteryActivity::getId)
.collect(Collectors.toList());
Map<Long, Integer> userDrawCountMap = lotteryUserCountService.lambdaQuery()
Map<Long, Integer> userDrawCountMap = lotteryUserCountService.query()
.eq(LotteryUserCount::getUserId, userId)
.in(LotteryUserCount::getActivityId, activityIds)
.eq(LotteryUserCount::getDrawDate, LocalDate.now())

View File

@ -16,12 +16,16 @@ import com.red.circle.other.infra.database.rds.service.activity.LotteryRecordSer
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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -45,95 +49,99 @@ public class LotteryDrawExe {
private final LotteryTicketRecordService lotteryTicketRecordService;
private final LotteryUserCountService lotteryUserCountService;
private final Random random = new Random();
@Transactional(rollbackFor = Exception.class)
public LotteryDrawResultCO execute(LotteryDrawCmd cmd) {
Long userId = cmd.requiredReqUserId();
String activityCode = cmd.getActivityCode();
Long activityId = cmd.getActivityId();
Timestamp now = TimestampUtils.now();
// 1. 校验活动
LotteryActivity activity = lotteryActivityService.lambdaQuery()
.eq(LotteryActivity::getActivityCode, activityCode)
.one();
ResponseAssert.isTrue(activity != null, "活动不存在");
ResponseAssert.isTrue(activity.getStatus() == 1, "活动未开始或已结束");
LotteryActivity activity = validateActivity(activityId, now);
LocalDateTime now = LocalDateTime.now();
ResponseAssert.isTrue(now.isAfter(activity.getStartTime()) && now.isBefore(activity.getEndTime()),
"不在活动时间内");
// 2. 校验抽奖次数限制
validateDrawCount(userId, activityId, activity);
// 2. 校验抽奖次数
if (activity.getDrawCountLimit() != null && activity.getDrawCountLimit() > 0) {
LotteryUserCount userCount = lotteryUserCountService.lambdaQuery()
.eq(LotteryUserCount::getUserId, userId)
.eq(LotteryUserCount::getActivityId, activity.getId())
.eq(LotteryUserCount::getDrawDate, LocalDate.now())
.one();
int drawCount = userCount != null ? userCount.getDrawCount() : 0;
ResponseAssert.isTrue(drawCount < activity.getDrawCountLimit(), "今日抽奖次数已用完");
}
// 3. 校验并扣除抽奖券
if (activity.getNeedTicket() == 1) {
deductTicket(userId, activity.getTicketCost());
}
// 3. 校验并扣除抽奖券写死消耗1次
Integer needTicket = 1;
deductTicket(userId, needTicket);
// 4. 执行抽奖算法
LotteryPrize prize = drawPrize(activity.getId());
// 5. 保存抽奖记录
String recordNo = generateRecordNo();
LotteryRecord record = new LotteryRecord();
record.setRecordNo(recordNo);
record.setUserId(userId);
record.setActivityId(activity.getId());
record.setDrawTime(now);
LotteryPrize prize = drawPrize(activityId);
// 5. 扣减奖品库存使用悲观锁防止超卖
if (prize != null) {
record.setPrizeId(prize.getId());
record.setPrizeName(prize.getPrizeName());
record.setPrizeType(prize.getPrizeType());
record.setIsWin(1);
record.setPrizeStatus(0); // 待发放
// 扣减库存
lotteryPrizeService.lambdaUpdate()
.eq(LotteryPrize::getId, prize.getId())
.gt(LotteryPrize::getRemainingStock, 0)
.setSql("remaining_stock = remaining_stock - 1")
.update();
} else {
record.setIsWin(0);
boolean deductSuccess = deductPrizeStock(prize.getId());
if (!deductSuccess) {
// 库存扣减失败视为未中奖
log.warn("Prize stock deduction failed, prizeId: {}", prize.getId());
prize = null;
}
}
lotteryRecordService.save(record);
// 6. 保存抽奖记录
LotteryRecord record = saveDrawRecord(userId, activityId, prize, now);
// 6. 更新用户抽奖次数
updateUserDrawCount(userId, activity.getId());
// 7. 更新用户抽奖次数
updateUserDrawCount(userId, activityId);
// 7. 组装返回结果
// 8. 组装返回结果
return convertToResultCO(record, prize);
}
/**
* 扣除抽奖券.
* 校验活动状态和时间.
*/
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) {
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 drawCount = userCount != null ? userCount.getDrawCount() : 0;
ResponseAssert.isTrue(LotteryErrorCode.DRAW_COUNT_EXHAUSTED,
drawCount < activity.getDrawCountLimit());
}
/**
* 扣除抽奖券优化使用批量处理减少数据库交互.
*/
private void deductTicket(Long userId, Integer ticketCost) {
List<LotteryTicket> tickets = lotteryTicketService.lambdaQuery()
// 查询可用抽奖券按过期时间排序先用快过期的
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(totalRemaining >= ticketCost, "抽奖券不足");
ResponseAssert.isTrue(LotteryErrorCode.INSUFFICIENT_TICKETS, totalRemaining >= ticketCost);
// 批量扣减
List<LotteryTicketRecord> records = new ArrayList<>();
int remaining = ticketCost;
for (LotteryTicket ticket : tickets) {
if (remaining <= 0) {
break;
@ -141,33 +149,43 @@ public class LotteryDrawExe {
int deduct = Math.min(remaining, ticket.getRemainingCount());
lotteryTicketService.lambdaUpdate()
// 使用乐观锁更新防止并发问题
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)
.update();
.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("抽奖消耗");
lotteryTicketRecordService.save(ticketRecord);
records.add(ticketRecord);
remaining -= deduct;
}
// 批量插入日志
if (!records.isEmpty()) {
lotteryTicketRecordService.saveBatch(records);
}
}
/**
* 抽奖算法 - 权重随机.
* 抽奖算法 - 权重随机优化使用ThreadLocalRandom.
*/
private LotteryPrize drawPrize(Long activityId) {
// 查询所有可用奖品
List<LotteryPrize> prizes = lotteryPrizeService.lambdaQuery()
List<LotteryPrize> prizes = lotteryPrizeService.query()
.eq(LotteryPrize::getActivityId, activityId)
.gt(LotteryPrize::getRemainingStock, 0)
.orderByAsc(LotteryPrize::getSortOrder)
.list();
if (prizes.isEmpty()) {
@ -179,8 +197,7 @@ public class LotteryDrawExe {
.map(LotteryPrize::getProbability)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 生成随机数
double randomValue = random.nextDouble() * totalProbability.doubleValue();
double randomValue = ThreadLocalRandom.current().nextDouble() * totalProbability.doubleValue();
double cumulative = 0.0;
// 根据概率选择奖品
@ -195,35 +212,86 @@ public class LotteryDrawExe {
}
/**
* 更新用户抽奖次数.
* 扣减奖品库存使用悲观锁防止超卖.
*/
private boolean deductPrizeStock(Long prizeId) {
// 使用数据库级别的原子操作
int affected = lotteryPrizeService.update()
.eq(LotteryPrize::getId, prizeId)
.gt(LotteryPrize::getRemainingStock, 0) // 必须有库存
.setSql("remaining_stock = remaining_stock - 1")
.execute()
? 1 : 0;
return affected > 0;
}
/**
* 保存抽奖记录.
*/
private LotteryRecord saveDrawRecord(Long userId, Long activityId, LotteryPrize prize, Timestamp now) {
String recordNo = generateRecordNo();
LotteryRecord record = new LotteryRecord();
record.setRecordNo(recordNo);
record.setUserId(userId);
record.setActivityId(activityId);
record.setDrawTime(now);
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) {
LotteryUserCount userCount = lotteryUserCountService.lambdaQuery()
LocalDate today = LocalDate.now();
// 先尝试直接更新
boolean updated = lotteryUserCountService.update()
.eq(LotteryUserCount::getUserId, userId)
.eq(LotteryUserCount::getActivityId, activityId)
.eq(LotteryUserCount::getDrawDate, LocalDate.now())
.one();
.eq(LotteryUserCount::getDrawDate, today)
.setSql("draw_count = draw_count + 1")
.execute();
if (userCount == null) {
userCount = new LotteryUserCount();
userCount.setUserId(userId);
userCount.setActivityId(activityId);
userCount.setDrawDate(LocalDate.now());
userCount.setDrawCount(1);
lotteryUserCountService.save(userCount);
} else {
lotteryUserCountService.lambdaUpdate()
.eq(LotteryUserCount::getId, userCount.getId())
.setSql("draw_count = draw_count + 1")
.update();
// 如果更新失败记录不存在则插入
if (!updated) {
try {
LotteryUserCount userCount = new LotteryUserCount();
userCount.setUserId(userId);
userCount.setActivityId(activityId);
userCount.setDrawDate(today);
userCount.setDrawCount(1);
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 + 1")
.execute();
}
}
}
/**
* 生成记录编号.
* 生成记录编号优化使用更高效的方式.
*/
private String generateRecordNo() {
return "LT" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8);
return "LT" + System.currentTimeMillis() +
String.format("%06d", ThreadLocalRandom.current().nextInt(1000000));
}
/**

View File

@ -1,5 +1,6 @@
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.framework.dto.PageResult;
import com.red.circle.other.app.dto.clientobject.activity.LotteryPrizeCO;
@ -37,14 +38,14 @@ public class LotteryRecordQryExe {
Integer pageSize = cmd.getPageSize() != null ? cmd.getPageSize() : 20;
// 构建查询条件
Page<LotteryRecord> page = new Page<>(pageNo, pageSize);
var query = lotteryRecordService.lambdaQuery()
IPage<LotteryRecord> page = new Page<>(pageNo, pageSize);
var query = lotteryRecordService.query()
.eq(LotteryRecord::getUserId, userId);
if (cmd.getActivityCode() != null) {
LotteryActivity activity = lotteryActivityService.lambdaQuery()
LotteryActivity activity = lotteryActivityService.query()
.eq(LotteryActivity::getActivityCode, cmd.getActivityCode())
.one();
.getOne();
if (activity != null) {
query.eq(LotteryRecord::getActivityId, activity.getId());
}
@ -54,7 +55,7 @@ public class LotteryRecordQryExe {
query.eq(LotteryRecord::getIsWin, 1);
}
Page<LotteryRecord> recordPage = query.orderByDesc(LotteryRecord::getDrawTime).page(page);
IPage<LotteryRecord> recordPage = query.orderByDesc(LotteryRecord::getDrawTime).page(page);
// 查询关联数据
List<Long> activityIds = recordPage.getRecords().stream()
@ -68,13 +69,13 @@ public class LotteryRecordQryExe {
.distinct()
.collect(Collectors.toList());
Map<Long, LotteryActivity> activityMap = lotteryActivityService.lambdaQuery()
Map<Long, LotteryActivity> activityMap = lotteryActivityService.query()
.in(LotteryActivity::getId, activityIds)
.list()
.stream()
.collect(Collectors.toMap(LotteryActivity::getId, a -> a));
Map<Long, LotteryPrize> prizeMap = lotteryPrizeService.lambdaQuery()
Map<Long, LotteryPrize> prizeMap = lotteryPrizeService.query()
.in(LotteryPrize::getId, prizeIds)
.list()
.stream()
@ -85,7 +86,13 @@ public class LotteryRecordQryExe {
.map(record -> convertToCO(record, activityMap, prizeMap))
.collect(Collectors.toList());
return PageResult.of(records, recordPage.getTotal(), pageNo, pageSize);
PageResult<LotteryRecordCO> result = new PageResult<>();
result.setRecords(records);
result.setTotal(recordPage.getTotal());
result.setCurrent(recordPage.getCurrent());
result.setSize(recordPage.getSize());
return result;
}
private LotteryRecordCO convertToCO(LotteryRecord record,

View File

@ -22,7 +22,7 @@ public class LotteryTicketCountQryExe {
public Integer execute(AppExtCommand cmd) {
Long userId = cmd.requiredReqUserId();
List<LotteryTicket> tickets = lotteryTicketService.lambdaQuery()
List<LotteryTicket> tickets = lotteryTicketService.query()
.eq(LotteryTicket::getUserId, userId)
.eq(LotteryTicket::getStatus, 1)
.gt(LotteryTicket::getRemainingCount, 0)

View File

@ -7,10 +7,11 @@ import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketSer
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
/**
* 查询我的抽奖券.
* 查询我的抽奖券列表.
*
* @author system
* @since 2025-10-18
@ -24,7 +25,7 @@ public class LotteryTicketQryExe {
public List<LotteryTicketCO> execute(AppExtCommand cmd) {
Long userId = cmd.requiredReqUserId();
List<LotteryTicket> tickets = lotteryTicketService.lambdaQuery()
List<LotteryTicket> tickets = lotteryTicketService.query()
.eq(LotteryTicket::getUserId, userId)
.eq(LotteryTicket::getStatus, 1)
.gt(LotteryTicket::getRemainingCount, 0)
@ -38,13 +39,7 @@ public class LotteryTicketQryExe {
private LotteryTicketCO convertToCO(LotteryTicket ticket) {
LotteryTicketCO co = new LotteryTicketCO();
co.setId(ticket.getId());
co.setTicketSource(ticket.getTicketSource());
co.setTotalCount(ticket.getTotalCount());
co.setUsedCount(ticket.getUsedCount());
co.setRemainingCount(ticket.getRemainingCount());
co.setExpireTime(ticket.getExpireTime());
co.setStatus(ticket.getStatus());
BeanUtils.copyProperties(ticket, co);
return co;
}

View File

@ -27,7 +27,7 @@ import org.springframework.stereotype.Service;
*/
@Service
@RequiredArgsConstructor
public class LotteryActivityServiceImpl implements LotteryActivityService {
public class LotteryActivityRestServiceImpl implements LotteryActivityRestService {
private final LotteryActivityListQryExe lotteryActivityListQryExe;
private final LotteryActivityDetailQryExe lotteryActivityDetailQryExe;

View File

@ -1,11 +1,7 @@
package com.red.circle.other.app.service.activity;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.other.app.command.activity.LotteryDrawExe;
import com.red.circle.other.app.command.activity.query.LotteryActivityDetailQryExe;
import com.red.circle.other.app.command.activity.query.LotteryActivityListQryExe;
import com.red.circle.other.app.command.activity.query.LotteryRecordQryExe;
import com.red.circle.other.app.command.activity.query.LotteryTicketQryExe;
import com.red.circle.other.app.command.activity.*;
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;
@ -48,11 +44,6 @@ public class LotteryServiceImpl implements LotteryService {
return lotteryDrawExe.execute(cmd);
}
@Override
public List<LotteryRecordCO> listMyRecords(LotteryRecordQryCmd cmd) {
return lotteryRecordQryExe.execute(cmd);
}
@Override
public List<LotteryTicketCO> listMyTickets(AppExtCommand cmd) {
return lotteryTicketQryExe.execute(cmd);

View File

@ -1,7 +1,7 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.red.circle.framework.dto.ClientObject;
import java.time.LocalDateTime;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -40,12 +40,12 @@ public class LotteryActivityCO extends ClientObject {
/**
* 开始时间.
*/
private LocalDateTime startTime;
private Timestamp startTime;
/**
* 结束时间.
*/
private LocalDateTime endTime;
private Timestamp endTime;
/**
* 状态0-未开始1-进行中2-已结束3-已关闭.

View File

@ -1,7 +1,7 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.red.circle.framework.dto.ClientObject;
import java.time.LocalDateTime;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -35,6 +35,6 @@ public class LotteryDrawResultCO extends ClientObject {
/**
* 抽奖时间.
*/
private LocalDateTime drawTime;
private Timestamp drawTime;
}

View File

@ -35,7 +35,7 @@ public class LotteryPrizeCO extends ClientObject {
/**
* 奖品类型.
*/
private Integer prizeType;
private String prizeType;
/**
* 奖品价值.

View File

@ -1,7 +1,7 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.red.circle.framework.dto.ClientObject;
import java.time.LocalDateTime;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -40,7 +40,7 @@ public class LotteryRecordCO extends ClientObject {
/**
* 抽奖时间.
*/
private LocalDateTime drawTime;
private Timestamp drawTime;
/**
* 奖品状态0-待发放1-已发放2-发放失败.

View File

@ -1,7 +1,7 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.red.circle.framework.dto.ClientObject;
import java.time.LocalDateTime;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -45,7 +45,7 @@ public class LotteryTicketCO extends ClientObject {
/**
* 过期时间.
*/
private LocalDateTime expireTime;
private Timestamp expireTime;
/**
* 状态.

View File

@ -1,7 +1,7 @@
package com.red.circle.other.app.dto.cmd.activity;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -16,11 +16,11 @@ import lombok.EqualsAndHashCode;
public class LotteryDrawCmd extends AppExtCommand {
/**
* 活动编码.
* 活动ID.
*
* @eo.required
*/
@NotBlank(message = "activityCode required.")
private String activityCode;
@NotNull(message = "activityId required.")
private Long activityId;
}

View File

@ -17,7 +17,7 @@ import java.util.List;
* @author system
* @since 2025-10-18
*/
public interface LotteryActivityService {
public interface LotteryActivityRestService {
/**
* 获取有效活动列表.

View File

@ -33,11 +33,6 @@ public interface LotteryService {
*/
LotteryDrawResultCO draw(LotteryDrawCmd cmd);
/**
* 查询用户抽奖记录.
*/
List<LotteryRecordCO> listMyRecords(LotteryRecordQryCmd cmd);
/**
* 查询用户抽奖券.
*/

View File

@ -1,8 +1,7 @@
package com.red.circle.other.infra.database.rds.dao.activity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryActivity;
import org.apache.ibatis.annotations.Mapper;
/**
* 抽奖活动Mapper.
@ -10,7 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @author system
* @since 2025-10-18
*/
@Mapper
public interface LotteryActivityMapper extends BaseMapper<LotteryActivity> {
public interface LotteryActivityMapper extends BaseDAO<LotteryActivity> {
}

View File

@ -1,8 +1,7 @@
package com.red.circle.other.infra.database.rds.dao.activity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryPrize;
import org.apache.ibatis.annotations.Mapper;
/**
* 奖品配置Mapper.
@ -10,7 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @author system
* @since 2025-10-18
*/
@Mapper
public interface LotteryPrizeMapper extends BaseMapper<LotteryPrize> {
public interface LotteryPrizeMapper extends BaseDAO<LotteryPrize> {
}

View File

@ -1,8 +1,7 @@
package com.red.circle.other.infra.database.rds.dao.activity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryRecord;
import org.apache.ibatis.annotations.Mapper;
/**
* 中奖记录Mapper.
@ -10,7 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @author system
* @since 2025-10-18
*/
@Mapper
public interface LotteryRecordMapper extends BaseMapper<LotteryRecord> {
public interface LotteryRecordMapper extends BaseDAO<LotteryRecord> {
}

View File

@ -1,8 +1,7 @@
package com.red.circle.other.infra.database.rds.dao.activity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicket;
import org.apache.ibatis.annotations.Mapper;
/**
* 抽奖券Mapper.
@ -10,7 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @author system
* @since 2025-10-18
*/
@Mapper
public interface LotteryTicketMapper extends BaseMapper<LotteryTicket> {
public interface LotteryTicketMapper extends BaseDAO<LotteryTicket> {
}

View File

@ -1,8 +1,7 @@
package com.red.circle.other.infra.database.rds.dao.activity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicketRecord;
import org.apache.ibatis.annotations.Mapper;
/**
* 抽奖券使用记录Mapper.
@ -10,7 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @author system
* @since 2025-10-18
*/
@Mapper
public interface LotteryTicketRecordMapper extends BaseMapper<LotteryTicketRecord> {
public interface LotteryTicketRecordMapper extends BaseDAO<LotteryTicketRecord> {
}

View File

@ -1,8 +1,7 @@
package com.red.circle.other.infra.database.rds.dao.activity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserCount;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户抽奖次数统计Mapper.
@ -10,7 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @author system
* @since 2025-10-18
*/
@Mapper
public interface LotteryUserCountMapper extends BaseMapper<LotteryUserCount> {
public interface LotteryUserCountMapper extends BaseDAO<LotteryUserCount> {
}

View File

@ -5,7 +5,8 @@ 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.time.LocalDateTime;
import java.io.Serializable;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -19,10 +20,9 @@ import lombok.experimental.Accessors;
* @since 2025-10-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("lottery_activity")
public class LotteryActivity extends TimestampBaseEntity {
public class LotteryActivity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ -55,13 +55,13 @@ public class LotteryActivity extends TimestampBaseEntity {
* 开始时间.
*/
@TableField("start_time")
private LocalDateTime startTime;
private Timestamp startTime;
/**
* 结束时间.
*/
@TableField("end_time")
private LocalDateTime endTime;
private Timestamp endTime;
/**
* 状态0-未开始1-进行中2-已结束3-已关闭.
@ -111,4 +111,8 @@ public class LotteryActivity extends TimestampBaseEntity {
@TableField("sort_order")
private Integer sortOrder;
private Timestamp createTime;
private Timestamp updateTime;
}

View File

@ -3,9 +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;
@ -19,10 +21,9 @@ import lombok.experimental.Accessors;
* @since 2025-10-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("lottery_prize")
public class LotteryPrize extends TimestampBaseEntity {
public class LotteryPrize implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ -55,7 +56,7 @@ public class LotteryPrize extends TimestampBaseEntity {
* 奖品类型1-实物2-虚拟币3-优惠券4-积分5-谢谢参与.
*/
@TableField("prize_type")
private Integer prizeType;
private String prizeType;
/**
* 奖品价值/数量.
@ -111,4 +112,8 @@ public class LotteryPrize extends TimestampBaseEntity {
@TableField("is_display")
private Integer isDisplay;
private Timestamp createTime;
private Timestamp updateTime;
}

View File

@ -3,11 +3,10 @@ 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.time.LocalDateTime;
import java.io.Serializable;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
@ -19,10 +18,9 @@ import lombok.experimental.Accessors;
* @since 2025-10-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("lottery_record")
public class LotteryRecord extends TimestampBaseEntity {
public class LotteryRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ -67,7 +65,7 @@ public class LotteryRecord extends TimestampBaseEntity {
* 奖品类型快照.
*/
@TableField("prize_type")
private Integer prizeType;
private String prizeType;
/**
* 是否中奖0-未中奖1-中奖.
@ -79,7 +77,7 @@ public class LotteryRecord extends TimestampBaseEntity {
* 抽奖时间.
*/
@TableField("draw_time")
private LocalDateTime drawTime;
private Timestamp drawTime;
/**
* 奖品状态0-待发放1-已发放2-发放失败.
@ -91,7 +89,7 @@ public class LotteryRecord extends TimestampBaseEntity {
* 发放时间.
*/
@TableField("deliver_time")
private LocalDateTime deliverTime;
private Timestamp deliverTime;
/**
* 备注.

View File

@ -3,11 +3,10 @@ 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.time.LocalDateTime;
import java.io.Serializable;
import java.sql.Timestamp;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
@ -19,10 +18,9 @@ import lombok.experimental.Accessors;
* @since 2025-10-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("lottery_ticket")
public class LotteryTicket extends TimestampBaseEntity {
public class LotteryTicket implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ -73,7 +71,7 @@ public class LotteryTicket extends TimestampBaseEntity {
* 过期时间.
*/
@TableField("expire_time")
private LocalDateTime expireTime;
private Timestamp expireTime;
/**
* 状态0-已失效1-正常.

View File

@ -3,10 +3,10 @@ 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 lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
@ -18,10 +18,9 @@ import lombok.experimental.Accessors;
* @since 2025-10-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("lottery_ticket_record")
public class LotteryTicketRecord extends TimestampBaseEntity {
public class LotteryTicketRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;

View File

@ -3,9 +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.sql.Timestamp;
import java.time.LocalDate;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -19,10 +21,9 @@ import lombok.experimental.Accessors;
* @since 2025-10-18
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("lottery_user_count")
public class LotteryUserCount extends TimestampBaseEntity {
public class LotteryUserCount implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ -57,4 +58,7 @@ public class LotteryUserCount extends TimestampBaseEntity {
@TableField("draw_count")
private Integer drawCount;
private Timestamp createTime;
private Timestamp updateTime;
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity;
import com.baomidou.mybatisplus.extension.service.IService;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryActivity;
/**
@ -9,6 +9,6 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryActivity;
* @author system
* @since 2025-10-18
*/
public interface LotteryActivityService extends IService<LotteryActivity> {
public interface LotteryActivityService extends BaseService<LotteryActivity> {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity;
import com.baomidou.mybatisplus.extension.service.IService;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryPrize;
/**
@ -9,6 +9,6 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryPrize;
* @author system
* @since 2025-10-18
*/
public interface LotteryPrizeService extends IService<LotteryPrize> {
public interface LotteryPrizeService extends BaseService<LotteryPrize> {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity;
import com.baomidou.mybatisplus.extension.service.IService;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryRecord;
/**
@ -9,6 +9,6 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryRecord;
* @author system
* @since 2025-10-18
*/
public interface LotteryRecordService extends IService<LotteryRecord> {
public interface LotteryRecordService extends BaseService<LotteryRecord> {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity;
import com.baomidou.mybatisplus.extension.service.IService;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicketRecord;
/**
@ -9,6 +9,6 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicketReco
* @author system
* @since 2025-10-18
*/
public interface LotteryTicketRecordService extends IService<LotteryTicketRecord> {
public interface LotteryTicketRecordService extends BaseService<LotteryTicketRecord> {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity;
import com.baomidou.mybatisplus.extension.service.IService;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicket;
/**
@ -9,6 +9,6 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicket;
* @author system
* @since 2025-10-18
*/
public interface LotteryTicketService extends IService<LotteryTicket> {
public interface LotteryTicketService extends BaseService<LotteryTicket> {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity;
import com.baomidou.mybatisplus.extension.service.IService;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserCount;
/**
@ -9,6 +9,6 @@ import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserCount;
* @author system
* @since 2025-10-18
*/
public interface LotteryUserCountService extends IService<LotteryUserCount> {
public interface LotteryUserCountService extends BaseService<LotteryUserCount> {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryActivity;
import com.red.circle.other.infra.database.rds.dao.activity.LotteryActivityMapper;
import com.red.circle.other.infra.database.rds.service.activity.LotteryActivityService;
@ -13,7 +13,7 @@ import org.springframework.stereotype.Service;
* @since 2025-10-18
*/
@Service
public class LotteryActivityServiceImpl extends ServiceImpl<LotteryActivityMapper, LotteryActivity>
public class LotteryActivityServiceImpl extends BaseServiceImpl<LotteryActivityMapper, LotteryActivity>
implements LotteryActivityService {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryPrize;
import com.red.circle.other.infra.database.rds.dao.activity.LotteryPrizeMapper;
import com.red.circle.other.infra.database.rds.service.activity.LotteryPrizeService;
@ -13,7 +13,7 @@ import org.springframework.stereotype.Service;
* @since 2025-10-18
*/
@Service
public class LotteryPrizeServiceImpl extends ServiceImpl<LotteryPrizeMapper, LotteryPrize>
public class LotteryPrizeServiceImpl extends BaseServiceImpl<LotteryPrizeMapper, LotteryPrize>
implements LotteryPrizeService {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryRecord;
import com.red.circle.other.infra.database.rds.dao.activity.LotteryRecordMapper;
import com.red.circle.other.infra.database.rds.service.activity.LotteryRecordService;
@ -13,7 +13,7 @@ import org.springframework.stereotype.Service;
* @since 2025-10-18
*/
@Service
public class LotteryRecordServiceImpl extends ServiceImpl<LotteryRecordMapper, LotteryRecord>
public class LotteryRecordServiceImpl extends BaseServiceImpl<LotteryRecordMapper, LotteryRecord>
implements LotteryRecordService {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicketRecord;
import com.red.circle.other.infra.database.rds.dao.activity.LotteryTicketRecordMapper;
import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketRecordService;
@ -13,7 +13,7 @@ import org.springframework.stereotype.Service;
* @since 2025-10-18
*/
@Service
public class LotteryTicketRecordServiceImpl extends ServiceImpl<LotteryTicketRecordMapper, LotteryTicketRecord>
public class LotteryTicketRecordServiceImpl extends BaseServiceImpl<LotteryTicketRecordMapper, LotteryTicketRecord>
implements LotteryTicketRecordService {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicket;
import com.red.circle.other.infra.database.rds.dao.activity.LotteryTicketMapper;
import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketService;
@ -13,7 +13,7 @@ import org.springframework.stereotype.Service;
* @since 2025-10-18
*/
@Service
public class LotteryTicketServiceImpl extends ServiceImpl<LotteryTicketMapper, LotteryTicket>
public class LotteryTicketServiceImpl extends BaseServiceImpl<LotteryTicketMapper, LotteryTicket>
implements LotteryTicketService {
}

View File

@ -1,6 +1,6 @@
package com.red.circle.other.infra.database.rds.service.activity.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserCount;
import com.red.circle.other.infra.database.rds.dao.activity.LotteryUserCountMapper;
import com.red.circle.other.infra.database.rds.service.activity.LotteryUserCountService;
@ -13,7 +13,7 @@ import org.springframework.stereotype.Service;
* @since 2025-10-18
*/
@Service
public class LotteryUserCountServiceImpl extends ServiceImpl<LotteryUserCountMapper, LotteryUserCount>
public class LotteryUserCountServiceImpl extends BaseServiceImpl<LotteryUserCountMapper, LotteryUserCount>
implements LotteryUserCountService {
}

View File

@ -0,0 +1,7 @@
<?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.LotteryActivityMapper">
</mapper>

View File

@ -0,0 +1,7 @@
<?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.LotteryPrizeMapper">
</mapper>

View File

@ -0,0 +1,7 @@
<?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.LotteryRecordMapper">
</mapper>

View File

@ -0,0 +1,7 @@
<?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.LotteryTicketMapper">
</mapper>

View File

@ -0,0 +1,6 @@
<?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.LotteryTicketRecordMapper">
</mapper>

View File

@ -0,0 +1,6 @@
<?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.LotteryUserCountMapper">
</mapper>

View File

@ -30,9 +30,9 @@ public class LotteryActivityClientEndpoint implements LotteryActivityClientApi {
@Override
public ResultResponse<LotteryActivityDTO> getByActivityCode(String activityCode) {
LotteryActivity activity = lotteryActivityService.lambdaQuery()
LotteryActivity activity = lotteryActivityService.query()
.eq(LotteryActivity::getActivityCode, activityCode)
.one();
.getOne();
if (activity == null) {
return ResultResponse.success(null);
@ -54,7 +54,7 @@ public class LotteryActivityClientEndpoint implements LotteryActivityClientApi {
@Override
public ResultResponse<List<LotteryActivityDTO>> listActivities() {
List<LotteryActivity> activities = lotteryActivityService.lambdaQuery()
List<LotteryActivity> activities = lotteryActivityService.query()
.eq(LotteryActivity::getStatus, 1)
.orderByDesc(LotteryActivity::getCreateTime)
.list();

View File

@ -8,6 +8,10 @@ import com.red.circle.other.inner.asserts.lottery.LotteryErrorCode;
import com.red.circle.other.inner.endpoint.activity.api.LotteryActivityManageClientApi;
import com.red.circle.other.inner.model.cmd.activity.LotteryActivitySaveCmd;
import com.red.circle.other.inner.model.dto.activity.LotteryActivityDTO;
import java.sql.Timestamp;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.http.MediaType;
@ -34,9 +38,9 @@ public class LotteryActivityManageClientEndpoint implements LotteryActivityManag
@Transactional(rollbackFor = Exception.class)
public ResultResponse<Void> createActivity(LotteryActivitySaveCmd cmd) {
// 校验活动编码唯一性
LotteryActivity exists = lotteryActivityService.lambdaQuery()
LotteryActivity exists = lotteryActivityService.query()
.eq(LotteryActivity::getActivityCode, cmd.getActivityCode())
.one();
.getOne();
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_CODE_EXISTS, exists == null);
LotteryActivity activity = new LotteryActivity();
@ -58,15 +62,8 @@ public class LotteryActivityManageClientEndpoint implements LotteryActivityManag
LotteryActivity activity = lotteryActivityService.getById(cmd.getId());
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_NOT_FOUND, activity != null);
// 校验活动编码唯一性
LotteryActivity exists = lotteryActivityService.lambdaQuery()
.eq(LotteryActivity::getActivityCode, cmd.getActivityCode())
.ne(LotteryActivity::getId, cmd.getId())
.one();
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_CODE_EXISTS, exists == null);
BeanUtils.copyProperties(cmd, activity);
lotteryActivityService.updateById(activity);
lotteryActivityService.updateSelectiveById(activity);
return ResultResponse.success();
}
@ -77,8 +74,44 @@ public class LotteryActivityManageClientEndpoint implements LotteryActivityManag
LotteryActivity activity = lotteryActivityService.getById(id);
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_NOT_FOUND, activity != null);
Integer currentStatus = activity.getStatus();
Timestamp now = TimestampUtils.now();
// 状态校验规则0-未开始1-进行中2-已结束3-已关闭
// 1. 未开始(0) -> 可以转换为进行中(1)已关闭(3)
// 2. 进行中(1) -> 可以转换为已结束(2)已关闭(3)
// 3. 已结束(2) -> 可以转换为已关闭(3)
// 4. 已关闭(3) -> 不可再转换
// 校验已关闭的活动不能再修改状态
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_STATUS_NOT_ALLOW_UPDATE,
currentStatus != 3);
// 校验状态转换的合法性
if (currentStatus == 0) {
// 未开始 -> 只能转换为 进行中(1) 已关闭(3)
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_STATUS_NOT_ALLOW_UPDATE,
status == 1 || status == 3);
// 转换为进行中时需要校验时间
if (status == 1) {
ResponseAssert.isTrue(LotteryErrorCode.NOT_IN_ACTIVITY_TIME,
now.after(activity.getStartTime()) || now.equals(activity.getStartTime()));
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_ENDED,
now.before(activity.getEndTime()));
}
} else if (currentStatus == 1) {
// 进行中 -> 只能转换为 已结束(2) 已关闭(3)
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_STATUS_NOT_ALLOW_UPDATE,
status == 2 || status == 3);
} else if (currentStatus == 2) {
// 已结束 -> 只能转换为 已关闭(3)
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_STATUS_NOT_ALLOW_UPDATE,
status == 3);
}
activity.setStatus(status);
lotteryActivityService.updateById(activity);
lotteryActivityService.updateSelectiveById(activity);
return ResultResponse.success();
}

View File

@ -1,5 +1,6 @@
package com.red.circle.other.app.inner.endpoint.activity;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.dto.ResultResponse;
@ -37,7 +38,7 @@ public class LotteryActivityQueryClientEndpoint implements LotteryActivityQueryC
Integer pageSize = cmd.getPageSize() != null ? cmd.getPageSize() : 20;
Page<LotteryActivity> page = new Page<>(pageNo, pageSize);
var query = lotteryActivityService.lambdaQuery();
var query = lotteryActivityService.query();
if (cmd.getActivityName() != null) {
query.like(LotteryActivity::getActivityName, cmd.getActivityName());
@ -55,7 +56,7 @@ public class LotteryActivityQueryClientEndpoint implements LotteryActivityQueryC
query.le(LotteryActivity::getStartTime, cmd.getStartTimeEnd());
}
Page<LotteryActivity> result = query.orderByDesc(LotteryActivity::getCreateTime).page(page);
IPage<LotteryActivity> result = query.orderByDesc(LotteryActivity::getCreateTime).page(page);
List<LotteryActivityDTO> dtoList = result.getRecords().stream()
.map(activity -> {

View File

@ -1,6 +1,9 @@
package com.red.circle.other.app.inner.endpoint.activity;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.framework.mybatis.mybatisplus.LambdaQueryWrapperChain;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryPrize;
import com.red.circle.other.infra.database.rds.service.activity.LotteryPrizeService;
import com.red.circle.other.inner.endpoint.activity.api.LotteryPrizeClientApi;
@ -30,7 +33,7 @@ public class LotteryPrizeClientEndpoint implements LotteryPrizeClientApi {
@Override
public ResultResponse<List<LotteryPrizeDTO>> listByActivityId(Long activityId) {
List<LotteryPrize> prizes = lotteryPrizeService.lambdaQuery()
List<LotteryPrize> prizes = lotteryPrizeService.query()
.eq(LotteryPrize::getActivityId, activityId)
.orderByAsc(LotteryPrize::getSortOrder)
.list();
@ -55,12 +58,10 @@ public class LotteryPrizeClientEndpoint implements LotteryPrizeClientApi {
@Override
public ResultResponse<Boolean> deductStock(Long id, Integer count) {
boolean success = lotteryPrizeService.lambdaUpdate()
.eq(LotteryPrize::getId, id)
LambdaUpdateWrapper<LotteryPrize> wrapper = new LambdaUpdateWrapper<LotteryPrize>().eq(LotteryPrize::getId, id)
.gt(LotteryPrize::getRemainingStock, 0)
.setSql("remaining_stock = remaining_stock - " + count)
.update();
.setSql("remaining_stock = remaining_stock - " + count);
boolean success = lotteryPrizeService.update(wrapper);
return ResultResponse.success(success);
}

View File

@ -65,7 +65,7 @@ public class LotteryPrizeManageClientEndpoint implements LotteryPrizeManageClien
@Override
public ResultResponse<List<LotteryPrizeDTO>> listByActivityId(Long activityId) {
List<LotteryPrize> prizes = lotteryPrizeService.lambdaQuery()
List<LotteryPrize> prizes = lotteryPrizeService.query()
.eq(LotteryPrize::getActivityId, activityId)
.orderByAsc(LotteryPrize::getSortOrder)
.list();
@ -107,7 +107,7 @@ public class LotteryPrizeManageClientEndpoint implements LotteryPrizeManageClien
* 更新活动总库存内部方法.
*/
private void updateActivityStockInternal(Long activityId) {
List<LotteryPrize> prizes = lotteryPrizeService.lambdaQuery()
List<LotteryPrize> prizes = lotteryPrizeService.query()
.eq(LotteryPrize::getActivityId, activityId)
.list();
@ -119,11 +119,11 @@ public class LotteryPrizeManageClientEndpoint implements LotteryPrizeManageClien
.mapToInt(LotteryPrize::getRemainingStock)
.sum();
lotteryActivityService.lambdaUpdate()
lotteryActivityService.update()
.eq(LotteryActivity::getId, activityId)
.set(LotteryActivity::getTotalStock, totalStock)
.set(LotteryActivity::getConsumedStock, totalStock - remainingStock)
.update();
.execute();
}
}

View File

@ -6,6 +6,8 @@ import com.red.circle.other.infra.database.rds.service.activity.LotteryRecordSer
import com.red.circle.other.inner.endpoint.activity.api.LotteryRecordClientApi;
import com.red.circle.other.inner.model.dto.activity.LotteryRecordDTO;
import java.time.LocalDateTime;
import com.red.circle.tool.core.date.TimestampUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.http.MediaType;
@ -29,7 +31,7 @@ public class LotteryRecordClientEndpoint implements LotteryRecordClientApi {
@Override
public ResultResponse<Long> saveRecord(String recordNo, Long userId, Long activityId,
Long prizeId, String prizeName, Integer prizeType,
Long prizeId, String prizeName, String prizeType,
Integer isWin) {
LotteryRecord record = new LotteryRecord();
record.setRecordNo(recordNo);
@ -39,7 +41,7 @@ public class LotteryRecordClientEndpoint implements LotteryRecordClientApi {
record.setPrizeName(prizeName);
record.setPrizeType(prizeType);
record.setIsWin(isWin);
record.setDrawTime(LocalDateTime.now());
record.setDrawTime(TimestampUtils.now());
record.setPrizeStatus(isWin == 1 ? 0 : null);
lotteryRecordService.save(record);
@ -49,9 +51,9 @@ public class LotteryRecordClientEndpoint implements LotteryRecordClientApi {
@Override
public ResultResponse<LotteryRecordDTO> getByRecordNo(String recordNo) {
LotteryRecord record = lotteryRecordService.lambdaQuery()
LotteryRecord record = lotteryRecordService.query()
.eq(LotteryRecord::getRecordNo, recordNo)
.one();
.getOne();
if (record == null) {
return ResultResponse.success(null);

View File

@ -1,5 +1,6 @@
package com.red.circle.other.app.inner.endpoint.activity;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.PageResult;
@ -13,10 +14,14 @@ import com.red.circle.other.inner.endpoint.activity.api.LotteryRecordManageClien
import com.red.circle.other.inner.model.cmd.activity.LotteryRecordQryCmd;
import com.red.circle.other.inner.model.dto.activity.LotteryActivityDTO;
import com.red.circle.other.inner.model.dto.activity.LotteryRecordDTO;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.red.circle.tool.core.date.TimestampUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.http.MediaType;
@ -46,7 +51,7 @@ public class LotteryRecordManageClientEndpoint implements LotteryRecordManageCli
Integer pageSize = cmd.getPageSize() != null ? cmd.getPageSize() : 20;
Page<LotteryRecord> page = new Page<>(pageNo, pageSize);
var query = lotteryRecordService.lambdaQuery();
var query = lotteryRecordService.query();
if (cmd.getUserId() != null) {
query.eq(LotteryRecord::getUserId, cmd.getUserId());
@ -55,9 +60,9 @@ public class LotteryRecordManageClientEndpoint implements LotteryRecordManageCli
query.eq(LotteryRecord::getActivityId, cmd.getActivityId());
}
if (cmd.getActivityCode() != null) {
LotteryActivity activity = lotteryActivityService.lambdaQuery()
LotteryActivity activity = lotteryActivityService.query()
.eq(LotteryActivity::getActivityCode, cmd.getActivityCode())
.one();
.getOne();
if (activity != null) {
query.eq(LotteryRecord::getActivityId, activity.getId());
}
@ -75,7 +80,7 @@ public class LotteryRecordManageClientEndpoint implements LotteryRecordManageCli
query.le(LotteryRecord::getDrawTime, cmd.getDrawTimeEnd());
}
Page<LotteryRecord> result = query.orderByDesc(LotteryRecord::getDrawTime).page(page);
IPage<LotteryRecord> result = query.orderByDesc(LotteryRecord::getDrawTime).page(page);
// 查询活动信息
List<Long> activityIds = result.getRecords().stream()
@ -83,7 +88,11 @@ public class LotteryRecordManageClientEndpoint implements LotteryRecordManageCli
.distinct()
.collect(Collectors.toList());
Map<Long, LotteryActivity> activityMap = lotteryActivityService.lambdaQuery()
if (activityIds.isEmpty()) {
return ResultResponse.success(PageResult.newPageResult(cmd.getPageSize()));
}
Map<Long, LotteryActivity> activityMap = lotteryActivityService.query()
.in(LotteryActivity::getId, activityIds)
.list()
.stream()
@ -126,8 +135,8 @@ public class LotteryRecordManageClientEndpoint implements LotteryRecordManageCli
// 4-积分调用积分服务
record.setPrizeStatus(1); // 已发放
record.setDeliverTime(LocalDateTime.now());
lotteryRecordService.updateById(record);
record.setDeliverTime(TimestampUtils.now());
lotteryRecordService.updateSelectiveById(record);
return ResultResponse.success();
}

View File

@ -1,5 +1,6 @@
package com.red.circle.other.app.inner.endpoint.activity;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicket;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryTicketRecord;
@ -31,7 +32,7 @@ public class LotteryTicketClientEndpoint implements LotteryTicketClientApi {
@Override
public ResultResponse<Integer> getUserTicketCount(Long userId) {
List<LotteryTicket> tickets = lotteryTicketService.lambdaQuery()
List<LotteryTicket> tickets = lotteryTicketService.query()
.eq(LotteryTicket::getUserId, userId)
.eq(LotteryTicket::getStatus, 1)
.gt(LotteryTicket::getRemainingCount, 0)
@ -47,7 +48,7 @@ public class LotteryTicketClientEndpoint implements LotteryTicketClientApi {
@Override
@Transactional(rollbackFor = Exception.class)
public ResultResponse<Boolean> deductTicket(Long userId, Integer count) {
List<LotteryTicket> tickets = lotteryTicketService.lambdaQuery()
List<LotteryTicket> tickets = lotteryTicketService.query()
.eq(LotteryTicket::getUserId, userId)
.eq(LotteryTicket::getStatus, 1)
.gt(LotteryTicket::getRemainingCount, 0)
@ -69,12 +70,12 @@ public class LotteryTicketClientEndpoint implements LotteryTicketClientApi {
}
int deduct = Math.min(remaining, ticket.getRemainingCount());
lotteryTicketService.lambdaUpdate()
LambdaUpdateWrapper<LotteryTicket> updateWrapper = new LambdaUpdateWrapper<LotteryTicket>()
.eq(LotteryTicket::getId, ticket.getId())
.setSql("used_count = used_count + " + deduct)
.setSql("remaining_count = remaining_count - " + deduct)
.update();
.setSql("remaining_count = remaining_count - " + deduct);
lotteryTicketService.update(updateWrapper);
// 记录使用
LotteryTicketRecord ticketRecord = new LotteryTicketRecord();

View File

@ -1,12 +1,15 @@
package com.red.circle.other.app.inner.endpoint.activity;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.ResultResponse;
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.service.activity.LotteryTicketRecordService;
import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketService;
import com.red.circle.other.inner.asserts.lottery.LotteryErrorCode;
import com.red.circle.other.inner.endpoint.activity.api.LotteryTicketManageClientApi;
import com.red.circle.other.inner.model.cmd.activity.LotteryTicketGrantCmd;
import java.sql.Timestamp;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
@ -32,9 +35,21 @@ public class LotteryTicketManageClientEndpoint implements LotteryTicketManageCli
@Override
@Transactional(rollbackFor = Exception.class)
public ResultResponse<Void> grantTickets(LotteryTicketGrantCmd cmd) {
// 校验必填参数
ResponseAssert.isTrue(LotteryErrorCode.USER_ID_REQUIRED,
cmd.getUserIds() != null && !cmd.getUserIds().isEmpty());
ResponseAssert.isTrue(LotteryErrorCode.GRANT_COUNT_INVALID,
cmd.getCount() != null && cmd.getCount() > 0);
// 来源ID必填 - 用于追踪抽奖券的发放来源
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_ID_REQUIRED,
cmd.getSourceId() != null && !cmd.getSourceId().isEmpty());
String ticketSource = cmd.getTicketSource() != null ? cmd.getTicketSource() : "SYSTEM";
Timestamp now = new Timestamp(System.currentTimeMillis());
for (Long userId : cmd.getUserIds()) {
// 创建抽奖券记录
LotteryTicket ticket = new LotteryTicket();
ticket.setUserId(userId);
ticket.setTicketSource(ticketSource);
@ -43,16 +58,16 @@ public class LotteryTicketManageClientEndpoint implements LotteryTicketManageCli
ticket.setUsedCount(0);
ticket.setRemainingCount(cmd.getCount());
ticket.setExpireTime(cmd.getExpireTime());
ticket.setStatus(1);
ticket.setStatus(1); // 正常状态
lotteryTicketService.save(ticket);
// 记录发放
// 记录发放日志
LotteryTicketRecord ticketRecord = new LotteryTicketRecord();
ticketRecord.setUserId(userId);
ticketRecord.setTicketId(ticket.getId());
ticketRecord.setChangeCount(cmd.getCount());
ticketRecord.setChangeType(1); // 发放
ticketRecord.setRemark(cmd.getRemark());
ticketRecord.setChangeType(1); // 1-发放
ticketRecord.setRemark(cmd.getRemark() != null ? cmd.getRemark() : "系统发放抽奖券");
lotteryTicketRecordService.save(ticketRecord);
}

View File

@ -27,11 +27,11 @@ public class LotteryUserCountClientEndpoint implements LotteryUserCountClientApi
@Override
public ResultResponse<Integer> getTodayCount(Long userId, Long activityId) {
LotteryUserCount userCount = lotteryUserCountService.lambdaQuery()
LotteryUserCount userCount = lotteryUserCountService.query()
.eq(LotteryUserCount::getUserId, userId)
.eq(LotteryUserCount::getActivityId, activityId)
.eq(LotteryUserCount::getDrawDate, LocalDate.now())
.one();
.getOne();
int count = userCount != null ? userCount.getDrawCount() : 0;
@ -40,11 +40,11 @@ public class LotteryUserCountClientEndpoint implements LotteryUserCountClientApi
@Override
public ResultResponse<Void> incrDrawCount(Long userId, Long activityId) {
LotteryUserCount userCount = lotteryUserCountService.lambdaQuery()
LotteryUserCount userCount = lotteryUserCountService.query()
.eq(LotteryUserCount::getUserId, userId)
.eq(LotteryUserCount::getActivityId, activityId)
.eq(LotteryUserCount::getDrawDate, LocalDate.now())
.one();
.getOne();
if (userCount == null) {
userCount = new LotteryUserCount();
@ -54,10 +54,10 @@ public class LotteryUserCountClientEndpoint implements LotteryUserCountClientApi
userCount.setDrawCount(1);
lotteryUserCountService.save(userCount);
} else {
lotteryUserCountService.lambdaUpdate()
lotteryUserCountService.update()
.eq(LotteryUserCount::getId, userCount.getId())
.setSql("draw_count = draw_count + 1")
.update();
.execute();
}
return ResultResponse.success();