从商品详情页点击“加入购物车”按钮,将当前商品加入购物车,并跳转到购物车页面
当两个应用协议、主机地址(或域名)、端口其中有一项不同,就认为他们的域不同。
这里的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面的不同域的框架中(iframe)的数据。
从page-web的itemController.js,引入$http服务,修改addToCart()方法
//添加商品到购物车
$scope.addToCart=function(){
// alert('SKUID:'+$scope.sku.id );
$http.get("http://localhost:9107/cart/addGoodsToCartList.do?itemId="
+$scope.sku.id+"&num="+$scope.num).success(
function (response) {
if(response.success){
location.href="http://localhost:9107/cart";
}else{
alert(response.message);
}
}
);
}
发现无法跨域,XMLHttpRequest cannot load
CORS是一个w3c标准,全称为“跨域资源共享”(Cross-origin response sharing)。CORS需要浏览器和服务器同时支持。目前所有浏览器都支持,IE不能低于IE10
允许浏览器向跨源服
务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
请求过程如下:
Preflight Request:
然后服务器端给我们返回一个Preflight Response
下面开始让购物车工程接收跨域请求
response.setHeader("Access-Control-Allow-Origin", "http://localhost:9105");
response.setHeader("Access-Control-Allow-Credentials", "true");
Access-Control-Allow-Origin是HTML5定义的一种解决资源跨域的策略
用过服务器端返回带有Access-Control-Allow-Origin表示的ResponseHeader,解决资源的跨域权限问题。
在response添加Access-Control-Allow-Origin,也可以设置为*
表示该资源谁都可以用
//添加商品到购物车
$scope.addToCart=function(){
// alert('SKUID:'+$scope.sku.id );
$http.get("http://localhost:9107/cart/addGoodsToCartList.do?itemId="
+$scope.sku.id+"&num="+$scope.num,{"withCredentials":true}).success(
function (response) {
if(response.success){
location.href="http://localhost:9107/cart";
}else{
alert(response.message);
}
}
);
}
调用测试,可以实现跨域。
CORS请求默认不发送cookie和HTTP认证信息。如果要把cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials,另一方面,需要在ajax请求中打开withCredentials属性。
4.2及以上版本可以使用注解跨域。需要跨域的方法上添加注解@CrossOrigin
@CrossOrigin(orgins="http://localhost:9105",allowCredentials="true")
后面的allowCredentials="true"可以省略
在结算页实现收件人地址选择功能
Tb_address为地址表,user_id其实就是user表的username
getOrderInfo.html拷贝到cart-web的webapp下
public List<TbAddress> findListByUserId(String userId);
@Override
public List<TbAddress> findListByUserId(String userId) {
TbAddressExample example = new TbAddressExample();
Criteria criteria = example.createCriteria();
criteria.andUserIdEqualTo(userId);
return addressMapper.selectByExample(example);
}
@RequestMapping("/findListByLoginUser")
public List<TbAddress> findListByLoginUser(){
// 获取登录用户
String username = SecurityContextHolder.getContext().getAuthentication().getName();
return addressService.findListByUserId(username);
}
// 获取地址列表
this.findAddressList = function () {
return $http.get('address/findListByLoginUser.do');
}
// 获取当前用户的地址列表
$scope.findAddressList = function () {
cartService.findAddressList().success(
function (response) {
$scope.addressList = response;
}
);
}
<ul class="addr-detail">
<li class="addr-item">
<div ng-repeat="address in addressList">
<div class="con name "><a href="javascript:;" >{{address.contact}}<span title="点击取消选择"> a>div>
<div class="con address">{{address.address}} <span>{{address.mobile}}span>
<span class="base" ng-if="address.isDefault=='1'">默认地址span>
<span class="edittext"><a data-toggle="modal" data-target=".edit" data-keyboard="false" >编辑a> <a href="javascript:;">删除a>span>
div>
<div class="clearfix">div>
div>
li>
ul>
<body ng-app="pinyougou" ng-controller="cartController" ng-init="findAddressList()">
<script type="text/javascript" src="plugins/angularjs/angular.min.js"> script>
<script type="text/javascript" src="js/base.js"> script>
<script type="text/javascript" src="js/service/cartService.js"> script>
<script type="text/javascript" src="js/controller/cartController.js"> script>
// 选择地址
$scope.selectAddress = function (address) {
$scope.address = address;
}
// 判断某地址对象是不是当前选择的地址
$scope.idSelectedAddress = function (address) {
if(address===$scope.address){
return true;
}else{
return false;
}
}
<div class="con name {{idSelectedAddress(address)?'selected':''}}"><a href="javascript:;" ng-click="selectAddress(address)">{{address.contact}}<span title="点击取消选择"> a>div>
<div class="fc-receiverInfo">寄送至:{{address.address}} 收货人:{{address.contact}} {{address.mobile}}div>
修改orderInfoController.js
// 获取当前用户的地址列表
$scope.findAddressList = function () {
cartService.findAddressList().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;
}
}
}
);
}
TODO
实现支付方式的选择,品优购支持两种支付方式:微信支付和货到付款
cartController.js
$scope.order = {paymentType:'1'};//订单对象
// 选择支付方式
$scope.selectPayType = function (type) {
$scope.order.paymentType = type;
}
getOrderInfo.html
<li class="selected" ng-click="selectPayType('1')">微信付款<span title="点击取消选择">span>li>
<li ng-click="selectPayType('2')">货到付款<span title="点击取消选择">span>li>
显示购物车的商品清单和合计数量、金额
<body ng-app="pinyougou" ng-controller="cartController" ng-init="findAddressList();findCartList()">
<ul class="send-detail" ng-repeat="orderItem in cart.orderItemList">
<li>
<div class="sendGoods">
<ul class="yui3-g">
<li class="yui3-u-1-6">
<span><img width="100px" height="100px" src="{{orderItem.picPath}}"/>span>
li>
<li class="yui3-u-7-12">
<div class="desc">{{orderItem.title}}div>
<div class="seven">7天无理由退货div>
li>
<li class="yui3-u-1-12">
<div class="price">¥{{orderItem.price.toFixed(2)}}div>
li>
<li class="yui3-u-1-12">
<div class="num">X{{orderItem.num}}div>
li>
<li class="yui3-u-1-12">
<div class="exit">有货div>
li>
ul>
div>
li>
ul>
修改getOrderInfo.html
<div class="order-summary">
<div class="static fr">
<div class="list">
<span><i class="number">{{totalValue.totalNum}}i>件商品,总商品金额span>
<em class="allprice">¥{{totalValue.totalMoney.toFixed(2)}}em>
div>
<div class="list">
<span>返现:span>
<em class="money">0.00em>
div>
<div class="list">
<span>运费:span>
<em class="transport">0.00em>
div>
div>
div>
<div class="clearfix trade">
<div class="fc-price">应付金额: <span class="price">¥{{totalValue.totalMoney.toFixed(2)}}span>div>
点击订单结算页的提交订单,将购物车保存到订单表和订单明细表,并将购物车数据清除。
Tb_order为订单主表
根据之前生成的user相关的代码,拷贝到工程order-interface、order-service和cart-web
对于互联网应用,可能某个表会占用很大的存储空间,比如订单表,每天都会生成很多订单,如果服务器的硬盘满了怎么办?
采用数据库分片,把一个数据库进行拆分,通过数据库中间件进行连接
如果采用数据自增,可能会产生重复的ID
为什么无需的UUID导致入库性能变差呢?
涉及到B+树索引的分类,关系型数据库的索引大多是B+树。
雪花算法在同一毫秒最多可生成1024*4096=42万左右个id
order-service的OrderServiceImpl.java
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private IdWorker idWorker;
@Autowired
private TbOrderItemMapper orderItemMapper;
@Override
public void add(TbOrder order) {
// 1. 从redis中提取购物车列表
List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());
// 2. 循环购物车列表添加订单
for (Cart cart : cartList) {
TbOrder tbOrder = new TbOrder();
long orderId = idWorker.nextId();
tbOrder.setOrderId(orderId);
tbOrder.setPaymentType(order.getPaymentType());// 付款方式
tbOrder.setStatus("1");//未付款
tbOrder.setCreateTime(new Date());
tbOrder.setUpdateTime(new Date());
tbOrder.setUserId(order.getUserId());
tbOrder.setReceiverAreaName(order.getReceiverAreaName());//收货人地址
tbOrder.setReceiverMobile(order.getReceiverMobile());//收货人电话
tbOrder.setReceiver(order.getReceiver());// 收货人
tbOrder.setSourceType(order.getSourceType());//订单来源
tbOrder.setSellerId(order.getSellerId());//商家id
double money = 0;//合计数
// 循环购物车中每条明细记录
for (TbOrderItem orderItem : cart.getOrderItemList()) {
orderItem.setId(idWorker.nextId());
orderItem.setOrderId(orderId);//订单编号
// 注意:商家id来自购物车
orderItem.setSellerId(cart.getSellerId());//商家id
orderItemMapper.insert(orderItem);
money += orderItem.getTotalFee().doubleValue();
}
tbOrder.setPayment(new BigDecimal(money));//合计
orderMapper.insert(tbOrder);
}
// 3. 清除redis中的购物车
redisTemplate.boundHashOps("cartList").delete(order.getUserId());
}
记得在类上添加事务注解
在spring的配置applicationContext-service中添加idWorker
<bean id="idWorker" class="util.IdWorker">
<constructor-arg index="0" value="0"/>
<constructor-arg index="1" value="0"/>
bean>
修改cart-web的OrderController.java
修改add方法
@RequestMapping("/add")
public Result add(@RequestBody TbOrder order){
// 获取当前登录人账号
String username = SecurityContextHolder.getContext().getAuthentication().getName();
order.setUserId(username);
order.setSourceType("2");//订单来源 PC
try {
orderService.add(order);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
修改cart-web的cartService.js
// 提交订单
this,submitOrder = function (order) {
return $http.post('order/add.do',order);
}
修改cartController.js
// 保存订单
$scope.submitOrder = function () {
$scope.order.receiverAreaName=$scope.address.address;
$scope.order.receiverMobile = $scope.address.mobile;
$scope.order.receiver = $scope.address.contact;//联系人
cartService.submitOrder($scope.order).success(
function (response) {
alert(response.message);
}
);
}
最终需要跑cart-web,依赖user-service cart-service order-service
中间小bug,开始时不出现收货地址和收货手机号,发现controller写错了,receiverAreaName和receiverMobile
后来发现数据库没有存sellerid,发现sellerid应该是来自cart,写成了order
// 保存订单
$scope.submitOrder = function () {
$scope.order.receiverAreaName=$scope.address.address;
$scope.order.receiverMobile = $scope.address.mobile;
$scope.order.receiver = $scope.address.contact;//联系人
cartService.submitOrder($scope.order).success(
function (response) {
if(response.success){
if($scope.order.paymentType==='1'){//微信支付,跳转到支付页面
location.href="pay.html";
}else{
location.href="paysuccess.html";
}
}else{
alert(response.message);//也可以跳转提示页面
}
}
);
}