電商秒殺

1.需求分析

1.需求

        所謂“秒殺”,就是網絡賣家發佈一些超低價格的商品,所有買家在同一時間網上搶購的一種銷售方式。通俗一點講就是網絡商家爲促銷等目的組織的網上限時搶購活動。由於商品價格低廉,往往一上架就被搶購一空,有時只用一秒鐘。

秒殺商品通常有兩種限制:庫存限制、時間限制。

需求:

  1. 商家提交秒殺商品申請,錄入秒殺商品數據,主要包括:商品標題、原價、秒殺價、商品圖片、介紹等信息
  2. 運營商審覈秒殺申請
  3. 秒殺頻道首頁列出秒殺商品(進行中的)點擊秒殺商品圖片跳轉到秒殺商品詳細頁。
  4. 商品詳細頁顯示秒殺商品信息,點擊立即搶購實現秒殺下單,下單時扣減庫存。當庫存爲0或不在活動期範圍內時無法秒殺。
  5. 秒殺下單成功,直接跳轉到支付頁面(微信掃碼),支付成功,跳轉到成功頁,填寫收貨地址、電話、收件人等信息,完成訂單。
  6. 當用戶秒殺下單5分鐘內未支付,取消預訂單,調用微信支付的關閉訂單接口,恢復庫存

​​​​​​​2.實現思路

        秒殺技術實現核心思想是運用緩存減少數據庫瞬間的訪問壓力!讀取商品詳細信息時運用緩存,當用戶點擊搶購時減少緩存中的庫存數量,當庫存數爲0時或活動期結束時,同步到數據庫。 產生的秒殺預訂單也不會立刻寫到數據庫中,而是先寫到緩存,當用戶付款成功後再寫入數據庫。

秒殺頻道首頁

1.秒殺頻道首頁,顯示正在秒殺的商品(已經開始,未結束的商品)

serviceImpl

        @Override
	public List<TbSeckillGoods> findList() {
		//獲取秒殺商品列表
		List<TbSeckillGoods> seckillGoodsList = redisTemplate.boundHashOps("seckillGoods").values();
		if(seckillGoodsList==null || seckillGoodsList.size()==0){
			TbSeckillGoodsExample example=new TbSeckillGoodsExample();
			Criteria criteria = example.createCriteria();
			criteria.andStatusEqualTo("1");//審覈通過
			criteria.andStockCountGreaterThan(0);//剩餘庫存大於0
			criteria.andStartTimeLessThanOrEqualTo(new Date());//開始時間小於等於當前時間
			criteria.andEndTimeGreaterThan(new Date());//結束時間大於當前時間
			seckillGoodsList= seckillGoodsMapper.selectByExample(example);		
			//將商品列表裝入緩存
			System.out.println("將秒殺商品列表裝入緩存");
			for(TbSeckillGoods seckillGoods:seckillGoodsList){
				redisTemplate.boundHashOps("seckillGoods").put(seckillGoods.getId(), seckillGoods);
			}			
		}
		return seckillGoodsList;
	}

2.秒殺商品詳情頁

serviceImpl

	@Override
	public TbSeckillGoods findOneFromRedis(Long id) {
		return  (TbSeckillGoods)redisTemplate.boundHashOps("seckillGoods").get(id);
	}

秒殺下單

serviceImpl

        @Autowired
	private RedisTemplate redisTemplate;
	
	@Autowired
	private IdWorker idWorker;
		
	@Override
	public void submitOrder(Long seckillId, String userId) {
		//從緩存中查詢秒殺商品		
		TbSeckillGoods seckillGoods =(TbSeckillGoods) redisTemplate.boundHashOps("seckillGoods").get(seckillId);
		if(seckillGoods==null){
			throw new RuntimeException("商品不存在");
		}
		if(seckillGoods.getStockCount()<=0){
			throw new RuntimeException("商品已搶購一空");
		}	
		//扣減(redis)庫存		
		seckillGoods.setStockCount(seckillGoods.getStockCount()-1);
		redisTemplate.boundHashOps("seckillGoods").put(seckillId, seckillGoods);//放回緩存
		if(seckillGoods.getStockCount()==0){//如果已經被秒光
    			seckillGoodsMapper.updateByPrimaryKey(seckillGoods);//同步到數據庫	
                redisTemplate.boundHashOps("seckillGoods").delete(seckillId);		
		}
		//保存(redis)訂單
		long orderId = idWorker.nextId();
		TbSeckillOrder seckillOrder=new TbSeckillOrder();
		seckillOrder.setId(orderId);
		seckillOrder.setCreateTime(new Date());
		seckillOrder.setMoney(seckillGoods.getCostPrice());//秒殺價格
		seckillOrder.setSeckillId(seckillId);
		seckillOrder.setSellerId(seckillGoods.getSellerId());
                seckillOrder.setUserId(userId);//設置用戶ID
		seckillOrder.setStatus("0");//狀態
		redisTemplate.boundHashOps("seckillOrder").put(userId, seckillOrder);
	}

秒殺支付

        用戶成功下單後,跳轉到支付頁面。支付頁顯示微信支付二維碼。用戶完成支付後,保存訂單到數據庫。

