SpringBoot秒殺系統實戰19-秒殺靜態化+訂單詳情靜態化

文章目錄

秒殺靜態化

改造商品詳情頁面的點擊秒殺的業務邏輯,我們調用js方法實現ajax異步發送消息,如果秒殺成功,那麼直接由客戶端去跳轉詳情頁面

window.location.href="order_detail.htm?orderId="+data.data.id;
function doMiaosha(){
		//alert("秒殺!");
		$.ajax({
			url:"/miaosha/do_miaosha",
			type:"POST",
			data:{
				goodsId:$("#goodsId").val()			
			},
			success:function(data){
				if(data.code==0){
					//秒殺成功,跳轉詳情頁面
					window.location.href="order_detail.htm?orderId="+data.data.id;		
				}else{
					layer.msg(data.msg);
				}
			},
			error:function(){
				layer.msg("請求有誤!");
			}
			//token如果cookie裏面有,會自己帶過去
			
		});
	}

改造後臺接收秒殺請求的doMiaosha方法接口,讓其不在去跳轉頁面了,而是直接返回包裝好的數據
原來我們是這樣寫的:

	@RequestMapping("/do_miaosha")//傳入user對象啊,不然怎麼取user的值,${user.nickname}
	public String toList(Model model,MiaoshaUser user,@RequestParam("goodsId") Long goodsId) {
		model.addAttribute("user", user);
		//如果用戶爲空,則返回至登錄頁面
		if(user==null){
			return "login";
		}
		GoodsVo goodsvo=goodsService.getGoodsVoByGoodsId(goodsId);
		//判斷商品庫存,庫存大於0,才進行操作,多線程下會出錯
		int  stockcount=goodsvo.getStockCount();		
		if(stockcount<=0) {//失敗			庫存至臨界值1的時候,此時剛好來了加入10個線程,那麼庫存就會-10
			model.addAttribute("errorMessage", CodeMsg.MIAOSHA_OVER_ERROR);
			return "miaosha_fail";
		}
		//判斷這個秒殺訂單形成沒有,判斷是否已經秒殺到了,避免一個賬戶秒殺多個商品 
		MiaoshaOrder order=orderService.getMiaoshaOrderByUserIdAndCoodsId(user.getId(),goodsId);
		if(order!=null) {//重複下單
			model.addAttribute("errorMessage", CodeMsg.REPEATE_MIAOSHA);
			return "miaosha_fail";
		}
		//可以秒殺,原子操作:1.庫存減1,2.下訂單,3.寫入秒殺訂單--->是一個事務
		OrderInfo orderinfo=miaoshaService.miaosha(user,goodsvo);
		//如果秒殺成功,直接跳轉到訂單詳情頁上去。
		model.addAttribute("orderinfo", orderinfo);
		model.addAttribute("goods", goodsvo);
		return "order_detail";//返回頁面login
	}

上面返回的是訂單詳情頁面,但是現在我們通過json返回給我們的前臺,秒殺成功則返回訂單信息,不成功返回相應的數據信息,現在我們的代碼如下:

	/**
	 * 
	 * 做了頁面靜態化的,直接返回訂單的信息
	 */
	//POST請求 
	@RequestMapping(value="/do_miaosha",method=RequestMethod.POST)
	@ResponseBody
	public Result<OrderInfo> doMiaosha(Model model,MiaoshaUser user,@RequestParam(value="goodsId",defaultValue="0") long goodsId) {
		model.addAttribute("user", user);
		//如果用戶爲空,則返回至登錄頁面
		if(user==null){
			return Result.error(CodeMsg.SESSION_ERROR);
		}
		GoodsVo goodsvo=goodsService.getGoodsVoByGoodsId(goodsId);
		//判斷商品庫存,庫存大於0,才進行操作,多線程下會出錯
		int  stockcount=goodsvo.getStockCount();		
		if(stockcount<=0) {//失敗			庫存至臨界值1的時候,此時剛好來了加入10個線程,那麼庫存就會-10
			//model.addAttribute("errorMessage", CodeMsg.MIAOSHA_OVER_ERROR);
			return Result.error(CodeMsg.MIAOSHA_OVER_ERROR);
		}
		//判斷這個秒殺訂單形成沒有,判斷是否已經秒殺到了,避免一個賬戶秒殺多個商品 
		MiaoshaOrder order=orderService.getMiaoshaOrderByUserIdAndGoodsId(user.getId(),goodsId);
		if(order!=null) {//重複下單
			//model.addAttribute("errorMessage", CodeMsg.REPEATE_MIAOSHA);
			return Result.error(CodeMsg.REPEATE_MIAOSHA);
		}
		//可以秒殺,原子操作:1.庫存減1,2.下訂單,3.寫入秒殺訂單--->是一個事務
		OrderInfo orderinfo=miaoshaService.miaosha(user,goodsvo);
		//如果秒殺成功,直接跳轉到訂單詳情頁上去。
		model.addAttribute("orderinfo", orderinfo);
		model.addAttribute("goods", goodsvo);
		return Result.success(orderinfo);
	}

