今日目标:
(1)掌握跨域请求CORS解决方案
(2)完成结算页收货人地址选择功能
(3)完成结算页支付方式选择
(4)完成结算页商品清单功能
(5)完成保存订单功能
目录
1、商品详细页跨域请求(购物车对接商品详细页)
1.1 需求分析
1.2 跨域调用测试
1.3 跨域解决方案CORS
2、结算页-收件人地址选择
2.1 需求分析
2.2 工程搭建
2.3 展示收货地址列表
2.4 选中收货地址
3、支付方式选择
3.1 需求分析
3.2 前端
4、送货清单与金额显示
4.1 需求分析
4.2 显示商品清单
4.3 显示总金额
5、保存订单
5.1 需求分析
5.2 分布式ID生成算法
5.3 保存订单
从商品详细页点击“加入购物车”按钮,将当前商品加入购物车,并跳转到购物车页面。
(1)修改freemarker静态资源中的itemController.js中的addToCart方法
// 加入商品到购物车
$scope.addToCart = function() {
// alert("SKU:" + $scope.sku.id + "加入购物车成功,购买数量为:" + $scope.num);
$http.get('http://localhost:9106/cart/addGoodsToCartList.do?itemId=' + $scope.sku.id + '&num=' + $scope.num).success(
function(rtn) {
if (rtn.success) {
// 新增购物车成功,跳转到购物车页面
location.href = 'http://localhost:9106/cart.html';
} else {
// 打印提示信息
alert(rtn.message);
}
}
);
}
(2)调用测试
(1)什么是CORS?
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
(2)请求过程
Preflight Request:
然后服务器端给我们返回一个Preflight Response
(3)实现跨域
控制层(cart-web),在addGoodsToCartList添加设置头信息代码(实现跨域)
response.setHeader("Access-Control-Allow-Origin", "http://localhost"); response.setHeader("Access-Control-Allow-Credentials", "true");
控制层(cart-web),在addGoodsToCartList方法上加入注解
@CrossOrigin(origins = "http://localhost",allowCredentials = "true")
注意:请求参数也需要修改,在itemController的addToCart提交请求的参数列表中加入:{'withCredentials':true},用逗号和前面的参数分隔
在结算页实现收件人地址选择功能
(1)复制代码生成器生成的服务层代码到 user 模块中,复制控制层代码以及静态页面到 cart 模块中
(1)后端:服务层接口(address-interface),在AddressService新增方法
/**
* 获取指定用户的收货地址列表
*
* @param userId 用户ID
* @return java.util.List
*/
List findListByLoginUser(String userId);
(2)后端:服务层实现(address-service),在AddressServiceImpl新增实现
@Override
public List findListByLoginUser(String userId) {
// 创建查询条件对象
TbAddressExample example = new TbAddressExample();
example.createCriteria().andUserIdEqualTo(userId);
// 执行查询
return addressMapper.selectByExample(example);
}
(3)后端:控制层(cart-web),在AddressController中新增方法
/**
* 获取当前用户的收货地址列表
*
* @return java.util.List
*/
@RequestMapping("/findListByLoginUser")
public List findListByLoginUser() {
// 获取当前登录用户
String loginUser = SecurityContextHolder.getContext().getAuthentication().getName();
return addressService.findListByLoginUser(loginUser);
}
(4)前端:在cartService.js中新增方法
// 获取当前登录用户的收货地址列表
this.findListByLoginUser = function () {
return $http.get('address/findListByLoginUser.do');
}
(5)前端:在cartController.js中新增方法
// 获取当前用户的收货地址列表
$scope.findListByLoginUser = function () {
cartService.findListByLoginUser().success(
function (rtn) {
$scope.addressList = rtn;
}
);
}
(6)前端:页面引入JS文件和基础指令
(7)页面循环展示收货地址列表
(1)前端:在cartController.js中新增方法
// 选择收货地址
$scope.selectAddress = function (address) {
$scope.address = address;
}
// 判断当前收货地址是否是被选中地址
$scope.isSelectedAddress = function (address) {
if ($scope.address == address) {
return true;
}
return false;
}
(2)前端:页面绑定方法
(3)展示默认选中的地址,在findListByLoginUser新增逻辑
实现支付方式的选择,品优购支持两种支付方式:微信支付和货到付款
(1)在cartController.js 中新增方法
// 选择付款方式
$scope.order = {paymentType:'1'};
$scope.selectPayment = function (type) {
$scope.order.paymentType = type;
}
(2)页面绑定单击事件(其中1为在线支付,2为货到付款)
显示购物车中的商品清单以及合计数量、金额
(1)初始化调用findCartList方法
(2)页面循环展示购物车列表
(1)绑定变量即可
(1)需求
点击订单结算页的提交订单 ,将购物车保存到订单表和订单明细表中,并将购物车数据清除.
(2)工程搭建
参看其他的服务层工程,注意web层还是在cart-web中,需要将order的控制层代码复制到cart-web的controller包下
我们采用的是开源的twitter( 非官方中文惯称:推特.是国外的一个网站,是一个社交网络及微博客服务) 的snowflake算法。
结构(64位2进制数):
时间戳(41bit):精确到毫秒
工作机器id(10bit):5位 数据中心ID 5位 机器ID
(1)后端:在spring的配置文件中配置雪花算法的工具类bean(order-service)
(2)后端:服务层实现(order-service),修改OrderServiceImpl中的add方法的逻辑
/**
* 增加
*/
@Override
public void add(TbOrder order) {
// 1.从redis中提取购物车列表
List cartList = (List) redisTemplate.boundHashOps("cartList").get(order.getUserId());
// 2.循环购物车保存订单
for (Cart cart : cartList) {
// 生成订单对象
TbOrder tbOrder = buildOrder(order);
// 设置商家ID
tbOrder.setSellerId(cart.getSellerId());
// 合计金额
double money = 0;
// 循环购物车明细
for (TbOrderItem orderItem : cart.getOrderItemList()) {
// 补全订单明细数据
orderItem.setOrderId(tbOrder.getOrderId());
orderItem.setSellerId(cart.getSellerId());
orderItem.setId(idWorker.nextId());
// 保存
orderItemMapper.insert(orderItem);
// 累加合计金额
money += orderItem.getTotalFee().doubleValue();
}
// 设置合计金额到订单
tbOrder.setPayment(BigDecimal.valueOf(money));
// 保存
orderMapper.insert(tbOrder);
}
// 3.清除购物车中的数据
redisTemplate.boundHashOps("cartList").delete(order.getUserId());
}
/**
* 构建订单对象
*
* @param order 订单部分数据
* @return com.pinyougou.pojo.TbOrder
*/
private TbOrder buildOrder(TbOrder order) {
TbOrder tbOrder = new TbOrder();
tbOrder.setOrderId(idWorker.nextId());
tbOrder.setPaymentType(order.getPaymentType());
tbOrder.setStatus(TbOrder.STATUS_NOT_PAY);
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());
return tbOrder;
}
(3)后端:控制层新增方法
/**
* 增加
* @param order
* @return
*/
@RequestMapping("/add")
public Result add(@RequestBody TbOrder order){
try {
order.setUserId(SecurityContextHolder.getContext().getAuthentication().getName());
// 设置订单来源:PC端
order.setSourceType("2");
orderService.add(order);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
(4)前端:在cartService.js中新增方法
// 提交订单
this.submitOrder = function (order) {
return $http.post('order/add.do', order);
}
(5)前端:在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 (rtn) {
if (rtn.success) {
// 提交订单成功,跳转到支付页面
if ($scope.order.paymentType == "1") {// 微信支付 跳转到支付页面
location.href = 'pay.html';
} else {// 货到付款 跳转到提示页面
location.href = 'paysuccess.html';
}
} else {
alert(rtn.message);
}
}
);
}
(6)前端:提交订单按钮绑定单击事件