以下是一个基于Java的手办盲盒商城系统中盲盒功能的核心源码实现示例,包含商品模型、盲盒引擎、用户交互及高并发优化策略:
一、商品模型(Item.java)
java
public class Item { private Long id; private String name; private String description; private Double blindBoxPrice; private String imageUrl; private Integer stock; // 构造方法、getter/setter省略 }二、盲盒类(BlindBox.java)
java
import java.util.List; import java.util.Random; public class BlindBox { private Long id; private List<Item> itemPool; // 奖品池 private String boxName; public BlindBox(Long id, List<Item> itemPool, String boxName) { this.id = id; this.itemPool = itemPool; this.boxName = boxName; } // 随机选择奖品(基础版) public Item openBox() { Random random = new Random(); return itemPool.get(random.nextInt(itemPool.size())); } // 权重分配算法(进阶版) public Item openBoxWithWeight() { // 实现Alias Method权重分配算法 // 示例:简化版按库存比例分配 int totalStock = itemPool.stream().mapToInt(Item::getStock).sum(); Random random = new Random(); int randValue = random.nextInt(totalStock); int currentSum = 0; for (Item item : itemPool) { currentSum += item.getStock(); if (randValue < currentSum) { return item; } } return null; } // getter/setter省略 }三、盲盒服务类(BlindBoxService.java)
java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class BlindBoxService { @Autowired private RedisTemplate<String, Integer> redisTemplate; // 分布式锁开箱(防止超卖) @Lock4j(keys = {"#boxId"}, expire = 5000, acquireTimeout = 3000) @Transactional public DrawResult openBlindBox(Long boxId, Long userId) { // 1. 从Redis校验库存 String stockKey = "stock:" + boxId; Integer currentStock = redisTemplate.opsForValue().get(stockKey); if (currentStock == null || currentStock <= 0) { throw new BusinessException("奖池已空"); } // 2. 执行开箱逻辑(调用权重算法) BlindBox blindBox = getBlindBoxById(boxId); Item prize = blindBox.openBoxWithWeight(); // 3. 扣减库存(Lua脚本保证原子性) String luaScript = "local key = KEYS[1]\n" + "local count = tonumber(ARGV[1])\n" + "local current = tonumber(redis.call('GET', key))\n" + "if current >= count then\n" + " redis.call('DECRBY', key, count)\n" + " return 1\n" + "else\n" + " return 0\n" + "end"; Boolean success = redisTemplate.execute( new DefaultRedisScript<>(luaScript, Boolean.class), Collections.singletonList(stockKey), String.valueOf(1) ); if (Boolean.FALSE.equals(success)) { throw new BusinessException("开箱失败,请重试"); } // 4. 生成订单(异步处理) mqProducer.sendOrderMessage(userId, boxId, prize.getId()); return new DrawResult(prize); } private BlindBox getBlindBoxById(Long boxId) { // 从数据库或缓存加载盲盒配置 // 示例代码省略 return null; } }四、高并发优化策略
分布式锁
使用@Lock4j注解结合Redisson实现接口级锁,防止同一用户重复开箱或超卖问题。Redis原子操作
通过Lua脚本实现库存扣减的原子性,避免竞态条件。异步处理
使用RocketMQ处理订单落库、通知等耗时操作,降低接口响应时间。缓存预热
系统启动时将盲盒配置和库存加载至Redis,减少数据库压力。限流策略
通过Sentinel对开箱接口进行QPS限流(如1000/秒),防止系统过载。
五、扩展功能实现
一番赏玩法
java// 终极奖品判断逻辑 public boolean isLastPrize(BlindBox box, Item prize) { return prize.getId().equals(box.getLastPrizeId()) && redisTemplate.opsForValue().get("stock:" + box.getId()) == 1; }动态概率调整
java// 根据库存比例调整权重 public void refreshWeights(BlindBox box) { int totalStock = box.getItemPool().stream().mapToInt(Item::getStock).sum(); box.getItemPool().forEach(item -> { double weight = (double) item.getStock() / totalStock; item.setWeight((int) (weight * 100)); // 转换为百分比 }); }防刷机制
java// 基于设备指纹的风控拦截 public boolean checkRisk(HttpServletRequest request) { String deviceId = generateDeviceFingerprint(request); Integer count = redisTemplate.opsForValue().get("draw:" + deviceId); return count != null && count > MAX_DRAW_PER_DAY; }