訂單詳情靜態化

當秒殺成功之後,由客戶端直接跳轉至靜態訂單詳情頁面,與之前一樣,初始化執行方法getOrderDetail,發起ajax請求獲取數據來渲染我們的靜態頁面

$(function(){
		getOrderDetail();
	});

getOrderDetail方法:

	function getOrderDetail() {
		//取參數orderId
		var orderId=getQueryString("orderId");
		$.ajax({
			url : "/order/detail",
			type : "GET",
			data : {
				orderId :orderId
			},
			success : function(data) {
				if (data.code == 0) {
					render(data.data);
				} else {
					layer.msg(data.msg);
				}
			},
			error : function() {
				layer.msg("請求有誤!");
			}
		});
	}

render方法和getQueryString方法:

//渲染頁面--------5-17
	function render(detail){
		//alert(detail.status);
		var goods=detail.goodsVo;
		var order=detail.order;
		$("#goodsName").text(goods.goodsName);
		$("#goodsImg").attr("src",goods.goodsImg); 
		$("#goodsPrice").text(order.goodsPrice);	
		$("#createDate").text(order.createDate);
		//判斷訂單的狀態orderStatus
		var status="";
		if(order.orderStatus==0){
			$("#orderStatus").text("未支付");
		}else if(order.orderStatus==1){
			$("#orderStatus").text("待發貨");
		}else if(order.orderStatus==2){
			$("#orderStatus").text("已發貨");
		}else if(order.orderStatus==3){
			$("#orderStatus").text("待收貨");
		}
		
	}
		
	//獲取請求路徑裏面的參數
	function getQueryString(name){
		var reg=new RegExp("(^|&)"+name+"=([^&]*)(&|$)");
		var r=window.location.search.substr(1).match(reg);
		if(r!=null){
			return unescape(r[2]);
		}
		return null;
	}

OrderDetailVo封裝來專門給頁面傳值(json信息):

public class OrderDetailVo {
private GoodsVo goodsVo;
private OrderInfo order;
public GoodsVo getGoodsVo() {
    return goodsVo;
}
public void setGoodsVo(GoodsVo goodsVo) {
    this.goodsVo = goodsVo;
}
public OrderInfo getOrder() {
    return order;
}
public void setOrder(OrderInfo order) {
    this.order = order;
}
}

後臺OrderController裏面接收訂單詳情請求的接口代碼:

@RequestMapping("/order")
@Controller
public class OrderController {
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;

@RequestMapping("/detail") 
@ResponseBody
public Result<OrderDetailVo> info(Model model,MiaoshaUser user,
        @RequestParam("orderId") long orderId) {
    if(user==null) {
        return Result.error(CodeMsg.SESSION_ERROR);
    }
    OrderInfo order=orderService.getOrderByOrderId(orderId);
    if(order==null) {
        return Result.error(CodeMsg.ORDER_NOT_EXIST);
    }
    //訂單存在的情況
    long goodsId=order.getGoodsId();
    GoodsVo gVo=goodsService.getGoodsVoByGoodsId(goodsId);
    OrderDetailVo oVo=new OrderDetailVo();
    oVo.setGoodsVo(gVo);
    oVo.setOrder(order);
    return Result.success(oVo);//返回頁面login
}   
}

GoodsService 代碼:

@Service
public class GoodsService {
public static final String COOKIE1_NAME_TOKEN="token";  
@Autowired
GoodsDao goodsDao;
@Autowired
RedisService redisService;  
public List<GoodsVo>  getGoodsVoList() {
    return goodsDao.getGoodsVoList();
}   
public GoodsVo getGoodsVoByGoodsId(long goodsId) {
    return goodsDao.getGoodsVoByGoodsId(goodsId);
}   
public void reduceStock(GoodsVo goodsvo) {
    MiaoshaGoods goods=new MiaoshaGoods();
    goods.setGoodsId(goodsvo.getId());
    goodsDao.reduceStock(goods);
}   
}

解決超賣

超賣場景:

不同用戶在讀請求的時候,發現商品庫存足夠,然後同時發起請求,進行秒殺操作,減庫存,導致庫存減爲負數。

