- 完善 User/Product/Order 实体字段和关联关系 - 更新 DTO 适配新增字段 - 增强 Service 层业务逻辑和 Repository 查询方法 - 优化控制器接口,完善管理后台 API - 新增请求监控过滤器和指标服务
1121 lines
46 KiB
Java
1121 lines
46 KiB
Java
package com.org.flashsalesystem.service;
|
||
|
||
import com.org.flashsalesystem.dto.FlashSaleDTO;
|
||
import com.org.flashsalesystem.entity.FlashSale;
|
||
import com.org.flashsalesystem.entity.Order;
|
||
import com.org.flashsalesystem.entity.Product;
|
||
import com.org.flashsalesystem.repository.FlashSaleRepository;
|
||
import com.org.flashsalesystem.repository.OrderRepository;
|
||
import com.org.flashsalesystem.repository.ProductRepository;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.beans.BeanUtils;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.beans.factory.annotation.Value;
|
||
import org.springframework.data.domain.Page;
|
||
import org.springframework.data.domain.PageRequest;
|
||
import org.springframework.data.domain.Pageable;
|
||
import org.springframework.data.domain.Sort;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.annotation.Transactional;
|
||
|
||
import java.math.BigDecimal;
|
||
import java.time.LocalDateTime;
|
||
import java.time.ZoneOffset;
|
||
import java.util.HashMap;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.Optional;
|
||
import java.util.concurrent.TimeUnit;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* 秒杀服务类
|
||
* 实现秒杀活动管理、库存控制、分布式锁等核心功能
|
||
*/
|
||
@Service
|
||
@Slf4j
|
||
public class FlashSaleService {
|
||
|
||
private static final String FLASH_SALE_CACHE_PREFIX = "flashsale:";
|
||
private static final String FLASH_SALE_STOCK_PREFIX = "flashsale_stock:";
|
||
private static final String FLASH_SALE_LOCK_PREFIX = "flashsale_lock:";
|
||
private static final String FLASH_SALE_SUCCESS_USERS_PREFIX = "flashsale_success:";
|
||
private static final String ACTIVE_FLASH_SALES = "active_flashsales";
|
||
@Autowired
|
||
private FlashSaleRepository flashSaleRepository;
|
||
@Autowired
|
||
private ProductRepository productRepository;
|
||
@Autowired
|
||
private OrderRepository orderRepository;
|
||
@Autowired
|
||
private RedisService redisService;
|
||
@Autowired
|
||
private DistributedLockService lockService;
|
||
@Autowired
|
||
private RedissonLockService redissonLockService;
|
||
@Autowired
|
||
private RateLimitService rateLimitService;
|
||
@Autowired
|
||
private ProductService productService;
|
||
@Value("${flashsale.cache.flashsale-expire-minutes:10}")
|
||
private int flashSaleCacheExpireMinutes;
|
||
@Value("${flashsale.seckill.max-quantity-per-user:1}")
|
||
private int maxQuantityPerUser;
|
||
|
||
/**
|
||
* 创建秒杀活动
|
||
*/
|
||
@Transactional
|
||
public FlashSaleDTO createFlashSale(FlashSaleDTO.CreateDTO createDTO) {
|
||
log.info("创建秒杀活动: 商品ID={}, 秒杀价格={}, 库存={}",
|
||
createDTO.getProductId(), createDTO.getFlashPrice(), createDTO.getFlashStock());
|
||
|
||
// 验证商品是否存在
|
||
Optional<Product> productOpt = productRepository.findById(createDTO.getProductId());
|
||
if (!productOpt.isPresent()) {
|
||
throw new RuntimeException("商品不存在");
|
||
}
|
||
|
||
Product product = productOpt.get();
|
||
if (product.getStatus() != 1) {
|
||
throw new RuntimeException("商品未上架");
|
||
}
|
||
|
||
// 验证时间
|
||
if (createDTO.getStartTime().isAfter(createDTO.getEndTime())) {
|
||
throw new RuntimeException("开始时间不能晚于结束时间");
|
||
}
|
||
|
||
if (createDTO.getStartTime().isBefore(LocalDateTime.now())) {
|
||
throw new RuntimeException("开始时间不能早于当前时间");
|
||
}
|
||
|
||
// 检查是否已有该商品的秒杀活动
|
||
Optional<FlashSale> existingFlashSale = flashSaleRepository.findByProductId(createDTO.getProductId());
|
||
if (existingFlashSale.isPresent()) {
|
||
throw new RuntimeException("该商品已有秒杀活动");
|
||
}
|
||
|
||
// 创建秒杀活动
|
||
FlashSale flashSale = new FlashSale();
|
||
BeanUtils.copyProperties(createDTO, flashSale);
|
||
flashSale.setStatus(1); // 未开始
|
||
|
||
flashSale = flashSaleRepository.save(flashSale);
|
||
|
||
// 缓存秒杀活动信息
|
||
cacheFlashSaleInfo(flashSale, product);
|
||
|
||
// 预热库存到Redis - 确保存储为数字类型
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
|
||
redisService.delete(stockKey); // 先删除可能存在的异常数据
|
||
redisService.setString(stockKey, flashSale.getFlashStock().toString()); // 确保存储为字符串数字
|
||
|
||
// 验证库存设置是否成功
|
||
String verifyStock = redisService.getString(stockKey);
|
||
log.info("秒杀活动库存初始化验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}",
|
||
flashSale.getId(), stockKey, flashSale.getFlashStock(), verifyStock);
|
||
|
||
if (!flashSale.getFlashStock().toString().equals(verifyStock)) {
|
||
log.error("库存初始化失败: flashSaleId={}, expected={}, actual={}",
|
||
flashSale.getId(), flashSale.getFlashStock(), verifyStock);
|
||
}
|
||
|
||
log.info("秒杀活动创建成功: ID={}", flashSale.getId());
|
||
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
}
|
||
|
||
/**
|
||
* 参与秒杀
|
||
*/
|
||
@Transactional
|
||
public FlashSaleDTO.ResultDTO participateFlashSale(Long userId, FlashSaleDTO.ParticipateDTO participateDTO) {
|
||
log.info("用户参与秒杀: 用户ID={}, 秒杀ID={}, 数量={}",
|
||
userId, participateDTO.getFlashSaleId(), participateDTO.getQuantity());
|
||
|
||
// 限流检查
|
||
if (!rateLimitService.checkFlashSaleRateLimit(userId)) {
|
||
return createFailResult("请求过于频繁,请稍后再试");
|
||
}
|
||
|
||
// 获取秒杀活动信息
|
||
FlashSale flashSale = getFlashSaleById(participateDTO.getFlashSaleId());
|
||
if (flashSale == null) {
|
||
return createFailResult("秒杀活动不存在");
|
||
}
|
||
|
||
// 检查活动状态
|
||
if (!flashSale.isActive()) {
|
||
return createFailResult("秒杀活动未开始或已结束");
|
||
}
|
||
|
||
// 检查用户是否已经参与过
|
||
String successUsersKey = FLASH_SALE_SUCCESS_USERS_PREFIX + flashSale.getId();
|
||
if (redisService.sIsMember(successUsersKey, userId)) {
|
||
return createFailResult("您已经参与过该秒杀活动");
|
||
}
|
||
|
||
// 检查数据库中是否已有订单
|
||
if (orderRepository.existsFlashSaleOrder(userId, flashSale.getProductId())) {
|
||
return createFailResult("您已经购买过该商品");
|
||
}
|
||
|
||
// 检查购买数量限制
|
||
if (participateDTO.getQuantity() > maxQuantityPerUser) {
|
||
return createFailResult("超过单用户最大购买数量限制");
|
||
}
|
||
|
||
// 使用Redisson分布式锁防止超卖
|
||
String lockKey = FLASH_SALE_LOCK_PREFIX + flashSale.getId();
|
||
|
||
if (!redissonLockService.tryLock(lockKey, 3, 10)) { // 等待3秒,持有10秒
|
||
return createFailResult("系统繁忙,请稍后再试");
|
||
}
|
||
|
||
try {
|
||
// 检查并修复库存数据
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
|
||
String currentStock = redisService.getString(stockKey);
|
||
log.info("秒杀前库存检查: flashSaleId={}, stockKey={}, currentStock={}",
|
||
flashSale.getId(), stockKey, currentStock);
|
||
|
||
if (currentStock == null || currentStock.trim().isEmpty()) {
|
||
log.warn("检测到库存数据异常,尝试修复: flashSaleId={}", flashSale.getId());
|
||
repairFlashSaleStock(flashSale.getId());
|
||
|
||
// 修复后重新获取库存
|
||
currentStock = redisService.getString(stockKey);
|
||
log.info("修复后库存: flashSaleId={}, stockKey={}, currentStock={}",
|
||
flashSale.getId(), stockKey, currentStock);
|
||
|
||
// 如果修复后仍然为空,直接设置库存
|
||
if (currentStock == null || currentStock.trim().isEmpty()) {
|
||
log.error("库存修复失败,直接重新设置库存: flashSaleId={}, stock={}",
|
||
flashSale.getId(), flashSale.getFlashStock());
|
||
redisService.setString(stockKey, flashSale.getFlashStock().toString());
|
||
currentStock = redisService.getString(stockKey);
|
||
log.info("强制设置库存后: flashSaleId={}, stockKey={}, currentStock={}",
|
||
flashSale.getId(), stockKey, currentStock);
|
||
}
|
||
}
|
||
|
||
// 最终验证库存数据有效性
|
||
currentStock = redisService.getString(stockKey);
|
||
if (currentStock == null || currentStock.trim().isEmpty()) {
|
||
log.error("库存数据最终验证失败: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey);
|
||
return createFailResult("秒杀活动库存初始化失败,请稍后重试");
|
||
}
|
||
|
||
// 使用Lua脚本原子性扣减库存
|
||
log.info("准备执行秒杀脚本: stockKey={}, quantity={}, userId={}, currentStock={}",
|
||
stockKey, participateDTO.getQuantity(), userId, currentStock);
|
||
Long remainingStock = redisService.executeFlashSaleScript(stockKey, participateDTO.getQuantity());
|
||
log.info("秒杀脚本执行完成: stockKey={}, remainingStock={}", stockKey, remainingStock);
|
||
|
||
if (remainingStock < 0) {
|
||
if (remainingStock == -1) {
|
||
log.warn("秒杀库存key不存在或数据异常: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey);
|
||
|
||
// 自动修复数据并重试一次
|
||
log.info("开始自动修复库存数据: flashSaleId={}", flashSale.getId());
|
||
try {
|
||
repairFlashSaleStock(flashSale.getId());
|
||
|
||
// 修复后重新验证库存
|
||
String repairedStock = redisService.getString(stockKey);
|
||
if (repairedStock != null && !repairedStock.trim().isEmpty()) {
|
||
log.info("库存修复成功,重新执行秒杀脚本: flashSaleId={}, stock={}",
|
||
flashSale.getId(), repairedStock);
|
||
|
||
// 重新执行秒杀脚本
|
||
Long retryResult = redisService.executeFlashSaleScript(stockKey,
|
||
participateDTO.getQuantity());
|
||
log.info("修复后重试结果: flashSaleId={}, result={}", flashSale.getId(), retryResult);
|
||
|
||
if (retryResult >= 0) {
|
||
// 修复后重试成功,继续正常流程
|
||
remainingStock = retryResult;
|
||
} else {
|
||
// 重试仍然失败
|
||
if (retryResult == -2) {
|
||
return createFailResult("商品已售罄");
|
||
} else {
|
||
return createFailResult("系统繁忙,请稍后重试");
|
||
}
|
||
}
|
||
} else {
|
||
log.error("库存修复失败: flashSaleId={}", flashSale.getId());
|
||
return createFailResult("系统繁忙,请稍后重试");
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("自动修复库存失败: flashSaleId={}", flashSale.getId(), e);
|
||
return createFailResult("系统繁忙,请稍后重试");
|
||
}
|
||
} else if (remainingStock == -2) {
|
||
log.info("秒杀库存不足: flashSaleId={}, 剩余库存不足", flashSale.getId());
|
||
return createFailResult("商品已售罄");
|
||
} else if (remainingStock == -3) {
|
||
log.error("秒杀参数异常: flashSaleId={}, quantity={}", flashSale.getId(),
|
||
participateDTO.getQuantity());
|
||
return createFailResult("参数异常,请检查购买数量");
|
||
} else {
|
||
log.error("秒杀脚本执行异常: flashSaleId={}, returnValue={}", flashSale.getId(), remainingStock);
|
||
return createFailResult("系统异常,请稍后重试");
|
||
}
|
||
}
|
||
|
||
// 创建订单
|
||
Order order = createFlashSaleOrder(userId, flashSale, participateDTO);
|
||
|
||
// 添加到成功用户集合
|
||
redisService.sAdd(successUsersKey, userId);
|
||
redisService.expire(successUsersKey, 24, TimeUnit.HOURS);
|
||
|
||
// 更新数据库库存
|
||
flashSaleRepository.updateFlashStock(flashSale.getId(), participateDTO.getQuantity());
|
||
|
||
// 发布秒杀成功消息
|
||
publishFlashSaleResult(userId, flashSale, order, true);
|
||
|
||
// 更新商品销量排行榜
|
||
productService.increaseSalesRank(flashSale.getProductId(), participateDTO.getQuantity());
|
||
|
||
log.info("秒杀成功: 用户ID={}, 订单ID={}, 剩余库存={}", userId, order.getId(), remainingStock);
|
||
|
||
return createSuccessResult(order, flashSale);
|
||
|
||
} catch (Exception e) {
|
||
log.error("秒杀处理异常: 用户ID={}, 秒杀ID={}", userId, participateDTO.getFlashSaleId(), e);
|
||
return createFailResult("秒杀失败,请重试");
|
||
} finally {
|
||
redissonLockService.unlock(lockKey);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取秒杀活动列表
|
||
*/
|
||
public Map<String, Object> getFlashSaleList(FlashSaleDTO.QueryDTO queryDTO) {
|
||
// 验证排序字段
|
||
String sortBy = validateSortField(queryDTO.getSortBy());
|
||
|
||
// 构建分页和排序
|
||
Sort sort = Sort.by(Sort.Direction.fromString(queryDTO.getSortDirection()), sortBy);
|
||
Pageable pageable = PageRequest.of(queryDTO.getPage(), queryDTO.getSize(), sort);
|
||
|
||
Page<FlashSale> flashSalePage;
|
||
LocalDateTime now = LocalDateTime.now();
|
||
|
||
// 如果指定了商品ID,按商品ID查询
|
||
if (queryDTO.getProductId() != null) {
|
||
if (queryDTO.getStatus() != null) {
|
||
switch (queryDTO.getStatus()) {
|
||
case 1: // 未开始
|
||
flashSalePage = flashSaleRepository.findByProductIdAndStatus(queryDTO.getProductId(), 1,
|
||
pageable);
|
||
break;
|
||
case 2: // 进行中
|
||
flashSalePage = flashSaleRepository.findByProductIdAndStatus(queryDTO.getProductId(), 2,
|
||
pageable);
|
||
break;
|
||
case 3: // 已结束
|
||
flashSalePage = flashSaleRepository.findByProductIdAndStatus(queryDTO.getProductId(), 3,
|
||
pageable);
|
||
break;
|
||
default:
|
||
flashSalePage = flashSaleRepository.findByProductId(queryDTO.getProductId(), pageable);
|
||
break;
|
||
}
|
||
} else {
|
||
flashSalePage = flashSaleRepository.findByProductId(queryDTO.getProductId(), pageable);
|
||
}
|
||
} else {
|
||
// 根据状态查询
|
||
if (queryDTO.getStatus() != null) {
|
||
switch (queryDTO.getStatus()) {
|
||
case 1: // 未开始
|
||
flashSalePage = flashSaleRepository.findUpcomingFlashSales(now, pageable);
|
||
break;
|
||
case 2: // 进行中
|
||
flashSalePage = flashSaleRepository.findActiveFlashSales(now, pageable);
|
||
break;
|
||
case 3: // 已结束
|
||
flashSalePage = flashSaleRepository.findEndedFlashSales(now, pageable);
|
||
break;
|
||
default:
|
||
flashSalePage = flashSaleRepository.findAll(pageable);
|
||
}
|
||
} else {
|
||
flashSalePage = flashSaleRepository.findAll(pageable);
|
||
}
|
||
}
|
||
|
||
// 转换为DTO
|
||
List<FlashSaleDTO> flashSaleDTOs = flashSalePage.getContent().stream()
|
||
.map(flashSale -> {
|
||
Product product = productRepository.findById(
|
||
flashSale.getProductId()).orElse(null);
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
})
|
||
.collect(Collectors.toList());
|
||
|
||
Map<String, Object> result = new HashMap<>();
|
||
result.put("content", flashSaleDTOs);
|
||
result.put("totalElements", flashSalePage.getTotalElements());
|
||
result.put("totalPages", flashSalePage.getTotalPages());
|
||
result.put("currentPage", flashSalePage.getNumber());
|
||
result.put("size", flashSalePage.getSize());
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 获取正在进行的秒杀活动
|
||
*/
|
||
public List<FlashSaleDTO> getActiveFlashSales() {
|
||
// 尝试从缓存获取
|
||
List<Object> cachedFlashSales = redisService.lRange(ACTIVE_FLASH_SALES, 0, -1);
|
||
if (!cachedFlashSales.isEmpty()) {
|
||
return cachedFlashSales.stream()
|
||
.map(obj -> (FlashSaleDTO) obj)
|
||
.collect(Collectors.toList());
|
||
}
|
||
|
||
// 从数据库获取
|
||
LocalDateTime now = LocalDateTime.now();
|
||
List<FlashSale> activeFlashSales = flashSaleRepository.findActiveFlashSalesWithStock(now);
|
||
|
||
List<FlashSaleDTO> flashSaleDTOs = activeFlashSales.stream()
|
||
.map(flashSale -> {
|
||
Product product = productRepository.findById(
|
||
flashSale.getProductId()).orElse(null);
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
})
|
||
.collect(Collectors.toList());
|
||
|
||
// 缓存结果
|
||
if (!flashSaleDTOs.isEmpty()) {
|
||
redisService.delete(ACTIVE_FLASH_SALES);
|
||
redisService.rPush(ACTIVE_FLASH_SALES, flashSaleDTOs.toArray());
|
||
redisService.expire(ACTIVE_FLASH_SALES, 5, TimeUnit.MINUTES);
|
||
}
|
||
|
||
return flashSaleDTOs;
|
||
}
|
||
|
||
/**
|
||
* 根据ID获取秒杀活动
|
||
*/
|
||
public FlashSaleDTO getFlashSaleDTOById(Long flashSaleId) {
|
||
FlashSale flashSale = getFlashSaleById(flashSaleId);
|
||
if (flashSale == null) {
|
||
return null;
|
||
}
|
||
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
}
|
||
|
||
/**
|
||
* 预热秒杀活动
|
||
*/
|
||
public void preloadFlashSale(Long flashSaleId) {
|
||
log.info("预热秒杀活动: {}", flashSaleId);
|
||
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
log.warn("秒杀活动不存在: {}", flashSaleId);
|
||
return;
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
|
||
// 缓存秒杀活动信息
|
||
cacheFlashSaleInfo(flashSale, product);
|
||
|
||
// 预热库存 - 确保存储为数字类型
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId;
|
||
redisService.delete(stockKey); // 先删除可能存在的异常数据
|
||
redisService.setString(stockKey, flashSale.getFlashStock().toString()); // 确保存储为字符串数字
|
||
|
||
// 验证预热是否成功
|
||
String verifyStock = redisService.getString(stockKey);
|
||
log.info("秒杀活动预热验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}",
|
||
flashSaleId, stockKey, flashSale.getFlashStock(), verifyStock);
|
||
|
||
if (!flashSale.getFlashStock().toString().equals(verifyStock)) {
|
||
log.error("秒杀活动预热失败: flashSaleId={}, expected={}, actual={}",
|
||
flashSaleId, flashSale.getFlashStock(), verifyStock);
|
||
} else {
|
||
log.info("秒杀活动预热完成: flashSaleId={}, stock={}", flashSaleId, verifyStock);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 修复Redis中的库存数据
|
||
*/
|
||
public void repairFlashSaleStock(Long flashSaleId) {
|
||
log.info("修复秒杀活动库存数据: {}", flashSaleId);
|
||
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
log.warn("秒杀活动不存在: {}", flashSaleId);
|
||
return;
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId;
|
||
|
||
// 获取当前Redis中的库存值
|
||
String currentStock = redisService.getString(stockKey);
|
||
log.info("当前Redis库存值: key={}, value={}", stockKey, currentStock);
|
||
|
||
// 如果库存值无效,则重新设置
|
||
try {
|
||
if (currentStock == null || currentStock.trim().isEmpty()) {
|
||
log.warn("库存值为空,重新设置: key={}, resetTo={}", stockKey, flashSale.getFlashStock());
|
||
redisService.setString(stockKey, flashSale.getFlashStock().toString());
|
||
|
||
// 验证设置是否成功
|
||
String verifyStock = redisService.getString(stockKey);
|
||
log.info("库存设置验证: key={}, setTo={}, actualValue={}",
|
||
stockKey, flashSale.getFlashStock(), verifyStock);
|
||
} else {
|
||
Integer stockNumber = Integer.parseInt(currentStock.trim());
|
||
if (stockNumber < 0 || stockNumber > flashSale.getFlashStock()) {
|
||
log.warn("库存值异常,重新设置: key={}, currentValue={}, resetTo={}",
|
||
stockKey, currentStock, flashSale.getFlashStock());
|
||
redisService.setString(stockKey, flashSale.getFlashStock().toString());
|
||
|
||
// 验证设置是否成功
|
||
String verifyStock = redisService.getString(stockKey);
|
||
log.info("异常库存修复验证: key={}, setTo={}, actualValue={}",
|
||
stockKey, flashSale.getFlashStock(), verifyStock);
|
||
} else {
|
||
log.info("库存值正常: key={}, value={}", stockKey, stockNumber);
|
||
}
|
||
}
|
||
} catch (NumberFormatException e) {
|
||
log.error("库存值格式异常,重新设置: key={}, currentValue={}", stockKey, currentStock, e);
|
||
redisService.delete(stockKey);
|
||
redisService.setString(stockKey, flashSale.getFlashStock().toString());
|
||
|
||
// 验证设置是否成功
|
||
String verifyStock = redisService.getString(stockKey);
|
||
log.info("格式异常修复验证: key={}, setTo={}, actualValue={}",
|
||
stockKey, flashSale.getFlashStock(), verifyStock);
|
||
}
|
||
|
||
log.info("库存数据修复完成: {}", flashSaleId);
|
||
}
|
||
|
||
/**
|
||
* 重新预热所有活跃的秒杀活动库存
|
||
*/
|
||
public void preloadAllActiveFlashSales() {
|
||
log.info("开始预热所有活跃秒杀活动库存");
|
||
|
||
try {
|
||
// 获取所有秒杀活动(包括未开始、进行中的)
|
||
LocalDateTime now = LocalDateTime.now();
|
||
List<FlashSale> activeFlashSales = flashSaleRepository.findAll();
|
||
|
||
if (activeFlashSales.isEmpty()) {
|
||
log.info("没有找到任何秒杀活动,跳过预热");
|
||
return;
|
||
}
|
||
|
||
int successCount = 0;
|
||
int failCount = 0;
|
||
|
||
for (FlashSale flashSale : activeFlashSales) {
|
||
try {
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
|
||
|
||
// 检查活动状态,只预热有效的活动
|
||
if (flashSale.getStatus() == 3) { // 已结束的活动跳过
|
||
log.debug("跳过已结束的秒杀活动: flashSaleId={}", flashSale.getId());
|
||
continue;
|
||
}
|
||
|
||
// 验证库存数据有效性
|
||
if (flashSale.getFlashStock() == null || flashSale.getFlashStock() < 0) {
|
||
log.warn("秒杀活动库存数据无效,跳过: flashSaleId={}, stock={}",
|
||
flashSale.getId(), flashSale.getFlashStock());
|
||
failCount++;
|
||
continue;
|
||
}
|
||
|
||
// 检查当前Redis中的库存
|
||
String currentStock = redisService.getString(stockKey);
|
||
log.debug("预热前检查库存: flashSaleId={}, stockKey={}, currentStock={}",
|
||
flashSale.getId(), stockKey, currentStock);
|
||
|
||
// 如果库存已存在且有效,不重复设置
|
||
if (currentStock != null && !currentStock.trim().isEmpty()) {
|
||
try {
|
||
Integer existingStock = Integer.parseInt(currentStock.trim());
|
||
if (existingStock >= 0 && existingStock <= flashSale.getFlashStock()) {
|
||
log.debug("库存已存在且有效,跳过设置: flashSaleId={}, existingStock={}",
|
||
flashSale.getId(), existingStock);
|
||
successCount++;
|
||
continue;
|
||
}
|
||
} catch (NumberFormatException e) {
|
||
log.warn("现有库存格式异常,将重新设置: flashSaleId={}, currentStock={}",
|
||
flashSale.getId(), currentStock);
|
||
}
|
||
}
|
||
|
||
// 删除可能存在的异常数据
|
||
redisService.delete(stockKey);
|
||
|
||
// 设置库存数据 - 确保存储为字符串数字
|
||
redisService.setString(stockKey, flashSale.getFlashStock().toString());
|
||
|
||
// 验证设置是否成功
|
||
String verifyStock = redisService.getString(stockKey);
|
||
if (flashSale.getFlashStock().toString().equals(verifyStock)) {
|
||
log.info("预热秒杀活动库存成功: flashSaleId={}, stock={}, status={}",
|
||
flashSale.getId(), flashSale.getFlashStock(), getStatusText(flashSale.getStatus()));
|
||
successCount++;
|
||
} else {
|
||
log.error("预热秒杀活动库存验证失败: flashSaleId={}, expected={}, actual={}",
|
||
flashSale.getId(), flashSale.getFlashStock(), verifyStock);
|
||
failCount++;
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
log.error("预热秒杀活动库存失败: flashSaleId={}", flashSale.getId(), e);
|
||
failCount++;
|
||
}
|
||
}
|
||
|
||
log.info("所有秒杀活动库存预热完成: 总数={}, 成功={}, 失败={}",
|
||
activeFlashSales.size(), successCount, failCount);
|
||
|
||
} catch (Exception e) {
|
||
log.error("预热所有秒杀活动库存时发生异常", e);
|
||
throw new RuntimeException("预热秒杀活动库存失败: " + e.getMessage(), e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取状态文本描述
|
||
*/
|
||
private String getStatusText(Integer status) {
|
||
if (status == null) return "未知";
|
||
switch (status) {
|
||
case 1:
|
||
return "未开始";
|
||
case 2:
|
||
return "进行中";
|
||
case 3:
|
||
return "已结束";
|
||
case 4:
|
||
return "已暂停";
|
||
default:
|
||
return "未知";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取秒杀活动剩余库存
|
||
*/
|
||
public Integer getFlashSaleStock(Long flashSaleId) {
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId;
|
||
Object stock = redisService.get(stockKey);
|
||
return stock != null ? Integer.valueOf(stock.toString()) : 0;
|
||
}
|
||
|
||
/**
|
||
* 更新秒杀活动
|
||
*/
|
||
@Transactional
|
||
public FlashSaleDTO updateFlashSale(Long flashSaleId, FlashSaleDTO.UpdateDTO updateDTO) {
|
||
log.info("更新秒杀活动: ID={}", flashSaleId);
|
||
|
||
// 获取现有秒杀活动
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
throw new RuntimeException("秒杀活动不存在");
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
|
||
// 检查活动状态,只有未开始的活动才能修改
|
||
if (flashSale.getStatus() != 1) {
|
||
throw new RuntimeException("只有未开始的秒杀活动才能修改");
|
||
}
|
||
|
||
// 更新字段
|
||
if (updateDTO.getFlashPrice() != null) {
|
||
flashSale.setFlashPrice(updateDTO.getFlashPrice());
|
||
}
|
||
if (updateDTO.getFlashStock() != null) {
|
||
flashSale.setFlashStock(updateDTO.getFlashStock());
|
||
}
|
||
if (updateDTO.getStartTime() != null) {
|
||
if (updateDTO.getStartTime().isBefore(LocalDateTime.now())) {
|
||
throw new RuntimeException("开始时间不能早于当前时间");
|
||
}
|
||
flashSale.setStartTime(updateDTO.getStartTime());
|
||
}
|
||
if (updateDTO.getEndTime() != null) {
|
||
flashSale.setEndTime(updateDTO.getEndTime());
|
||
}
|
||
if (updateDTO.getStatus() != null) {
|
||
flashSale.setStatus(updateDTO.getStatus());
|
||
}
|
||
|
||
// 验证时间
|
||
if (flashSale.getStartTime().isAfter(flashSale.getEndTime())) {
|
||
throw new RuntimeException("开始时间不能晚于结束时间");
|
||
}
|
||
|
||
// 保存更新
|
||
flashSale = flashSaleRepository.save(flashSale);
|
||
|
||
// 更新缓存
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
cacheFlashSaleInfo(flashSale, product);
|
||
|
||
// 更新Redis库存
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
|
||
redisService.set(stockKey, flashSale.getFlashStock());
|
||
|
||
log.info("秒杀活动更新成功: ID={}", flashSale.getId());
|
||
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
}
|
||
|
||
/**
|
||
* 删除秒杀活动
|
||
*/
|
||
@Transactional
|
||
public boolean deleteFlashSale(Long flashSaleId) {
|
||
log.info("删除秒杀活动: ID={}", flashSaleId);
|
||
|
||
// 获取现有秒杀活动
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
throw new RuntimeException("秒杀活动不存在");
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
|
||
// 检查活动状态,只有未开始的活动才能删除
|
||
if (flashSale.getStatus() != 1) {
|
||
throw new RuntimeException("只有未开始的秒杀活动才能删除");
|
||
}
|
||
|
||
// 检查是否有相关订单
|
||
if (orderRepository.existsFlashSaleOrder(null, flashSale.getProductId())) {
|
||
throw new RuntimeException("该秒杀活动已有订单,无法删除");
|
||
}
|
||
|
||
// 删除秒杀活动
|
||
flashSaleRepository.deleteById(flashSaleId);
|
||
|
||
// 清除相关缓存
|
||
clearFlashSaleCache(flashSaleId);
|
||
|
||
log.info("秒杀活动删除成功: ID={}", flashSaleId);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 发布秒杀活动
|
||
*/
|
||
@Transactional
|
||
public FlashSaleDTO publishFlashSale(Long flashSaleId) {
|
||
log.info("发布秒杀活动: ID={}", flashSaleId);
|
||
|
||
// 获取现有秒杀活动
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
throw new RuntimeException("秒杀活动不存在");
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
|
||
// 检查活动状态
|
||
if (flashSale.getStatus() != 1) {
|
||
throw new RuntimeException("只有未开始的秒杀活动才能发布");
|
||
}
|
||
|
||
// 验证时间
|
||
LocalDateTime now = LocalDateTime.now();
|
||
if (flashSale.getStartTime().isBefore(now)) {
|
||
throw new RuntimeException("开始时间不能早于当前时间");
|
||
}
|
||
if (flashSale.getStartTime().isAfter(flashSale.getEndTime())) {
|
||
throw new RuntimeException("开始时间不能晚于结束时间");
|
||
}
|
||
|
||
// 验证商品存在
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
if (product == null) {
|
||
throw new RuntimeException("关联商品不存在");
|
||
}
|
||
|
||
// 验证库存
|
||
if (flashSale.getFlashStock() <= 0) {
|
||
throw new RuntimeException("秒杀库存必须大于0");
|
||
}
|
||
|
||
// 预热缓存
|
||
preloadFlashSale(flashSaleId);
|
||
|
||
log.info("秒杀活动发布成功: ID={}", flashSaleId);
|
||
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
}
|
||
|
||
/**
|
||
* 暂停秒杀活动
|
||
*/
|
||
@Transactional
|
||
public FlashSaleDTO pauseFlashSale(Long flashSaleId) {
|
||
log.info("暂停秒杀活动: ID={}", flashSaleId);
|
||
|
||
// 获取现有秒杀活动
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
throw new RuntimeException("秒杀活动不存在");
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
|
||
// 检查活动状态
|
||
if (flashSale.getStatus() != 2) {
|
||
throw new RuntimeException("只有进行中的秒杀活动才能暂停");
|
||
}
|
||
|
||
// 更新状态为暂停 (status = 4)
|
||
flashSaleRepository.updateStatus(flashSaleId, 4);
|
||
flashSale.setStatus(4);
|
||
|
||
// 更新缓存
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
cacheFlashSaleInfo(flashSale, product);
|
||
|
||
log.info("秒杀活动暂停成功: ID={}", flashSaleId);
|
||
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
}
|
||
|
||
/**
|
||
* 恢复秒杀活动
|
||
*/
|
||
@Transactional
|
||
public FlashSaleDTO resumeFlashSale(Long flashSaleId) {
|
||
log.info("恢复秒杀活动: ID={}", flashSaleId);
|
||
|
||
// 获取现有秒杀活动
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
throw new RuntimeException("秒杀活动不存在");
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
|
||
// 检查活动状态
|
||
if (flashSale.getStatus() != 4) {
|
||
throw new RuntimeException("只有暂停的秒杀活动才能恢复");
|
||
}
|
||
|
||
// 检查是否已结束
|
||
LocalDateTime now = LocalDateTime.now();
|
||
if (flashSale.getEndTime().isBefore(now)) {
|
||
throw new RuntimeException("秒杀活动已结束,无法恢复");
|
||
}
|
||
|
||
// 更新状态为进行中 (status = 2)
|
||
flashSaleRepository.updateStatus(flashSaleId, 2);
|
||
flashSale.setStatus(2);
|
||
|
||
// 更新缓存
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
cacheFlashSaleInfo(flashSale, product);
|
||
|
||
log.info("秒杀活动恢复成功: ID={}", flashSaleId);
|
||
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
}
|
||
|
||
/**
|
||
* 结束秒杀活动
|
||
*/
|
||
@Transactional
|
||
public FlashSaleDTO endFlashSale(Long flashSaleId) {
|
||
log.info("结束秒杀活动: ID={}", flashSaleId);
|
||
|
||
// 获取现有秒杀活动
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (!flashSaleOpt.isPresent()) {
|
||
throw new RuntimeException("秒杀活动不存在");
|
||
}
|
||
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
|
||
// 检查活动状态
|
||
if (flashSale.getStatus() == 3) {
|
||
throw new RuntimeException("秒杀活动已经结束");
|
||
}
|
||
if (flashSale.getStatus() == 1) {
|
||
throw new RuntimeException("秒杀活动尚未开始,无法结束");
|
||
}
|
||
|
||
// 更新状态为已结束 (status = 3)
|
||
flashSaleRepository.updateStatus(flashSaleId, 3);
|
||
flashSale.setStatus(3);
|
||
|
||
// 清除相关缓存
|
||
clearFlashSaleCache(flashSaleId);
|
||
|
||
// 更新缓存
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
cacheFlashSaleInfo(flashSale, product);
|
||
|
||
log.info("秒杀活动结束成功: ID={}", flashSaleId);
|
||
|
||
return buildFlashSaleDTO(flashSale, product);
|
||
}
|
||
|
||
/**
|
||
* 更新秒杀活动状态
|
||
*/
|
||
@Transactional
|
||
public void updateFlashSaleStatus() {
|
||
LocalDateTime now = LocalDateTime.now();
|
||
|
||
// 更新未开始的活动为进行中
|
||
List<FlashSale> upcomingFlashSales = flashSaleRepository.findUpcomingFlashSales(now);
|
||
for (FlashSale flashSale : upcomingFlashSales) {
|
||
if (flashSale.isStarted() && !flashSale.isEnded()) {
|
||
flashSaleRepository.updateStatus(flashSale.getId(), 2);
|
||
log.info("秒杀活动开始: {}", flashSale.getId());
|
||
}
|
||
}
|
||
|
||
// 更新进行中的活动为已结束
|
||
List<FlashSale> activeFlashSales = flashSaleRepository.findActiveFlashSales(now);
|
||
for (FlashSale flashSale : activeFlashSales) {
|
||
if (flashSale.isEnded()) {
|
||
flashSaleRepository.updateStatus(flashSale.getId(), 3);
|
||
log.info("秒杀活动结束: {}", flashSale.getId());
|
||
|
||
// 清除相关缓存
|
||
clearFlashSaleCache(flashSale.getId());
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据ID获取秒杀活动实体
|
||
*/
|
||
private FlashSale getFlashSaleById(Long flashSaleId) {
|
||
// 先从缓存获取
|
||
String cacheKey = FLASH_SALE_CACHE_PREFIX + flashSaleId;
|
||
Map<Object, Object> flashSaleMap = redisService.hGetAll(cacheKey);
|
||
|
||
if (!flashSaleMap.isEmpty()) {
|
||
FlashSale flashSale = new FlashSale();
|
||
flashSale.setId(flashSaleId);
|
||
flashSale.setProductId(Long.valueOf((String) flashSaleMap.get("productId")));
|
||
flashSale.setFlashPrice(new BigDecimal((String) flashSaleMap.get("flashPrice")));
|
||
flashSale.setFlashStock(Integer.valueOf((String) flashSaleMap.get("flashStock")));
|
||
flashSale.setStartTime(LocalDateTime.parse((String) flashSaleMap.get("startTime")));
|
||
flashSale.setEndTime(LocalDateTime.parse((String) flashSaleMap.get("endTime")));
|
||
flashSale.setStatus(Integer.valueOf((String) flashSaleMap.get("status")));
|
||
return flashSale;
|
||
}
|
||
|
||
// 缓存中没有,从数据库获取
|
||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||
if (flashSaleOpt.isPresent()) {
|
||
FlashSale flashSale = flashSaleOpt.get();
|
||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||
cacheFlashSaleInfo(flashSale, product);
|
||
return flashSale;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 缓存秒杀活动信息
|
||
*/
|
||
private void cacheFlashSaleInfo(FlashSale flashSale, Product product) {
|
||
String cacheKey = FLASH_SALE_CACHE_PREFIX + flashSale.getId();
|
||
Map<String, Object> flashSaleMap = new HashMap<>();
|
||
flashSaleMap.put("productId", flashSale.getProductId().toString());
|
||
flashSaleMap.put("flashPrice", flashSale.getFlashPrice().toString());
|
||
flashSaleMap.put("flashStock", flashSale.getFlashStock().toString());
|
||
flashSaleMap.put("startTime", flashSale.getStartTime().toString());
|
||
flashSaleMap.put("endTime", flashSale.getEndTime().toString());
|
||
flashSaleMap.put("status", flashSale.getStatus().toString());
|
||
|
||
if (product != null) {
|
||
flashSaleMap.put("productName", product.getName());
|
||
flashSaleMap.put("productPrice", product.getPrice().toString());
|
||
flashSaleMap.put("productImageUrl", product.getImageUrl());
|
||
}
|
||
|
||
redisService.hMSet(cacheKey, flashSaleMap);
|
||
redisService.expire(cacheKey, flashSaleCacheExpireMinutes, TimeUnit.MINUTES);
|
||
}
|
||
|
||
/**
|
||
* 构建秒杀活动DTO
|
||
*/
|
||
private FlashSaleDTO buildFlashSaleDTO(FlashSale flashSale, Product product) {
|
||
FlashSaleDTO dto = new FlashSaleDTO();
|
||
BeanUtils.copyProperties(flashSale, dto);
|
||
|
||
if (product != null) {
|
||
dto.setProductName(product.getName());
|
||
dto.setProductImageUrl(product.getImageUrl());
|
||
dto.setOriginalPrice(product.getPrice());
|
||
}
|
||
|
||
// 获取剩余库存
|
||
Integer remainingStock = getFlashSaleStock(flashSale.getId());
|
||
dto.setRemainingStock(remainingStock);
|
||
|
||
// 设置状态描述
|
||
LocalDateTime now = LocalDateTime.now();
|
||
if (flashSale.getStartTime().isAfter(now)) {
|
||
dto.setStatusDescription("未开始");
|
||
dto.setCanParticipate(false);
|
||
dto.setTimeToStart(
|
||
flashSale.getStartTime().toEpochSecond(ZoneOffset.of("+8")) * 1000 - System.currentTimeMillis());
|
||
} else if (flashSale.getEndTime().isBefore(now)) {
|
||
dto.setStatusDescription("已结束");
|
||
dto.setCanParticipate(false);
|
||
dto.setTimeToEnd(0L);
|
||
} else {
|
||
dto.setStatusDescription("进行中");
|
||
dto.setCanParticipate(remainingStock > 0);
|
||
dto.setTimeToEnd(
|
||
flashSale.getEndTime().toEpochSecond(ZoneOffset.of("+8")) * 1000 - System.currentTimeMillis());
|
||
}
|
||
|
||
return dto;
|
||
}
|
||
|
||
/**
|
||
* 验证排序字段
|
||
*/
|
||
private String validateSortField(String sortBy) {
|
||
if (sortBy == null || sortBy.trim().isEmpty()) {
|
||
return "startTime"; // 默认排序字段
|
||
}
|
||
|
||
// 允许的排序字段列表
|
||
switch (sortBy.toLowerCase()) {
|
||
case "starttime":
|
||
case "start_time":
|
||
return "startTime";
|
||
case "endtime":
|
||
case "end_time":
|
||
return "endTime";
|
||
case "createdat":
|
||
case "created_at":
|
||
return "createdAt";
|
||
case "id":
|
||
return "id";
|
||
case "flashprice":
|
||
case "flash_price":
|
||
return "flashPrice";
|
||
case "flashstock":
|
||
case "flash_stock":
|
||
return "flashStock";
|
||
case "status":
|
||
return "status";
|
||
default:
|
||
log.warn("无效的排序字段: {}, 使用默认排序字段: startTime", sortBy);
|
||
return "startTime";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建秒杀订单
|
||
*/
|
||
private Order createFlashSaleOrder(Long userId, FlashSale flashSale, FlashSaleDTO.ParticipateDTO participateDTO) {
|
||
Order order = new Order();
|
||
order.setOrderNo("FS" + System.currentTimeMillis() + String.format("%03d", new java.util.Random().nextInt(1000)));
|
||
order.setUserId(userId);
|
||
order.setProductId(flashSale.getProductId());
|
||
order.setQuantity(participateDTO.getQuantity());
|
||
order.setTotalPrice(flashSale.getFlashPrice().multiply(BigDecimal.valueOf(participateDTO.getQuantity())));
|
||
order.setStatus(1); // 待支付
|
||
order.setOrderType(2); // 秒杀订单
|
||
order.setReceiverPhone(participateDTO.getPhone());
|
||
order.setReceiverAddress(participateDTO.getAddress());
|
||
order.setRemark("秒杀订单");
|
||
|
||
return orderRepository.save(order);
|
||
}
|
||
|
||
/**
|
||
* 发布秒杀结果消息
|
||
*/
|
||
private void publishFlashSaleResult(Long userId, FlashSale flashSale, Order order, boolean success) {
|
||
Map<String, Object> message = new HashMap<>();
|
||
message.put("userId", userId);
|
||
message.put("flashSaleId", flashSale.getId());
|
||
message.put("productId", flashSale.getProductId());
|
||
message.put("success", success);
|
||
message.put("orderId", order != null ? order.getId() : null);
|
||
message.put("timestamp", System.currentTimeMillis());
|
||
|
||
redisService.publish("flashsale:result", message);
|
||
}
|
||
|
||
/**
|
||
* 创建成功结果
|
||
*/
|
||
private FlashSaleDTO.ResultDTO createSuccessResult(Order order, FlashSale flashSale) {
|
||
FlashSaleDTO.ResultDTO result = new FlashSaleDTO.ResultDTO();
|
||
result.setSuccess(true);
|
||
result.setMessage("秒杀成功");
|
||
result.setOrderId(order.getId());
|
||
result.setFlashSaleId(flashSale.getId());
|
||
result.setProductId(flashSale.getProductId());
|
||
result.setQuantity(order.getQuantity());
|
||
result.setTotalPrice(order.getTotalPrice());
|
||
result.setOrderTime(order.getCreatedAt());
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 创建失败结果
|
||
*/
|
||
private FlashSaleDTO.ResultDTO createFailResult(String message) {
|
||
FlashSaleDTO.ResultDTO result = new FlashSaleDTO.ResultDTO();
|
||
result.setSuccess(false);
|
||
result.setMessage(message);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 清除秒杀活动缓存
|
||
*/
|
||
private void clearFlashSaleCache(Long flashSaleId) {
|
||
String cacheKey = FLASH_SALE_CACHE_PREFIX + flashSaleId;
|
||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId;
|
||
String successUsersKey = FLASH_SALE_SUCCESS_USERS_PREFIX + flashSaleId;
|
||
|
||
redisService.delete(cacheKey);
|
||
redisService.delete(stockKey);
|
||
redisService.delete(successUsersKey);
|
||
redisService.delete(ACTIVE_FLASH_SALES);
|
||
}
|
||
}
|