release: 定时抽奖

release-2024-08-08
huangyw 4 months ago
parent 3ffd9f9549
commit 9e7f41f5c0

@ -54,5 +54,6 @@ public class ErrorConstants {
public static final String NOT_END_OF_SIGN_UP = "报名未结束,不允许抽奖";
public static final String TICKET_CITY_IS_REQUIRED = "门票抽奖城市必填";
public static final String REAL_NUMBER_IS_REQUIRED = "实物抽奖数量必填";
public static final String DRAW_TIME_IS_REQUIRED = "必须填写开奖时间";
}

@ -0,0 +1,111 @@
package com.luoo.user.Scheduler;
import com.luoo.user.pojo.Lottery;
import com.luoo.user.service.DrawLotteryService;
import com.luoo.user.service.LotteryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
* @program: luoo_parent
* @description:
* @author: yawei.huang
* @create: 2024-08-02 14:39
**/
@Slf4j
@Component
@Configuration
public class DrawLotteryScheduler {
private final ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
private ScheduledFuture<?> scheduledFuture;
private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();
private final DrawLotteryService drawLotteryService;
public DrawLotteryScheduler(DrawLotteryService drawLotteryService) {
this.drawLotteryService = drawLotteryService;
}
@PostConstruct
public void init() {
taskScheduler.initialize();
List<Lottery> signingList = drawLotteryService.findSigningList();
signingList.forEach(item -> {
scheduleDraw(item.getDrawTime(), item.getId());
});
}
@PreDestroy
public void destroy() {
taskScheduler.shutdown();
}
public void scheduleDraw(LocalDateTime drawTime, String id) {
ScheduledFuture<?> existingFuture = scheduledFutures.get(id);
if (existingFuture != null && !existingFuture.isCancelled()) {
cancelDraw(id);
}
// 将 LocalDateTime 转换为 Instant然后获取毫秒数
ZoneId zoneId = ZoneId.systemDefault();
Instant instant = drawTime.atZone(zoneId).toInstant();
long delay = instant.toEpochMilli() - System.currentTimeMillis();
if (delay > 0) {
scheduledFuture = taskScheduler.schedule(() -> {
// 这里执行操作
log.info("抽奖{}", id);
drawLotteryService.auto(id, null);
}, new Date(System.currentTimeMillis() + delay));
scheduledFutures.put(id, scheduledFuture);
log.info("scheduleDraw--------:{}",scheduledFutures);
} else {
// 处理时间已过的情况
// 立即抽奖
drawLotteryService.auto(id, null);
}
}
// 取消任务的方法
public void cancelDraw(String id) {
log.info("cancelDraw--------:{}",scheduledFutures);
// 从映射中获取与 ID 关联的 ScheduledFuture 对象
ScheduledFuture<?> scheduledFuture = scheduledFutures.get(id);
// 检查 ScheduledFuture 是否存在
if (scheduledFuture != null) {
// 取消任务并尝试中断正在执行的任务
boolean canceled = scheduledFuture.cancel(true);
if (canceled) {
log.info("Task with ID {} was canceled.", id);
} else {
log.info("Task with ID {} could not be canceled.", id);
}
scheduledFutures.remove(id);
// 从映射中移除该 ID 的条目
scheduledFutures.remove(id);
} else {
log.info("No task found with ID: {}", id);
}
}
}

@ -10,6 +10,7 @@ import com.luoo.user.dto.point.UserPointLogSearchDto;
import com.luoo.user.pojo.Lottery;
import com.luoo.user.pojo.TaskPoint;
import com.luoo.user.pojo.UserPointLog;
import com.luoo.user.service.DrawLotteryService;
import com.luoo.user.service.LotteryService;
import com.luoo.user.service.TaskPointService;
import com.luoo.user.service.UserPointLogService;
@ -17,7 +18,6 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiOperationSort;
import io.swagger.annotations.ApiParam;
import java.util.List;
@ -59,6 +59,9 @@ public class PointController {
@Autowired
private LotteryService lotteryService;
@Autowired
private DrawLotteryService drawLotteryService;
@ApiOperation(value = "1.1.新增任务积分配置(PC)", notes = "仅限admin权限用户调用")
@PostMapping("/task/add")
@ -297,9 +300,8 @@ public class PointController {
@ApiImplicitParam(name = "id", value = "抽奖id", required = true, dataType = "String", paramType = "query")
})
public Result<Void> auto(@RequestHeader("Authorization") String token, @NotNull String id) {
lotteryService.auto(id, token);
drawLotteryService.auto(id, token);
return Result.success();
}
}

@ -4,7 +4,11 @@ import com.luoo.user.pojo.Lottery;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
public interface LotteryDao extends JpaRepository<Lottery, String>,
JpaSpecificationExecutor<Lottery> {
public List<Lottery> findLotteriesByStatus(Integer status);
}

