一、实现秒杀功能
1.1 数据库设计
1.2 商品列表页
1.3 商品详情页
1.4 订单详情页
1.5 流程
用户进入商品列表页,选中要秒杀的商品,点击详情进入商品详情页,点击秒杀,秒杀成功,进入订单详情页。
二、秒杀商品列表页
2.1 页面展示
2.2 商品列表页对应的数据实体
秒杀商品表有以一个主键,一个外键,需要把商品和用户分开再建立一个表。
//将Goods表和MiaoshaGoods表合并
public class GoodsVo extends Goods {
private Integer stockCount; //库存
private Date startDate;
private Date endDate;
private Double miaoshaPrice;
.......
}
2.3 查找所有商品
@Mapper
public interface GoodsDao {
@Select("select g.*,mg.stock_count,mg.start_date,mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id=g.id")
public List<GoodsVo> selectGoods();
}
2.4 查找所有商品的业务逻辑
查找所有商品返回列表页
public String to_login(HttpServletResponse response, Model model,/* @CookieValue(name = MiaoShaUserService.COOKIE_NAME, required = false)String token,
@PathVariable(name = MiaoShaUserService.COOKIE_NAME,required = false)String token1*/MiaoshaUser user){
//得不到任何的cookie信息,则说明未登陆过,返回到登录界面
/* if(StringUtils.isEmpty(token)&&StringUtils.isEmpty(token1)){
return "login";
}
String realToken = StringUtils.isEmpty(token1) ? token : token1;
MiaoshaUser byToken = miaoShaUserService.getByToken(token, response);
System.out.println("拿到token对应的user");*/
List<GoodsVo> goods = goodsService.selectAll();
System.out.println(goods.size());
model.addAttribute("goodsList",goods);
return "goods_list";
}
三、商品详情页
3.1 页面展示
3.2 页面展示逻辑
点击详情,前端会把商品对应的商品id传给后端,后端根据商品id的到对应的秒杀商品信息传给前端。进行展示。
@RequestMapping("/to_detail/{goodsId}")
public String goodsDetail(@PathVariable("goodsId")Long goodsId,Model model,MiaoshaUser miaoshaUser){
GoodsVo goodsVo = goodsService.seleteReferGoods(goodsId);
GoodsDetailVo goodsDetailVo = new GoodsDetailVo();
goodsDetailVo.setGoodsVo(goodsVo);
goodsDetailVo.setUser(miaoshaUser);
goodsDetailVo.setStatus(goodsVo.getStockCount());
System.out.println(goodsVo.getMiaoshaPrice() + " " + "库存" + goodsVo.getStockCount());
System.out.println("商品的要点:" + goodsDetailVo.getRemailSeconds() + " " + goodsDetailVo.getStatus());
long end = goodsVo.getEndDate().getTime();
long start = goodsVo.getStartDate().getTime();
long now = System.currentTimeMillis();
int remainSeconds = 0;
int miaoshastatus = 0;
//判断秒杀是否开始
if(start > now){
//秒杀未开始
miaoshastatus = 0;
remainSeconds = (int)(end - start)/1000;
}
else if(end <= now){
//秒杀已经结束
miaoshastatus = 2;
remainSeconds = -1;
}
else{
//秒杀正在进行
miaoshastatus = 1;
remainSeconds = (int)(end - now)/1000;
}
if(miaoshaUser == null){
System.out.println("秒杀对象为空");
}else {
System.out.println("秒杀对象不为空");
}
goodsDetailVo.setRemailSeconds(remainSeconds);
model.addAttribute("goods",goodsVo);
model.addAttribute("remainSeconds",remainSeconds);
model.addAttribute("user",miaoshaUser);
return "goods_detail";
}
3.3 秒杀是否开始
取现在的时间与秒杀开始时间和秒杀结束时间进行比较,将时间差传递给前端。倒计时等交给前端去处理。
- 秒杀未开始和秒杀结束情况下,秒杀按钮是不能点击的。
- 秒杀未开始会有一个倒计时提醒还有多长时间才能进行秒杀。
- 秒杀进行中提示是秒杀进行中。
function countDown() {
//获取秒杀倒计时进行判断,0-->正在进行秒杀,-1-->秒杀结束,remailSeconds>0-->代表倒计时
var remailSeconds = $("#remailSeconds").val();
//alert("remailSeconds:"+remailSeconds);
var timeout;
if (remailSeconds > 0) {//秒杀还没有开始,进行倒计时功能
$("#buyButton").attr("disabled", true);
$("#miaoshaTip").html("秒杀倒计时:" + remailSeconds + "秒");
//倒计时
timeout = setTimeout(function() {
$("#countDown").text(remailSeconds - 1);
$("#remailSeconds").val(remailSeconds - 1);//remailSeconds这是input
countDown();
}, 1000);//一秒钟之后回调函数
} else if (remailSeconds == 0) {//正在进行秒杀
$("#buyButton").attr("disabled", false);
if (timeout) {//如果timeout有值的情况
clearTimeout(timeout);
}
//将文案修改 df1fab4272a24cdf9432adb9fd69cb38
$("#miaoshaTip").html("秒杀进行中");
//添加验证码
$("#vertifyCodeImg").attr("src","/miaosha/vertifyCode?goodsId="+$("#goodsId").val());
$("#vertifyCodeImg").show();
$("#vertifyCode").show();
} else {
//小于0的情况,秒杀结束,将秒杀按钮设置为不可点击
$("#buyButton").attr("disabled", true);
$("#miaoshaTip").html("秒杀结束");
//隐藏验证码
$("#vertifyCodeImg").hide();
$("#vertifyCode").hide();
}
}
四、点击秒杀
4.1 判断用户是否登录,并根据goodsID得到对应的商品信息
根据请求所带的cookie,取到对应的session信息,再有sessionID从redis中取到对应的用户信息,如果能取到则证明用户已经登录,否则未登录。
@RequestMapping("/do_miaosha")
public String doMiaoSha(Model model, MiaoshaUser miaoshaUser, @RequestParam("goodsId")long goodsId){
//1.判断用户是否登录
if(miaoshaUser == null){
return "login";
}
GoodsVo goodsVo = goodsService.seleteReferGoods(goodsId);
......
}
4.2 判断商品的库存够不够
由上面拿到的对应的商品ID,从Goods表中得到对应商品的库存,判断库存是否支持秒杀。
//2.判断库存是否足够秒杀
Integer stockCount = goodsVo.getStockCount();
if(stockCount <= 0){
model.addAttribute("errmsg", CodeMsg.MIAOSHA_OVER_ERROR.getMsg());
return "miaosha_fail";
}
4.3 判断用户是不是重复秒杀
- 从redis中如果取到用户对应的秒杀信息,则证明用户已经秒杀过商品
- 从redis中取不到则从MiaoshaOrder表中去取,如果能取到则证明用户已经秒杀过商品
- 否则不是重复秒杀,每一步都需要把对应的信息添加到Model对象中
//3.判断是否重复秒杀
//先从缓存中取
OrderInfo orderInfo = null;
OrderInfo orderInfo1 = redisService.get(MiaoshaOrderKey.OrderByManyID, "" + miaoshaUser.getId() + goodsVo.getId(), OrderInfo.class);
if(orderInfo1 != null){
model.addAttribute("errmsg",CodeMsg.REPEATE_MIAOSHA.getMsg());
return "miaosha_fail";
}else{
orderInfo = orderService.getOrderInfo(miaoshaUser.getId(), goodsVo.getId());
}
if(orderInfo != null){
model.addAttribute("errmsg",CodeMsg.REPEATE_MIAOSHA.getMsg());
return "miaosha_fail";
}
4.4 进行秒杀
4.4.1 减少库存
需要减少Goods表中的库存,和MiaoshaGoods表中的库存。
//减少库存,Goods表的库存得减少,MiaoshaGoods表的库存也得减少
goodsVo.setStockCount(goodsVo.getStockCount() - 1);
System.out.println("步骤3.1");
goodsService.reduceGoodsStock(goodsVo.getId());
System.out.println("步骤3.2");
miaoshaGoodsService.reduceMiaoshaGoodsCount(goodsVo.getId());
System.out.println("步骤3.3");
4.4.2 将秒杀商品的信息存到OrderInfo表中和Miaosha_order表中
//根据MiaoshaUser和goodsvo生成OrderInfor对象的信息,并将OrderInfo信息插入到Order_Info表中,将MiaoshaOrder信息插入到miaosha_order表中。
public OrderInfo creatOrder(MiaoshaUser user, GoodsVo goodsvo) {
//1.生成order_info
OrderInfo orderInfo=new OrderInfo();
orderInfo.setDeliveryAddrId(0L);//long类型 private Long deliveryAddrId; L
orderInfo.setCreateDate(new Date());
orderInfo.setGoodsCount(1);
orderInfo.setGoodsId(goodsvo.getId());
//秒杀价格
orderInfo.setGoodsPrice(goodsvo.getMiaoshaPrice());
orderInfo.setOrderChannel(1);
//订单状态 ---0-新建未支付 1-已支付 2-已发货 3-已收货
orderInfo.setOrderStatus(0);
//用户id
orderInfo.setUserId(user.getId());
//返回orderId
//long orderId=
insertOrderInfo(orderInfo);
System.out.println("步骤4.1");
//2.生成miaosha_order
MiaoshaOrder miaoshaorder =new MiaoshaOrder();
miaoshaorder.setGoodsId(goodsvo.getId());
//将订单id传给秒杀订单里面的订单orderid
miaoshaorder.setOrderId(orderInfo.getId());
miaoshaorder.setUserId(user.getId());
System.out.println("步骤5.1");
insertMiaoshaOrder(miaoshaorder);
System.out.println("步骤5.2");
return orderInfo;
}
4.5 秒杀完成,将秒杀信息存到缓存中
秒杀信息的key是用户ID和GoodsID,因为每个客户只能进行一次秒杀
//5.秒杀成功,存入redis中
redisService.set(MiaoshaOrderKey.OrderByManyID,""+miaoshaUser.getId() + goodsVo.getId(),orderInfo);
4.6 返回订详情页
五、订单详情页
5.1 页面展示
5.2 对应的封装类
public class OrderInfo {
private Long id;
private Long userId;//用户
private Long goodsId;
private Long deliveryAddrId;//收货地址
private String goodsName;//商品名称
private Integer goodsCount;//商品下单数量
private Double goodsPrice;//下单价格
private Integer orderChannel;//下单渠道
private Integer orderStatus;//下单状态,0:未支付,1:已支付,2:已发货,3:已收货,4:已退款,5:已完成
private Date createDate;//下单时间
private Date payDate;//支付时间
}
5.3 MiaoshaOrder表对应的封装类
public class MiaoshaOrder {
private Long id;
private Long userId;
private Long orderId;//订单ID
private Long goodsId;//商品ID