大众点评项目 订单功能 秒杀基本环境
SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则
- 基础+实战的Demo和Coding上传到我的代码仓库
- 在原有基础上加入一些设计模式,stream+lamdba等新的糖
- 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式
代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Redis优化-链接: RedisLightningDealsProject
需求:订单功能 秒杀基本环境
商品分析
在点评项目中,存在了优惠券这种商品,优惠券分成了两种
- 普通优惠券:无限量抢购,无时间限制
- 秒杀优惠券:限量,限时
分析表结构
这里是基本优惠券表
这里是秒杀优惠券表,本身就是基本优惠券,多了一些字段
Redis实现全局唯一ID
@Component
public class RedisIDProductor {
/**
* 我的当前时间号
*/
private static final long BEGIN_TIMESTAMP = 1670847120L;
/**
* 序列号位数
*/
private static final int COUNT_BITS = 32;
注入Redis
@Resource
private StringRedisTemplate stringRedisTemplate;
public RedisIDProductor(StringRedisTemplate stringRedisTemplate){
this.stringRedisTemplate = stringRedisTemplate;
}
通过Redis自增id创建,这里分成三个步骤
- 生成时间戳,就是时间差值,32位
- 生成Redis序列号,通过Redis的有序自增 ,32位
- 通过位运算进行拼接!
public long nextId(String keyPrefix){
// 生成时间戳,就是时间差值,32位
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
long timestamp = nowSecond - BEGIN_TIMESTAMP;
// 生成Redis序列号,通过Redis的有序自增 ,32位
String curTime = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
Long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + curTime);
// 通过位运算进行拼接!
return timestamp<<COUNT_BITS | count;
}
业务实现
先通过PostMan提交post请求,更新优惠券信息
{
"shopId":1,
"title":"100元代金券",
"subTitle":"周一至周日均可使用",
"rules":"全场通用\\n无需预约\\n可无限叠加\\不兑现、不找零\\n仅限堂食",
"payValue":8000,
"actualValue":10000,
"type":1,
"stock":100,
"beginTime":"2022-12-12T10:09:17",
"endTime":"2022-12-26T14:09:04"
}
DB查看优惠券信息
通过请求的URL区修改对应的方法
@Service
@Slf4j
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
@Resource
private ISeckillVoucherService seckillVoucherService;
@Resource
private RedisIDProductor redisIDProductor;
由于是对多个表的操作,
**@Transactional **保证了事务
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
// voucher:通过id去查到底是那种优惠券
SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
return Result.fail("秒杀尚未开始");
}
if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
return Result.fail("秒杀已经结束");
}
if (voucher.getStock() < 1) {
return Result.fail("库存不足");
}
这里是通过MP本身的API直接进行库存的增减
//验证结束,扣减库存
boolean flag = seckillVoucherService.update()
.setSql("stock = stock - 1")
.eq("voucher_id", voucherId)
.update();
if (!flag) {
return Result.fail("库存不足");
}
链式API封装订单信息,将其保存到订单表中
//封装订单
VoucherOrder voucherOrder = new VoucherOrder();
Long userId = UserHolder.getUser().getId();
long orderId = redisIDProductor.nextId("order");
//订单Id、用户Id、优惠券Id
voucherOrder.setId(orderId).setUserId(userId).setVoucherId(voucherId);
save(voucherOrder);
return Result.ok(orderId);
}
}
代码总览
@RestController
@RequestMapping("/voucher-order")
public class VoucherOrderController {
@Resource
private IVoucherOrderService voucherOrderService;
@PostMapping("seckill/{id}")
public Result seckillVoucher(@PathVariable("id") Long voucherId) {
return voucherOrderService.seckillVoucher(voucherId);
// return Result.fail("功能未完成");
}
}
public interface IVoucherOrderService extends IService<VoucherOrder> {
Result seckillVoucher(Long voucherId);
}
@Service
@Slf4j
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
@Resource
private ISeckillVoucherService seckillVoucherService;
@Resource
private RedisIDProductor redisIDProductor;
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
return Result.fail("秒杀尚未开始");
}
if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
return Result.fail("秒杀已经结束");
}
if (voucher.getStock() < 1) {
return Result.fail("库存不足");
}
//验证结束,扣减库存
boolean flag = seckillVoucherService.update()
.setSql("stock = stock - 1")
.eq("voucher_id", voucherId)
.update();
if (!flag) {
return Result.fail("库存不足");
}
//封装订单
VoucherOrder voucherOrder = new VoucherOrder();
Long userId = UserHolder.getUser().getId();
long orderId = redisIDProductor.nextId("order");
//订单Id、用户Id、优惠券Id
voucherOrder.setId(orderId).setUserId(userId).setVoucherId(voucherId);
save(voucherOrder);
return Result.ok(orderId);
}
}
@Service
public class VoucherServiceImpl extends ServiceImpl<VoucherMapper, Voucher> implements IVoucherService {
@Resource
private ISeckillVoucherService seckillVoucherService;
@Override
public Result queryVoucherOfShop(Long shopId) {
// 查询优惠券信息
List<Voucher> vouchers = getBaseMapper().queryVoucherOfShop(shopId);
// 返回结果
return Result.ok(vouchers);
}
@Override
@Transactional
public void addSeckillVoucher(Voucher voucher) {
// 保存优惠券
save(voucher);
// 保存秒杀信息
SeckillVoucher seckillVoucher = new SeckillVoucher();
seckillVoucher.setVoucherId(voucher.getId());
seckillVoucher.setStock(voucher.getStock());
seckillVoucher.setBeginTime(voucher.getBeginTime());
seckillVoucher.setEndTime(voucher.getEndTime());
seckillVoucherService.save(seckillVoucher);
}
}