@ -1,10 +1,13 @@
package com.luoo.user.dto.point;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.luoo.user.pojo.LotteryRegion;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.Transient;
import java.io.Serializable;
import java.time.LocalDateTime;
@ -45,12 +48,21 @@ public class LotteryDto implements Serializable {
@ApiModelProperty(value = "地区名称")
private String regionName;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "报名开始时间")
private LocalDateTime applyStartTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "报名结束时间")
private LocalDateTime applyEndTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "抽奖时间")
private LocalDateTime drawTime;
@ApiModelProperty(value = "抽奖方式 1-自动抽奖 2-手动抽奖")
private Integer way;

@ -2,11 +2,9 @@ package com.luoo.user.listener;
import com.luoo.user.pojo.UserPointLog;
import com.luoo.user.service.UserPointLogService;
import dto.MusicPointDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@ -2,7 +2,6 @@ package com.luoo.user.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;
import javax.persistence.Column;
@ -78,13 +77,23 @@ public class Lottery {
private String regionName;
@Column(name = "apply_start_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "报名开始时间")
private LocalDateTime applyStartTime;
@Column(name = "apply_end_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "报名结束时间")
private LocalDateTime applyEndTime;
@Column(name = "draw_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "抽奖时间")
private LocalDateTime drawTime;
@Column(name = "way")
@ApiModelProperty(value = "抽奖方式 1-自动抽奖 2-手动抽奖")
private Integer way;

@ -0,0 +1,156 @@
package com.luoo.user.service;
import com.luoo.user.dao.LotteryDao;
import com.luoo.user.dao.LotteryRegionDao;
import com.luoo.user.dao.LotteryUserDao;
import com.luoo.user.pojo.Lottery;
import com.luoo.user.pojo.LotteryRegion;
import com.luoo.user.pojo.LotteryUser;
import constants.Constants;
import constants.ErrorConstants;
import dto.UserLoginDto;
import dto.UserMessageDto;
import enums.MessageTypeEnum;
import enums.PointEnums;
import exception.BizException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import util.JwtUtil;
import java.time.LocalDateTime;
import java.util.*;
/**
* @program: luoo_parent
* @description:
* @author: yawei.huang
* @create: 2024-08-02 16:11
**/
@Service
public class DrawLotteryService {
private final LotteryDao lotteryDao;
private final LotteryRegionDao lotteryRegionDao;
private final LotteryUserDao lotteryUserDao;
private final JwtUtil jwtUtil;
private final UserMessageService userMessageService;
public DrawLotteryService(LotteryDao lotteryDao, LotteryRegionDao lotteryRegionDao, LotteryUserDao lotteryUserDao, JwtUtil jwtUtil, UserMessageService userMessageService) {
this.lotteryDao = lotteryDao;
this.lotteryRegionDao = lotteryRegionDao;
this.lotteryUserDao = lotteryUserDao;
this.jwtUtil = jwtUtil;
this.userMessageService = userMessageService;
}
public List<Lottery> findSigningList() {
return lotteryDao.findLotteriesByStatus(PointEnums.LOTTERY_STATUS_SIGN.getCode());
}
/**
*
*
* @param id id
*/
@Transactional(rollbackFor = Exception.class)
public void auto(String id, String token) {
Lottery lottery = lotteryDao.findById(id).get();
LocalDateTime applyEndTime = lottery.getApplyEndTime();
// 比较当前时间与applyEndTime
if (LocalDateTime.now().isBefore(applyEndTime)) {
throw new BizException(ErrorConstants.NOT_END_OF_SIGN_UP);
}
if (Objects.equals(lottery.getStatus(), PointEnums.LOTTERY_STATUS_SIGN.getCode())) {
if (Objects.equals(lottery.getType(), PointEnums.LOTTERY_TICKET.getCode())) {
// 门票抽奖业务
List<LotteryRegion> lotteryRegionList = lotteryRegionDao.findByLotteryId(id);
lotteryRegionList.forEach(lotteryRegion -> {
Integer num = lotteryRegion.getNum();
if (num > 0) {
List<LotteryUser> lotteryUserList = lotteryUserDao.findByLotteryIdAndRegionId(id,
lotteryRegion.getRegionId());
lotteryFunc(token, lotteryUserList, num);
}
});
} else if (Objects.equals(lottery.getType(), PointEnums.LOTTERY_REAL.getCode())) {
// 实物抽奖业务
List<LotteryUser> byLotteryId = lotteryUserDao.findByLotteryId(id);
lotteryFunc(token, byLotteryId, lottery.getNum());
}
// 抽奖状态为报名中,自动抽奖
lottery.setStatus(PointEnums.LOTTERY_STATUS_LOTTERY.getCode());
lotteryDao.save(lottery);
}
}
/**
*
*
* @param token
* @param lotteryUserList
* @param num
*/
private void lotteryFunc(String token, List<LotteryUser> lotteryUserList, Integer num) {
List<LotteryUser> winnerList = new ArrayList<>();
if (lotteryUserList.size() > num) {
// 抽奖人数大于设定人数
// 随机设定人数的数量中奖
winnerList = drawWinners(lotteryUserList, num);
} else {
// 抽奖人数小于设定人数
// 全部中奖
winnerList = lotteryUserList;
}
winnerList.forEach(lotteryUser -> {
lotteryUser.setResult(PointEnums.LOTTERY_RESULT_WIN.getCode());
if (StringUtils.isNotBlank(token)) {
UserLoginDto userLoginDto = jwtUtil.getUserLoginDto(token);
lotteryUser.setUpdateUser(userLoginDto.getUserId());
}
lotteryUserDao.save(lotteryUser);
// 发送恭喜中奖站内信
UserMessageDto userMessageDto = new UserMessageDto();
userMessageDto.setType(MessageTypeEnum.PRIVATE_MESSAGE.getType());
userMessageDto.setSendUserId(StringUtils.isNotBlank(token) ? jwtUtil.getUserLoginDto(token).getUserId() : null);
userMessageDto.setTitle("恭喜中奖!");
userMessageDto.setContent("恭喜中奖请加微信XXXXXX");
userMessageDto.setUserId(lotteryUser.getUserId());
userMessageDto.setSendUserAvatar(Constants.RESOURCE_PREFIX + (StringUtils.isNotBlank(token) ? jwtUtil.getUserLoginDto(token).getAvatar() : null));
userMessageDto.setSendUserNickName(StringUtils.isNotBlank(token) ? jwtUtil.getUserLoginDto(token).getNickName() : null);
userMessageService.sendUserMessage(userMessageDto);
});
}
/**
*
*
* @param list
* @param winnersCount
* @return
*/
private List<LotteryUser> drawWinners(List<LotteryUser> list, int winnersCount) {
List<LotteryUser> winners = new ArrayList<>();
Collections.shuffle(winners, new Random());
return winners.subList(0, Math.min(winnersCount, winners.size()));
}
}

@ -1,6 +1,7 @@
package com.luoo.user.service;
import api.PageResult;
import com.luoo.user.Scheduler.DrawLotteryScheduler;
import com.luoo.user.dao.LotteryDao;
import com.luoo.user.dao.LotteryRegionDao;
import com.luoo.user.dao.LotteryUserDao;
@ -12,22 +13,16 @@ import com.luoo.user.pojo.LotteryUser;
import com.luoo.user.pojo.Region;
import com.luoo.user.pojo.UserInfo;
import com.luoo.user.pojo.UserPointLog;
import constants.Constants;
import constants.ErrorConstants;
import dto.UserLoginDto;
import dto.UserMessageDto;
import enums.MessageTypeEnum;
import enums.PointEnums;
import enums.UserTypeEnum;
import enums.UserVipStatusEnum;
import exception.BizException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
@ -36,7 +31,6 @@ import javax.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
@ -80,23 +74,22 @@ public class LotteryService {
private final LotteryUserDao lotteryUserDao;
private final UserMessageService userMessageService;
private final DrawLotteryScheduler drawLotteryScheduler;
public LotteryService(LotteryDao lotteryDao, IdWorker idWorker, JwtUtil jwtUtil,
RedisLockUtil redisLockUtil, RegionService regionService, LotteryRegionDao lotteryRegionDao,
UserInfoDao userInfoDao, RabbitTemplate rabbitTemplate, LotteryUserDao lotteryUserDao, UserMessageService userMessageService) {
public LotteryService(LotteryDao lotteryDao, JwtUtil jwtUtil, IdWorker idWorker, RedisLockUtil redisLockUtil, RegionService regionService, LotteryRegionDao lotteryRegionDao, UserInfoDao userInfoDao, RabbitTemplate rabbitTemplate, LotteryUserDao lotteryUserDao, DrawLotteryScheduler drawLotteryScheduler) {
this.lotteryDao = lotteryDao;
this.idWorker = idWorker;
this.jwtUtil = jwtUtil;
this.idWorker = idWorker;
this.redisLockUtil = redisLockUtil;
this.regionService = regionService;
this.lotteryRegionDao = lotteryRegionDao;
this.userInfoDao = userInfoDao;
this.rabbitTemplate = rabbitTemplate;
this.lotteryUserDao = lotteryUserDao;
this.userMessageService = userMessageService;
this.drawLotteryScheduler = drawLotteryScheduler;
}
/**
*
*
@ -236,6 +229,10 @@ public class LotteryService {
UserLoginDto userLoginDto = jwtUtil.getUserLoginDto(token);
Lottery lottery = lotteryDao.findById(id).orElse(null);
if (lottery.getDrawTime() == null) {
throw new BizException(ErrorConstants.DRAW_TIME_IS_REQUIRED);
}
lottery.setUpdateUser(userLoginDto.getUserId());
lottery.setUpdateUserName(userLoginDto.getNickName());
@ -243,6 +240,8 @@ public class LotteryService {
lotteryDao.save(lottery);
drawLotteryScheduler.scheduleDraw(lottery.getDrawTime(), id);
}
/**
@ -281,6 +280,9 @@ public class LotteryService {
});
// 取消抽奖
drawLotteryScheduler.cancelDraw(id);
}
/**
@ -359,105 +361,6 @@ public class LotteryService {
}
/**
*
*
* @param id id
*/
@Transactional(rollbackFor = Exception.class)
public void auto(String id, String token) {
Lottery lottery = lotteryDao.findById(id).get();
LocalDateTime applyEndTime = lottery.getApplyEndTime();
// 比较当前时间与applyEndTime
if (LocalDateTime.now().isBefore(applyEndTime)) {
throw new BizException(ErrorConstants.NOT_END_OF_SIGN_UP);
}
if (Objects.equals(lottery.getStatus(), PointEnums.LOTTERY_STATUS_SIGN.getCode())) {
if (Objects.equals(lottery.getType(), PointEnums.LOTTERY_TICKET.getCode())) {
// 门票抽奖业务
List<LotteryRegion> lotteryRegionList = lotteryRegionDao.findByLotteryId(id);
lotteryRegionList.forEach(lotteryRegion -> {
Integer num = lotteryRegion.getNum();
if (num > 0) {
List<LotteryUser> lotteryUserList = lotteryUserDao.findByLotteryIdAndRegionId(id,
lotteryRegion.getRegionId());
lotteryFunc(token, lotteryUserList, num);
}
});
} else if (Objects.equals(lottery.getType(), PointEnums.LOTTERY_REAL.getCode())) {
// 实物抽奖业务
List<LotteryUser> byLotteryId = lotteryUserDao.findByLotteryId(id);
lotteryFunc(token, byLotteryId, lottery.getNum());
}
// 抽奖状态为报名中,自动抽奖
lottery.setStatus(PointEnums.LOTTERY_STATUS_LOTTERY.getCode());
lotteryDao.save(lottery);
}
}
/**
*
*
* @param token
* @param lotteryUserList
* @param num
*/
private void lotteryFunc(String token, List<LotteryUser> lotteryUserList, Integer num) {
List<LotteryUser> winnerList = new ArrayList<>();
if (lotteryUserList.size() > num) {
// 抽奖人数大于设定人数
// 随机设定人数的数量中奖
winnerList = drawWinners(lotteryUserList, num);
} else {
// 抽奖人数小于设定人数
// 全部中奖
winnerList = lotteryUserList;
}
winnerList.forEach(lotteryUser -> {
lotteryUser.setResult(PointEnums.LOTTERY_RESULT_WIN.getCode());
if (StringUtils.isNotBlank(token)) {
UserLoginDto userLoginDto = jwtUtil.getUserLoginDto(token);
lotteryUser.setUpdateUser(userLoginDto.getUserId());
}
lotteryUserDao.save(lotteryUser);
// 发送恭喜中奖站内信
UserMessageDto userMessageDto = new UserMessageDto();
userMessageDto.setType(MessageTypeEnum.PRIVATE_MESSAGE.getType());
userMessageDto.setSendUserId(StringUtils.isNotBlank(token) ? jwtUtil.getUserLoginDto(token).getUserId() : null);
userMessageDto.setTitle("恭喜中奖!");
userMessageDto.setContent("恭喜中奖请加微信XXXXXX");
userMessageDto.setUserId(lotteryUser.getUserId());
userMessageDto.setSendUserAvatar(Constants.RESOURCE_PREFIX + (StringUtils.isNotBlank(token) ? jwtUtil.getUserLoginDto(token).getAvatar() : null));
userMessageDto.setSendUserNickName(StringUtils.isNotBlank(token) ? jwtUtil.getUserLoginDto(token).getNickName() : null);
userMessageService.sendUserMessage(userMessageDto);
});
}
/**
*
*
* @param list
* @param winnersCount
* @return
*/
private List<LotteryUser> drawWinners(List<LotteryUser> list, int winnersCount) {
List<LotteryUser> winners = new ArrayList<>();
Collections.shuffle(winners, new Random());
return winners.subList(0, Math.min(winnersCount, winners.size()));
}
/**
*
*

@ -0,0 +1,2 @@
alter table tb_lottery
add draw_time datetime null comment '开奖时间' after apply_end_time;
Loading…
Cancel
Save