最簡單的方法,更新數據庫減庫存的時候,進行庫存限制條件,在reduceStock(GoodsVo goodsvo)這個方法裏,sql要多加一個stock_count > 0即:

//stock_count>0的時候纔去更新,數據庫本身會有鎖,那麼就不會在數據庫中同時多個線程更新一條記錄,使用數據庫特性來保證超賣的問題
@Update("update miaosha_goods set stock_count=stock_count-1 where goods_id=#{goodsId} and stock_count>0")
public void reduceStock(MiaoshaGoods goods);

也可以對讀操作加上顯式鎖(select … for update)這樣一來用戶1在進行讀操作時用戶2就需要排隊等待了,但是如果商品很熱門併發量很高那麼效率就會大大的下降。

訂單詳情頁面order_detail.htm完整代碼:

<!DOCTYPE html>
<html>  
<head>
<meta charset="UTF-8"/><!--<meta charset="UTF-8" />  thymeleaf模板引擎默認是Template modes:HTML5解析的,所以解析比較嚴格。  -->
<title>訂單詳情</title>
	<!-- thymeleaf引入靜態資源的方式,@加大括弧    "/" 代表static路徑-->
	<!-- jquery -->
	<!-- <script type="text/javascript" src="/js/jequery.min.js}"></script> -->
	<script type="text/javascript" src="/jquery-validation/lib/jquery-1.11.1.js"></script>
	<!-- bootstrap -->
	<!-- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous"/>
	 -->
	<link type="text/css" rel="stylesheet" href="/bootstrap/css/bootstrap.css"/>
	<script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>

</head>
<body>
	<div class="panel panel-default">
		<div class="panel-heading">秒殺訂單詳情</div>
		<table class="table" id="goodslist">
		<tr>
			<td>商品名稱</td>
			<td colspan="3" id="goodsName"></td>
		</tr>
		<tr>
			<td>商品圖片</td>
			<td colspan="2"><img id="goodsImg" width="80" height="60"></img></td>
		</tr>
		<tr>
			<td>訂單原價</td>
			<td colspan="3" id="goodsPrice"></td>
		</tr>
		<tr>
			<td>下單時間</td>
			<td id="createDate" colspan="2"></td>
		</tr>
		<tr>
			<td>訂單狀態</td>
			<td id="orderStatus"><!-- 根據數值,做顯示判斷 -->
			</td>
			<td>						<!-- 如果上面是未支付,那麼下面會生成一個立即支付的按鈕 -->
				<button class="btn btn-primary btn-block" type="submit" id="payButton">立即支付</button>
			</td>
		</tr>
		<tr>
			<td>收貨人</td>
			<td colspan="2">tom  15008484456</td>
		</tr>
		<tr>
			<td>收貨地址</td>
			<td colspan="2">四川崇州市</td>
		</tr>
		</table>
	</div>
</body>
<script type="text/javascript">
	
	$(function(){
		getOrderDetail();
	});

	function getOrderDetail() {
		//取參數orderId
		var orderId=getQueryString("orderId");
		$.ajax({
			url : "/order/detail",
			type : "GET",
			data : {
				orderId :orderId

			},
			success : function(data) {
				if (data.code == 0) {
					render(data.data);
				} else {
					layer.msg(data.msg);
				}
			},
			error : function() {
				layer.msg("請求有誤!");
			}
		//token如果cookie裏面有,會自己帶過去

		});
	}
	//渲染頁面--------5-17
	function render(detail){
		//alert(detail.status);
		var goods=detail.goodsVo;
		var order=detail.order;
		$("#goodsName").text(goods.goodsName);
		$("#goodsImg").attr("src",goods.goodsImg); 
		$("#goodsPrice").text(order.goodsPrice);	
		$("#createDate").text(order.createDate);
		//判斷訂單的狀態orderStatus
		var status="";
		if(order.orderStatus==0){
			$("#orderStatus").text("未支付");
		}else if(order.orderStatus==1){
			$("#orderStatus").text("待發貨");
		}else if(order.orderStatus==2){
			$("#orderStatus").text("已發貨");
		}else if(order.orderStatus==3){
			$("#orderStatus").text("待收貨");
		}
		
	}
		
	//獲取請求路徑裏面的參數
	function getQueryString(name){
		var reg=new RegExp("(^|&)"+name+"=([^&]*)(&|$)");
		var r=window.location.search.substr(1).match(reg);
		if(r!=null){
			return unescape(r[2]);
		}
		return null;
	}
</script>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章