電商專案day17(跨域&訂單)
今日目標:
掌握跨域請求CORS
完成結算頁收貨人地址選擇功能
完成結算頁支付方式選擇
完成結算頁商品清單功能
儲存訂單功能
一.商品詳情頁跨域請求
1.購物車詳情頁面功能的對接
首先我們在模板中配置新增購物車的點選按鈕,我們主要獲得兩個引數 itemId 和 num
通過我們通過插值替換,獲得商品的id
我們在本地的靜態工程中的controller中的js 中pageController.js編寫方法新增購車車的方法,通過請求cart_web的工程
//新增商品到購物車 $scope.addItemToCartList=function(){ $http.get("http://localhost:8087/cart/addItemToCartList.do?itemId="+itemId+"&num="+$scope.num).success(function(response){ if (response.success) { //新增購物車成功 location.href="http://localhost:8087/cart.html"; }else{ alert(response.message); } }) }
我們重新啟動購物車相關服務 通過商品詳情頁面訪問,點選新增購物車按鈕,出現如下問題:
無法進行跨域訪問
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin
域:包含;協議、域名、埠號 三種任意一個不同,就是涉及瀏覽器跨域問題。
http://www.demo.com/a.html
https://www.demo.com/a.html
http://www.demo.com/a.html
http://www.test.com/a.html
http://www.demo.com:8000/a.html
http://www.demo.com/a.html
跨域大前提:請求方式是非同步請求。ajax。 angularjs的$http發起的請求,全部是非同步請求。
同步請求:
location.href <a href=""> form表單提交。
2.跨域請求的解決方案(三種解決方案)
第一種: jQuery:提供跨域解決方案,jsonp 原理:動態的生成<script>發起同步請求。
第二種: CORS:w3c提供的“跨域資源共享”Cross-origin resource sharing
它允許瀏覽器向跨源伺服器,發出 XMLHttpRequest 請求,從而克服了 AJAX 只能同源使用的限制。整個 CORS 通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS 通訊與同源的 AJAX 通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現 AJAX 請求跨
源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。因此,實現 CORS 通訊的關鍵是伺服器。只要伺服器實現了 CORS 介面,就可以跨源通訊。
又上圖我們可以看到,會進行兩次請求和兩次相應,主要方法是通過設定一些頭資訊
我們在controller層的新增購物車列表中編寫兩行程式碼:
response.setHeader("Access-Control-Allow-Origin", "http://localhost:9105");
response.setHeader("Access-Control-Allow-Credentials", "true");
Access-Control-Allow-Origin
Access-Control-Allow-Origin 是 HTML5 中定義的一種解決資源跨域的策略。
他是通過伺服器端返回帶有 Access-Control-Allow-Origin 標識的 Response header,用來解決資源的跨域許可權問題。
使用方法,在 response 新增 Access-Control-Allow-Origin,例如
Access-Control-Allow-Origin:www.google.com
也可以設定為 * 表示該資源誰都可以用
第三種方案: springMVC跨域註解
我們只需要新增一個註解: @CrossOrigin即可
注意:版本一定要高 4.2以上
我們伺服器端允許了跨域的請求,我們必須客戶端也開啟跨域
如果我們不寫上面的配置的話,瀏覽器則不發cookie
重新測試:
則跳轉到購物車頁面
二.訂單結算頁收件人功能展示
1.首先構建訂單的模組 order_web order_interface order_service
複製address地址的相關檔案
2.注意我們沒有寫order_web工程,直接在cart_web中編寫,這樣我們就不用重新建立工程,新增spring-security的安全框架
需求分析:
我們必須在AddressControlelr中編寫通過使用者id找對應的地址列表
/**
* 通過使用者id查詢使用者對應的所有的地址
*/
@RequestMapping("findAddressListByUserId")
public List<TbAddress> findAddressListByUserId(){
//通過spring-security框架獲得使用者的id
String userId = SecurityContextHolder.getContext().getAuthentication().getName();
return addressService.findAddressListByUserId(userId);
}
service層
/**
* 通過使用者的id查詢所對應的地址列表
* @param userId
* @return
*/
@Override
public List<TbAddress> findAddressListByUserId(String userId) {
//通過條件查詢獲得地址的列表
TbAddressExample example = new TbAddressExample();
Criteria criteria = example.createCriteria();
criteria.andUserIdEqualTo(userId);
List<TbAddress> addressList = addressMapper.selectByExample(example);
return addressList;
}
前臺頁面:
//通過使用者的id查詢使用者的地址列表
this.findAddressListByUserId=function(){
return $http.get('address/findAddressListByUserId.do');
}
orderController.js
//傳送請求,獲得使用者對應的地址資訊
$scope.findAddressListByUserId=function () {
addressService.findAddressListByUserId().success(function (response) {
$scope.addressList=response;
})
}
頁面點選的,聯動底層的寄送地址
前臺實現:
//展示收件人地址列表
$scope.findAddressListByUserId=function () {
addressService.findAddressListByUserId().success(function (response) {
//收件人地址列表
$scope.addressList=response;
for(var i=0;i< $scope.addressList.length;i++){
if($scope.addressList[i].isDefault=='1'){//預設收件人地址
$scope.address=$scope.addressList[i];
break;
}
}
//如果沒有設定預設收件人地址,取第一個地址為預設地址
if($scope.address==null){
$scope.address=$scope.addressList[0];
}
})
}
//定義寄送至的收件人地址物件
$scope.address=null;
//勾選預設收件人地址
$scope.isSelected=function (addr) {
if($scope.address==addr){
return true;
}else {
return false;
}
}
//跟改點選選中的狀態然後我們,動態給address賦值
$scope.updateSelected=function (addr) {
$scope.address=addr;
}
頁面:
三.支付方式選擇
思路分析:我們通過分析該專案由兩種支付方式,分別是線上是否,通過傳過來的值,我們通過傳過來的值1或者2判斷時那種支付方式
需求分析:
//支付方式,
//定義一個實體類
$scope.entity={paymentType:"1"};
$scope.updatePaymentType=function (type) {
$scope.entity.paymentType=type;
}
四.商品清單與金額顯示
思路分析:其實這個功能我們在購物車詳情頁面已經實現了,findCartList() 所有我們直接用就可以了
六.儲存清單
思路分析:
分散式id生成器,IDwork 這個是基於推特公司的開源演算法,雪花演算法
訂單與商家關聯:
購物車結算時,如果有多個商家的商品,根據商家生成多個訂單,
資料庫表的分析:
注意:我們設計金錢的相關資料時,我們不能頁面傳遞資料,只能後臺組裝.
隨著訂單的與日俱增,一臺資料庫無法滿足訂單,資料的儲存,需要,搭建叢集的方式,儲存資料,主要考慮訂單主鍵生成策略
四種方法:
第一種:UUID 缺點無法基於UUID生成主鍵,完成排序
第二種:oracle 的sequence (序列)
第三種:可以基於redis實現數字加一
第四種:開源框架,技術,分散式id生成器 twitter 基於雪花演算法生成id
資料庫表結構分析:
tb_order 訂單表
`expire` datetime DEFAULT NULL COMMENT '過期時間,定期清理', //基於定時任務框架定期清理無效訂單 spring task quartz
後端組裝資料
`order_id` bigint(20) NOT NULL COMMENT '訂單id', //不是主鍵自增
`payment` decimal(20,2) DEFAULT NULL COMMENT '實付金額。精確到2位小數;單位:元。如:200.07,表示:200元7分',
`status` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '狀態:1、未付款,2、已付款,3、未發貨,4、已發貨,5、交易成功,6、交易關閉,7、待評價',
`create_time` datetime DEFAULT NULL COMMENT '訂單建立時間',
`update_time` datetime DEFAULT NULL COMMENT '訂單更新時間',
`user_id` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '使用者id',
`source_type` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '訂單來源:1:app端,2:pc端,3:M端,4:微信端,5:手機qq端',
`seller_id` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '商家ID', //來著購物車
頁面提交資料
`payment_type` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '支付型別,1、線上支付,2、貨到付款',
`receiver_area_name` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '收貨人地區名稱(省,市,縣)街道',
`receiver_mobile` varchar(12) COLLATE utf8_bin DEFAULT NULL COMMENT '收貨人手機',
`receiver` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '收貨人',
tb_order_item 訂單項表
`id` bigint(20) NOT NULL, //不是主鍵自增
`order_id` bigint(20) NOT NULL COMMENT '訂單id',
儲存訂單實現流程:
1、構建訂單模組
2、將程式碼生成器生成的訂單相關程式碼拷貝到專案中
3、組裝訂單資料
1、根據使用者名稱從redis中獲取購物車列表
2、構建訂單資料(後臺組裝資料、前臺頁面傳遞資料)
4、儲存組裝好的資料到資料庫中
後臺程式碼實現:
controller層,我們通過spring-cecurity獲得使用者id
service層:
我們大多的資料從資料庫中獲得的,我們通過遍歷購物車列表,每個購物車就是一個訂單,我們自己建立訂單,賦值,最後新增完後我們必須要刪除redis資料庫中的的購物車資料
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private TbOrderItemMapper tbOrderItemMapper;
/**
* 增加
*/
@Override
public void add(TbOrder order) {
//我們必須自己組裝資料,很多的資料我們只能從購物車列表中獲得,可能購物車中有兩個商家,那麼我麼
//要通過迴圈購物車列表來生成訂單 ,從redis中獲得購物車資料
//訂單與商家關聯,訂單資料有很多是來自購物車列表資料
List<Cart> cartList = (List<Cart>) redisTemplate.boundValueOps(order.getUserId()).get();
//迴圈購物車 ,組裝訂單 ,每個購物車列表就是一個訂單
for (Cart cart : cartList) {
//構建訂單物件
TbOrder tbOrder = new TbOrder();
/*
`order_id` bigint(20) NOT NULL COMMENT '訂單id', //不是主鍵自增
`payment` decimal(20,2) DEFAULT NULL COMMENT '實付金額。精確到2位小數;單位:元。如:200.07,表示:200元7分',
`status` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '狀態:1、未付款,2、已付款,3、未發貨,4、已發貨,5、交易成功,6、交易關閉,7、待評價',
`create_time` datetime DEFAULT NULL COMMENT '訂單建立時間',
`update_time` datetime DEFAULT NULL COMMENT '訂單更新時間',
`user_id` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '使用者id',
`source_type` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '訂單來源:1:app端,2:pc端,3:M端,4:微信端,5:手機qq端',
`seller_id` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '商家ID', //來著購物車
*/
//訂單id的封裝
long orderId = idWorker.nextId();
tbOrder.setOrderId(orderId);
//訂單狀態封裝
tbOrder.setStatus("1");
//建立訂單時間
tbOrder.setCreateTime(new Date());
//建立跟新訂單時間
tbOrder.setUpdateTime(new Date());
//使用者id,因為我們在controller層已經封裝 了,所以我們自己獲得就行
tbOrder.setUserId(order.getUserId());
//訂單來源
tbOrder.setSourceType("2");
//商家id
tbOrder.setSellerId(cart.getSellerId());
/*
頁面提交資料
`payment_type` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '支付型別,1、線上支付,2、貨到付款',
`receiver_area_name` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '收貨人地區名稱(省,市,縣)街道',
`receiver_mobile` varchar(12) COLLATE utf8_bin DEFAULT NULL COMMENT '收貨人手機',
`receiver` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '收貨人',
*/
//支付型別的封裝
tbOrder.setPaymentType(order.getPaymentType());
//收貨人地址
tbOrder.setReceiverAreaName(order.getReceiverAreaName());
//收貨人手機
tbOrder.setReceiverMobile(order.getReceiverMobile());
//收貨人
tbOrder.setReceiver(order.getReceiver());
//遍歷購物車明細資料,組裝訂單詳情資料
//我們主要後臺組裝資料 連個資料 其他的度,在購物車新增資料的時候已經組裝好了
List<TbOrderItem> orderItemList = cart.getOrderItemList();
//定義一個費用統計的變數
double payment = 0.00;
for (TbOrderItem orderItem : orderItemList) {
// `id` bigint(20) NOT NULL, //不是主鍵自增
orderItem.setId(idWorker.nextId());
// `order_id` bigint(20) NOT NULL COMMENT '訂單id',
orderItem.setOrderId(orderId);
//獲得費用
payment+=orderItem.getTotalFee().doubleValue();
//儲存到orderItem中
tbOrderItemMapper.insert(orderItem);
}
//payment 實付金額 我們不是前天傳的,我們從後臺算的
tbOrder.setPayment(new BigDecimal(payment));
//新增到訂單表中
orderMapper.insert(tbOrder);
}
//新增完,我們把redis中的購物車資料列表刪除
redisTemplate.boundHashOps("cartList").delete(order.getUserId());
}
最後實現訂單的新增