1.生成二維碼serviceimpl

        @RequestMapping("/createNative")
	public Map createNative(){
		//獲取當前用戶		
		String userId=SecurityContextHolder.getContext().getAuthentication().getName();
		//到redis查詢秒殺訂單
		TbSeckillOrder seckillOrder = seckillOrderService.searchOrderFromRedisByUserId(userId);
		//判斷秒殺訂單存在
		if(seckillOrder!=null){
			long fen=  (long)(seckillOrder.getMoney().doubleValue()*100);//金額(分)
			return weixinPayService.createNative(seckillOrder.getId()+"",+fen+"");
		}else{
			return new HashMap();
		}		
	}

2.支付成功保存訂單serviceImpl

	@Override
	public void saveOrderFromRedisToDb(String userId, Long orderId, String transactionId) {
		System.out.println("saveOrderFromRedisToDb:"+userId);
		//根據用戶ID查詢日誌
		TbSeckillOrder seckillOrder = (TbSeckillOrder) redisTemplate.boundHashOps("seckillOrder").get(userId);
		if(seckillOrder==null){
			throw new RuntimeException("訂單不存在");
		}
		//如果與傳遞過來的訂單號不符
		if(seckillOrder.getId().longValue()!=orderId.longValue()){
			throw new RuntimeException("訂單不相符");
		}		
		seckillOrder.setTransactionId(transactionId);//交易流水號
		seckillOrder.setPayTime(new Date());//支付時間
		seckillOrder.setStatus("1");//狀態
		seckillOrderMapper.insert(seckillOrder);//保存到數據庫
		redisTemplate.boundHashOps("seckillOrder").delete(userId);//從redis中清除
	}

3.支付狀態查詢Controller

    @RequestMapping("/queryPayStatus")
	public Result queryPayStatus(String out_trade_no){
		//獲取當前用戶		
		String userId=SecurityContextHolder.getContext().getAuthentication().getName();
		Result result=null;		
		int x=0;		
		while(true){
			//調用查詢接口
			Map<String,String> map = weixinPayService.queryPayStatus(out_trade_no);
			if(map==null){//出錯			
				result=new  Result(false, "支付出錯");
				break;
			}			
			if(map.get("trade_state").equals("SUCCESS")){//如果成功				
				result=new  Result(true, "支付成功");				
				seckillOrderService.saveOrderFromRedisToDb(userId, Long.valueOf(out_trade_no), map.get("transaction_id"));
				break;
			}			
			try {
				Thread.sleep(3000);//間隔三秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}	
			//不讓循環無休止地運行定義變量,如果超過了這個值則退出循環,設置時間爲1分鐘
			x++;
			if(x>20){				
				result=new  Result(false, "二維碼超時");	
				//1.調用微信的關閉訂單接口(學員實現)
				Map<String,String> payresult = weixinPayService.closePay(out_trade_no);				
				if( !"SUCCESS".equals(payresult.get("result_code")) ){//如果返回結果是正常關閉
					if("ORDERPAID".equals(payresult.get("err_code"))){
						result=new Result(true, "支付成功");	
						seckillOrderService.saveOrderFromRedisToDb(userId, Long.valueOf(out_trade_no), map.get("transaction_id"));
					}					
				}				
				if(result.isSuccess()==false){
					System.out.println("超時,取消訂單");
					//2.調用刪除
					seckillOrderService.deleteOrderFromRedis(userId, Long.valueOf(out_trade_no));	
				}				
				break;
			}				
		}
		return result;
	}

訂單超時處理

1.當用戶下單後5分鐘尚未付款應該釋放訂單,增加庫存serviceImpl

        @Override
	public void deleteOrderFromRedis(String userId, Long orderId) {
		//根據用戶ID查詢日誌
		TbSeckillOrder seckillOrder = (TbSeckillOrder) redisTemplate.boundHashOps("seckillOrder").get(userId);
		if(seckillOrder!=null &&
                 seckillOrder.getId().longValue()== orderId.longValue() ){
			 redisTemplate.boundHashOps("seckillOrder").delete(userId);//刪除緩存中的訂單
			 //恢復庫存
			//1.從緩存中提取秒殺商品		
			 TbSeckillGoods seckillGoods=(TbSeckillGoods)redisTemplate.boundHashOps("seckillGoods").get(seckillOrder.getSeckillId());
			 if(seckillGoods!=null){
				 seckillGoods.setStockCount(seckillGoods.getStockCount()+1);	
                                redisTemplate.boundHashOps("seckillGoods").put(seckillOrder.getSeckillId(), seckillGoods);//存入緩存
			 }			 
		}	
	}

2.關閉微信訂單serviceImpl

    public Map closePay(String out_trade_no) {
		Map param=new HashMap();
		param.put("appid", appid);//公衆賬號ID
		param.put("mch_id", partner);//商戶號
		param.put("out_trade_no", out_trade_no);//訂單號
		param.put("nonce_str", WXPayUtil.generateNonceStr());//隨機字符串
		String url="https://api.mch.weixin.qq.com/pay/closeorder";
		try {
			String xmlParam = WXPayUtil.generateSignedXml(param, partnerkey);
			HttpClient client=new HttpClient(url);
			client.setHttps(true);
			client.setXmlParam(xmlParam);
			client.post();
			String result = client.getContent();
			Map<String, String> map = WXPayUtil.xmlToMap(result);
			System.out.println(map);
			return map;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}		
	}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章