【Spring+Mybatis+SpringMVC整合项目六】天猫商城(前台-购物流程)

前台一共有以下这些场景

这里记录购买商品这里场景开发,首先看看购物流程

 1. 登录
2. 访问产品页
3. 立即购买
4. 进入结算页面
5. 加入购物车
6. 查看购物车
7. 选中购物车中的商品
8. 又到了第4步的结算页面
9. 在结算页面生成订单
10. 付款
11. 确认收货
12. 评价


围绕购物流程最重要的两个表是OrderItem 和 Order表

关于OrderItem的业务行为
1. 立即购买 —— 新增 OrderItem
2. 加入购物车 —— 新增 OrderItem
3. 查看购物车 —— 显示未和Order关联的OrderItem
4. 选中购物车中的商品 —— 选中OrderItem
5. 结算页面 —— 显示选中的OrderItem

6. 生成订单 —— 新增Order
7 .付款 —— 修改Order状态
8. 我的订单 —— 显示Order
9. 确认收货 —— 修改Order状态

OrderItem表

Order表

 


当我们按下立即购买时,如果未登陆,那么点击立即购买之后会弹出一个登陆窗口,需要登陆后才能购买

前端检查是否登录js代码

$(".buyLink").click(function(){
            var page = "forecheckLogin";
            $.get(
                page,
                function(result){
                    if("success"==result){
                        var num = $(".productNumberSetting").val();
                        location.href= $(".buyLink").attr("href")+"&num="+num;
                    }
                    else{
                        $("#loginModal").modal('show');
                    }
                }
            );
            return false;
        });

forecheckLogin

@RequestMapping("forecheckLogin")
    @ResponseBody
    public String checkLogin( HttpSession session) {
        User user =(User)  session.getAttribute("user");
        if(null!=user)
            return "success";
        return "fail";
    }

如果我们登陆了会做如下操作

通过上个步骤访问的地址 /forebuyone 导致ForeController.buyone()方法被调用
1. 获取参数pid
2. 获取参数num
3. 根据pid获取产品对象p
4. 从session中获取用户对象user

接下来就是新增订单项OrderItem,如

在OrderItem表里插入一条数据,这条数据会表示:
1. pid =844 购买的商品id
2. oid = null, 这个订单项还没有生成对应的订单,即还在购物车中
3. uid= 3,用户的id是3
4. number=3, 购买了3件产品 

新增订单项要考虑两个情况
a. 如果已经存在这个产品对应的OrderItem,并且还没有生成订单,即还在购物车中。 那么就应该在对应的OrderItem基础上,调整数量
a.1 基于用户对象user,查询没有生成订单的订单项集合
a.2 遍历这个集合
a.3 如果产品是一样的话,就进行数量追加
a.4 获取这个订单项的 id

b. 如果不存在对应的OrderItem,那么就新增一个订单项OrderItem
b.1 生成新的订单项
b.2 设置数量,用户和产品
b.3 插入到数据库
b.4 获取这个订单项的 id

最后, 基于这个订单项id客户端跳转到结算页面/forebuy

@RequestMapping("forebuyone")
    public String buyone(int pid, int num, HttpSession session) {
        Product p = productService.get(pid);
        int oiid = 0;
 
        User user =(User)  session.getAttribute("user");
        boolean found = false;
        List<OrderItem> ois = orderItemService.listByUser(user.getId());
        for (OrderItem oi : ois) {
            if(oi.getProduct().getId().intValue()==p.getId().intValue()){
                oi.setNumber(oi.getNumber()+num);
                orderItemService.update(oi);
                found = true;
                oiid = oi.getId();
                break;
            }
        }
 
        if(!found){
            OrderItem oi = new OrderItem();
            oi.setUid(user.getId());
            oi.setNumber(num);
            oi.setPid(pid);
            orderItemService.add(oi);
            oiid = oi.getId();
        }
        return "redirect:forebuy?oiid="+oiid;
    }
 
}

结算页面是这样的

可以发现跳转到/forebuy其实就是进行了服务器端的跳转,使得ForeServlet.buy()被调用了

1. 通过字符串数组获取参数oiid
为什么这里要用字符串数组试图获取多个oiid,而不是int类型仅仅获取一个oiid?因为结算页面还需要显示在购物车中选中的多条OrderItem数据,所以为了兼容从购物车页面跳转过来的需求,要用字符串数组获取多个oiid
2. 准备一个泛型是OrderItem的集合ois
3. 根据前面步骤获取的oiids,从数据库中取出OrderItem对象,并放入ois集合中
4. 累计这些ois的价格总数,赋值在total上
5. 把订单项集合放在session的属性 "ois" 上
6. 把总价格放在 model的属性 "total" 上
7. 服务端跳转到buy.jsp 

@RequestMapping("forebuy")
    public String buy( Model model,String[] oiid,HttpSession session){
        List<OrderItem> ois = new ArrayList<>();
        float total = 0;
 
        for (String strid : oiid) {
            int id = Integer.parseInt(strid);
            OrderItem oi= orderItemService.get(id);
            total +=oi.getProduct().getPromotePrice()*oi.getNumber();
            ois.add(oi);
        }
 
        session.setAttribute("ois", ois);
        model.addAttribute("total", total);
        return "fore/buy";
    }
 
}

在buy.jsp中,点击提交按钮后,就把表单里面的信息提交到/forecreateOrder

