一、秒杀倒计时
1.1 跳转逻辑
在跳转到详情页面的时候,对比秒杀开始时间、结束时间与当前时间。在model中传入秒杀状态与倒计时时间。
在前端页面根据秒杀状态与倒计时的状态进行不同的显示。
/**
* 跳转商品详情页
* @author 47roro
* @date 2022/4/15
* @param goodsId
* @return java.lang.String
**/
@RequestMapping("/toDetail/{goodsId}")
public String toDetail(Model model, User user,@PathVariable Long goodsId){
model.addAttribute("user", user);
GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
Date startDate = goodsVo.getStartDate();
Date endDate = goodsVo.getEndDate();
Date nowDate = new Date();
int secKillStatus = 0;
int remainSeconds = 0;
if(nowDate.before(startDate)){
remainSeconds = (int)((startDate.getTime() - nowDate.getTime()) / 1000);
}else if(nowDate.after(endDate)){
secKillStatus = 2;
remainSeconds = -1;
}else {
secKillStatus = 1;
}
model.addAttribute("secKillStatus", secKillStatus);
model.addAttribute("remainSeconds", remainSeconds);
model.addAttribute("goods", goodsService.findGoodsVoByGoodsId(goodsId));
return "goodsDetail";
}
1.2 测试
二、秒杀按钮
1.1 按钮事件
因为要进行事件的处理,做成form表单提交的方式。
<form id="secKillForm" method="post" action="/seckill/doSeckill">
<input type="hidden" name="goodsId" th:value="${goods.id}">
<button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
</form>
根据秒杀状态对按钮进行disabled
处理。
结合倒计时进行设置:
$(function () {
countDown();
});
function countDown() {
var remainSeconds = $("#remainSeconds").val();
var timeout;
//秒杀还未开始
if (remainSeconds > 0) {
$("#buyButton").attr("disabled", true);
timeout = setTimeout(function () {
$("#countDown").text(remainSeconds - 1);
$("#remainSeconds").val(remainSeconds - 1);
countDown();
}, 1000);
// 秒杀进行中
} else if (remainSeconds == 0) {
$("#buyButton").attr("disabled", false);
if (timeout) {
clearTimeout(timeout);
}
$("#seckillTip").html("秒杀进行中")
} else {
$("#buyButton").attr("disabled", true);
$("#seckillTip").html("秒杀已经结束");
}
};
1.2 测试
三、秒杀功能实现
3.1 秒杀跳转页面
首先判断用户是否登录,未登录则跳转至登录页面login
。
然后获取秒杀商品信息。
判断库存是否充足以及是否重复抢购,若不足或者重复抢购,则抛出异常。
若没有异常,则创建新订单,并跳转至订单详情页面。
/**
* 秒杀
* @author 47roro
* @date 2022/4/16
* @param model
* @param user
* @param goodsId
* @return java.lang.String
**/
@RequestMapping("/doSecKill")
public String doSecKill(Model model, User user, Long goodsId){
if(user == null){
return "login";
}
model.addAttribute("user", user);
GoodsVo goods = goodsService.findGoodsVoByGoodsId(goodsId);
//判断库存
if(goods.getStockCount() < 1){
model.addAttribute("errmsg", RespBeanEnum.EMPT_STOCK.getMessage());
return "secKillFail";
}
//判断是否重复抢购(mybatis plus)
SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id", goodsId));
if(seckillOrder != null){
model.addAttribute("errmsg", RespBeanEnum.REPEAT_ERROR.getMessage());
}
Order order = orderService.seckill(user, goods);
model.addAttribute("order", order);
model.addAttribute("goods", goods);
return "orderDetail";
}
3.2 秒杀订单接口
public interface IOrderService extends IService<Order> {
/**
* 秒杀订单
* @author 47roro
* @date 2022/4/16
* @param user
* @param goods
* @return com.example.seckill.pojo.Order
**/
Order seckill(User user, GoodsVo goods);
}
3.3 秒杀订单具体实现
减少库存,分别创建订单和秒杀订单。(暂时未考虑超卖问题)
/**
* 秒杀订单具体实现
* @author 47roro
* @date 2022/4/16
* @param user
* @param goods
* @return com.example.seckill.pojo.Order
**/
@Override
public Order seckill(User user, GoodsVo goods) {
//秒杀商品表减库存
SeckillGoods seckillGoods = seckillGoodsService.getOne(new QueryWrapper<SeckillGoods>().eq("goods_id", goods.getId()));
seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);
seckillGoodsService.updateById(seckillGoods);
//生成订单
Order order = new Order();
order.setUserId(user.getId());
order.setGoodsId(goods.getId());
order.setDeliveryAddrId(0L);
order.setGoodsName(goods.getGoodsName());
order.setGoodsCount(1);
order.setGoodsPrice(seckillGoods.getSeckillPrice());
order.setOrderChannel(1);
order.setStatus(0);
order.setCreateDate(new Date());
orderMapper.insert(order);
//生成秒杀订单
SeckillOrder seckillOrder = new SeckillOrder();
seckillOrder.setOrderId(order.getId());
seckillOrder.setUserId(user.getId());
seckillOrder.setGoodsId(goods.getId());
seckillOrderService.save(seckillOrder);
return order;
}
3.4 订单详情页面(测试)
总结
目前只是做了简单的CRUD操作,后续会进行加锁、消息队列等优化。