SSM秒杀项目(六)秒杀模块

秒杀页面展示:

 

 第一次点击秒杀,减库存下订单,再次点击造成重复秒杀,无法购买。

秒杀逻辑:

①为秒杀按钮绑定点击事件获取秒杀地址----------οnclick="getSeckillPath()",当点击秒杀按钮,通过ajax异步提交获取秒杀地址;

   //获取秒杀地址
   function getSeckillPath(){
      var goodsId = $("#goodsId").val();
      $.ajax({
        url:"/seckill_ssm/"+goodsId+"/getPath",
        type:"GET",
        success:function(result){
           if (result.code == 0){
              //获取秒杀path
              var path = result.data;
              domiaosha(path);//在服务端对path验证成功后才执行秒杀
           }else{
              layer.msg(result.msg);
           }
        },
        error:function(){
           layer.msg("请求不合法");
        }
      });
   }

具体步骤为:

 根据路径参数找到对应的Controller获取秒杀Path返回,秒杀Path其实就是一个随机串,主要是对随机生成的UUID做一次MD5计算然后将Path写入redis缓存中。如果ajax请求返回的信息码是0,表明秒杀Path成功,否则提示用户错误信息。

@RequestMapping("/{goodsId}/getPath")
	@ResponseBody
	public Result<String> getMiaoShaPath(Model model, 
        @CookieValue(value=UserServiceImpl.COOKI_NAME_TOKEN, required=false) String 
          cookieToken, HttpServletResponse response,@PathVariable("goodsId")long goodsId){
		User user = userService.getByToken(response, cookieToken);
		//如果用户为空,不能进行秒杀,返回登录界面
		if (user == null){
			return Result.error(CodeMsg.NAME_EMPTY);
		}
		//生成随机串秒杀Path
		String path = seckillService.createSeckillPath(user,goodsId);
		log.info("path:"+path);
		return Result.success(path);
	}



秒杀Path获取函数
public String createSeckillPath(User user, long goodsId) {
		String path = Md5Util.md5(UUIDutil.uuid()+"123456");
		redisService.set(MiaoshaKey.getSeckillPath,""+user.getId()+"_"+goodsId, path);
		return path;
	}

②将秒杀Path提交给服务端进行验证,验证通过后执行真正的秒杀操作。

function domiaosha(path){
      $.ajax({
        url:"/seckill_ssm/"+path+"/do_seckill",
        type:"POST",
        data:{goodsId:$("#goodsId").val()},
        success:function(result){
            if (result.code ==0){
                getMiaoShaResult($("#goodsId").val());
            }else{
               //显示信息
               layer.msg(result.msg);
            }
        }
      });
   }

③秒杀业务逻辑

 a.首先从redis中预减对应商品的库存,返回的是剩下的库存stock,随后对stock进行一个判断,如果stock<0,则表示库存不足,直接返回库存不足的信息。

 b.查询数据库中是否已经有对应的用户id和商品id的秒杀单形成,如果有则表明此次秒杀为重复下单,否则将此次秒杀的用户信息和商品id封装起来发送到队列中。

c.队列消费者监听队列,有任务后开始消费(处理)。从队列中拿到之前封装的对象,开始进行减库存、下订单操作。

d.首先仍然是判断库存是否充足和是否重复下单行为。其后进行减库存、下订单的原子操作,需要加上事务,以便在其中一项操作未能成功时进行事务回滚。如果库存更新成功,则创建订单,返回订单,同时将订单存入redis缓存中。

e.与此同时,客户端对返回的信息进行处理,分为秒杀失败、排队中(消息队列正在处理秒杀业务)、订单生成成功。对于排队中客户端需要进行轮询,直到秒杀订单形成。

function getMiaoShaResult(goodsId){
      $.ajax({
         url:"/seckill_ssm/getResult",
         type:"GET",
         data:{goodsId:$("#goodsId").val()},
         success:function(result){
             if (result.code == 0){
                var data = result.data;
                if (data < 0){
                   layer.msg("抱歉,秒杀失败");
                }else if(data == 0){
                   //继续轮询
                   setTimeout(function(){
                      getMiaoShaResult(goodsId);
                   },50);//50ms轮询一次
                   //layer.msg(result.msg);
                   
                }else{
                   layer.confirm("恭喜你,秒杀成功!查看订单?",{btn:["确定","取消"]},
                         function(){
                            //确定跳转订单详情页面
                            alert(goodsId);
                            window.location.href="/seckill_ssm/order_detail? 
                            goodsId="+goodsId;
                         },
                         
                         function(){
                           layer.closeAll();
                         }
                   );
                   
                }
             }
         },
         error:function(){
				layer.msg("请求有误!");
		  }
         
      });
   }
    @RequestMapping("/{path}/do_seckill")
	@ResponseBody
	public Result<Integer> creatOrderDetail(Model model, 
    @CookieValue(value=UserServiceImpl.COOKI_NAME_TOKEN,required=false)String 
    cookieToken, HttpServletResponse response,@RequestParam("goodsId")long 
    goodsId,@PathVariable("path") String path){
		//1根据token从缓存中取用户登录信息
		User user = userService.getByToken(response, cookieToken);
		//如果用户为空,不能进行秒杀,返回登录界面
		if (user == null){
			return Result.error(CodeMsg.NAME_EMPTY);//code 3; msg 用户名为空
		}
		//验证path地址
		boolean isPathCorrect = seckillService.CheckPath(user.getId(),goodsId,path);
		if (!isPathCorrect)
			return Result.error(CodeMsg.REQUEST_ILLGEAL);
		//2预减redis缓存中的库存(什么时候加到redis的缓存中的?)这里是减了之后剩余的库存
		long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock,""+goodsId);
		System.out.println("stock:"+stock);//这里有个问题:同一用户可以重复减库存
		//3.判断减少缓存中库存是否小于0
		if (stock < 0){
			return Result.error(CodeMsg.ORDER_ERROR);//库存不足
		}
		//4.秒杀单是否形成,避免重复秒杀
		MiaoshaOrder order = orderService.getMiaoshaOrderById(user.getId(), goodsId);
		if (order != null){
			//重复下单
			return Result.error(CodeMsg.ORDER_REPEATE);
		}
		//5.将请求入队列,
		MiaoShaMessage  mms = new MiaoShaMessage();
		mms.setGoodsId(goodsId);
		mms.setUser(user);
		mQSender.sendMiaoShaMessage(mms);
		return Result.success(0);
	}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值