到了订单生成的开发,对于每个订单,都有自己的一个状态

1. 首先是创建订单,刚创建好之后,订单处于waitPay 待付款状态
2. 接着是付款,付款后,订单处于waitDelivery 待发货状态
3. 前两步都是前台用户操作导致的,接下来需要到后台做发货操作,发货后,订单处于waitConfirm 待确认收货状态
4. 接着又是前台用户进行确认收货操作,操作之后,订单处于waitReview 待评价状态
5. 最后进行评价,评价之后,订单处于finish 完成状态

以上状态都是一个接一个的,不能跳状态进行。
比较特殊的是,无论当前订单处于哪个状态,都可以进行删除操作。 像订单这样极其重要的业务数据,实际上是不允许真正从数据库中删除掉的,而是把状态标记为删除,以表示其被删掉了,所以在删除之后,订单处于 delete 已删除状态 

这里,在写createOrder代码之前,我们还需在OrderService中新增方法add(Order c,List<OrderItem> ois);

看看它的实现方法

1.增加了事务管理,因为增加订单需要同时修改两个表,orderItem表和order表,如果在修改了orderItem表之后发生突发意外,没能修改到order表,会导致数据不一致,所以这里增加事务管理,当有异常时就回滚

2.传入ois是为了算出总价格,最后返回所有商品价格之和

@Override
    @Transactional(propagation= Propagation.REQUIRED,rollbackForClassName="Exception")
 
    public float add(Order o, List<OrderItem> ois) {
        float total = 0;
        add(o);
 
        if(false)
            throw new RuntimeException();
 
        for (OrderItem oi: ois) {
            oi.setOid(o.getId());
            orderItemService.update(oi);
            total+=oi.getProduct().getPromotePrice()*oi.getNumber();
        }
        return total;
    }

现在开始写createOrder代码

提交订单访问路径 /forecreateOrder, 导致ForeController.createOrder 方法被调用
1. 从session中获取user对象
2. 通过参数Order接受地址,邮编,收货人,用户留言等信息
3. 根据当前时间加上一个4位随机数生成订单号
4. 根据上述参数,创建订单对象
5. 把订单状态设置为等待支付
6. 从session中获取订单项集合 ( 在forebuy中,订单项集合被放到了session中 )
7. 把订单加入到数据库,并且遍历订单项集合,设置每个订单项的order,更新到数据库
8. 统计本次订单的总金额
9. 客户端跳转到确认支付页forealipay,并带上订单id和总金额

@RequestMapping("forecreateOrder")
public String createOrder( Model model,Order order,HttpSession session){
    User user =(User)  session.getAttribute("user");
    String orderCode = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + RandomUtils.nextInt(10000);
    order.setOrderCode(orderCode);
    order.setCreateDate(new Date());
    order.setUid(user.getId());
    order.setStatus(OrderService.waitPay);
    List<OrderItem> ois= (List<OrderItem>)  session.getAttribute("ois");
 
    float total =orderService.add(order,ois);
    return "redirect:forealipay?oid="+order.getId() +"&total="+total;
}

支付页forealipay没有做什么事情,因为这里并没有真正使用到支付的功能~只是做了一个跳转。。。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isELIgnored="false"%>
     
<div class="aliPayPageDiv">
    <div class="aliPayPageLogo">
        <img class="pull-left" src="img/site/simpleLogo.png">
        <div style="clear:both"></div>
    </div>
     
    <div>
        <span class="confirmMoneyText">扫一扫付款(元)</span>
        <span class="confirmMoney">
        ¥<fmt:formatNumber type="number" value="${param.total}" minFractionDigits="2"/></span>
         
    </div>
    <div>
        <img class="aliPayImg" src="img/site/alipay2wei.png">
    </div>
 
    <div>
        <a href="forepayed?oid=${param.oid}&total=${param.total}"><button class="confirmPay">确认支付</button></a>
    </div>
 
</div>

1. 在上一步确认访问按钮提交数据到/forepayed,导致ForeController.payed方法被调用
1.1 获取参数oid
1.2 根据oid获取到订单对象order
1.3 修改订单对象的状态和支付时间
1.4 更新这个订单对象到数据库
1.5 把这个订单对象放在model的属性"o"上
1.6 服务端跳转到payed.jsp

@RequestMapping("forepayed")
public String payed(int oid, float total, Model model) {
    Order order = orderService.get(oid);
    order.setStatus(OrderService.waitDelivery);
    order.setPayDate(new Date());
    orderService.update(order);
    model.addAttribute("o", order);
    return "fore/payed";
}

payed.jsp图

其中这里可以点击 查看交易详情,跳转到个人订单页面

 

/forebought导致ForeController.bought()方法被调用
1. 通过session获取用户user
2. 查询user所有的状态不是"delete" 的订单集合os
3. 为这些订单填充订单项
4. 把os放在model的属性"os"上
5. 服务端跳转到bought.jsp

@RequestMapping("forebought")
    public String bought( Model model,HttpSession session) {
        User user =(User)  session.getAttribute("user");
        List<Order> os= orderService.list(user.getId(),OrderService.delete);
 
        orderItemService.fill(os);
 
        model.addAttribute("os", os);
 
        return "fore/bought";
    }
 
}

 bought.jsp

猜你喜欢

转载自blog.csdn.net/weixin_41866960/article/details/84800786
今日推荐