今日目标:
掌握跨域请求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());
}
最后实现订单的添加