效果图

有订单时的效果图
JavaWeb购物系统(六)购物车订单模块的实现
无订单时的效果图
JavaWeb购物系统(六)购物车订单模块的实现
订单详情页
JavaWeb购物系统(六)购物车订单模块的实现

功能

  1. 生成订单
  2. 订单页的展示
  3. 查看订单详情

正文

说明

和购物车同样的,首先得知道我们的订单对应的哪个实体对象。一个用户可能有多条订单记录,一个订单里边可以包含多个商品(也可以理解为多个购物项)。理清这个逻辑之后,我们就可以得到两个实体:订单实体类详细的订单项
对应到界面上就是如下图:
JavaWeb购物系统(六)购物车订单模块的实现

order.jsp(订单页)

<%@ page import="com.service.OrderService" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<%@ page import="com.myUtil.ProcessUtil" %>
<%@ page import="com.entity.Order" %>
<%@ page import="java.util.List" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: huawei
  Date: 2022/10/22
  Time: 20:02
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>我的订单</title>
    <!--Bootstrap5 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/css/bootstrap.min.css">
    <%--icon图标--%>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
    <!--  popper.min.js 用于弹窗、提示、下拉菜单 -->
    <script src="https://cdn.staticfile.org/popper.js/2.9.3/umd/popper.min.js"></script>
    <!-- 最新的 Bootstrap5 核心 JavaScript 文件 -->
    <script src="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
</head>
<body>
<%
    OrderService orderService = new OrderService();
    String userId = ProcessUtil.getUserIdBySessionId(session);
    List<Order> orderList= orderService.showAllOrder(userId);
    if (orderList!=null && orderList.size()!=0){
        session.setAttribute("orderList",orderList);
    }
%>
<nav class="navbar-expand-lg navbar navbar-dark bg-primary">
    <div class="container-fluid ">
        <a class="navbar-brand" href="#">我的订单</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNavDropdown">
            <ul class="navbar-nav">
                <li class="nav-item">
                    <a class="nav-link active" aria-current="page" href="/MyProject/index.jsp">Home</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<c:if test="${empty sessionScope.orderList}">
    <div class="container" >
        <div class="card position-relative" style="margin: 50px;height: 280px;background: #ffffff url(img/CartBackground.png) no-repeat; background-position: center left;">
            <div class="position-absolute top-50 start-50 translate-middle">
                <h7>
                    您还未购买过任何商品哦!赶紧行动吧!您可以:
                </h7><br>
                <a class="btn btn-primary btn-lg" href="/MyProject/index.jsp">购物</a>
            </div>
        </div>
    </div>
</c:if>
<c:if test="${!empty sessionScope.orderList}">
    <div class="container">
        <div class="card">
            <table class="table table-hover text-center">
                <tr>
                    <td>订单号</td>
                    <td>下单时间</td>
                    <td>总价</td>
                    <td>状态</td>
                    <td>详情</td>
                </tr>
                <c:forEach items="${sessionScope.orderList}" var="order">
                    <tr style="vertical-align: middle !important;text-align: center;">
                        <td>
                            <%--<img style="width: 100px;height: 100px" src="${or}"/>--%>
                            <span>${order.id}</span>
                        </td>
                        <td>
                            <%--分割下单时间--%>
                            <c:set value="${fn:split(order.create_time,'T')}" var="time"></c:set>
                            <c:forEach var="tm" items="${time}">
                                ${tm}
                            </c:forEach>
                        </td>
                        <td>${order.price}</td>
                        <td>0</td>
                        <td><a type="submit" class="btn btn-danger" href="/MyProject/orderDetail.jsp?orderId=${order.id}">查看</a></td>
                    </tr>
                </c:forEach>
            </table>
            <div class="row justify-content-between">
            </div>
        </div>
    </div>
</c:if>
</body>
</html>

订单的展示

这里通过在jsp页面,调用service层showAllOrder()来获取所有的订单列表。然后通过for-Each来将列表的每条信息渲染到前端页面

订单的查看

和前面几节讲的留言删除删除购物项思路一样,通过携带唯一标识ID(下单时间的毫秒数)来查看对应订单的详细信息。因为查看订单详情需要跳转页面,所有我们这里采用<a></a>标签来进行跳转。相关代码:

<td>
	<a type="submit" class="btn btn-danger" href="/MyProject/orderDetail.jsp?orderId=${order.id}">查看</a>
</td>

orderDetail.jsp(订单详情页)

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.entity.OrderItem" %>
<%@ page import="com.service.OrderItemService" %>
<%@ page import="java.util.List" %>
<%@ page import="com.service.OrderService" %>
<%@ page import="com.myUtil.ProcessUtil" %><%--
  Created by IntelliJ IDEA.
  User: huawei
  Date: 2022/10/22
  Time: 22:40
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>订单详情页</title>
    <!--Bootstrap5 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/css/bootstrap.min.css">
    <%--icon图标--%>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
    <!--  popper.min.js 用于弹窗、提示、下拉菜单 -->
    <script src="https://cdn.staticfile.org/popper.js/2.9.3/umd/popper.min.js"></script>
    <!-- 最新的 Bootstrap5 核心 JavaScript 文件 -->
    <script src="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
</head>
<body>
<%
    OrderItemService orderItemService = new OrderItemService();
//    String orderId = (String)session.getAttribute("orderId");
//    if (orderId==null){
        // 当用户重新登录之后/重启服务器,seesion域中会清空orderId。所以需要
      String  orderId = request.getParameter("orderId");
//    }
    List<OrderItem> orderItems = orderItemService.showAllOrderItem(orderId);
    if (orderItems !=null && orderItems.size()!=0){
        session.setAttribute("orderItems",orderItems);
    }
%>
<nav class="navbar-expand-lg navbar navbar-dark bg-primary">
    <div class="container-fluid ">
        <a class="navbar-brand" href="#">订单详情页</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNavDropdown">
            <ul class="navbar-nav">
                <li class="nav-item">
                    <a class="nav-link active" aria-current="page" href="/MyProject/index.jsp">Home</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<c:if test="${!empty sessionScope.orderItems}">
    <div class="container">
        <div class="card">
            <table class="table table-hover text-center">
                <tr>
                    <td>订单号</td>
                    <td>商品名称</td>
                    <td>价格</td>
                    <td>数量</td>
                    <td>总价格</td>
                </tr>
                <c:forEach items="${sessionScope.orderItems}" var="orderItem">
                    <tr style="vertical-align: middle !important;text-align: center;">
                        <td>${orderItem.order_id}</td>
                        <td>
                            <img style="width: 100px;height: 100px" src="${orderItem.img}"/>
                            <span>${orderItem.name}</span>
                        </td>
                        <td>${orderItem.price}</td>
                        <td>${orderItem.count}</td>
                        <td>${orderItem.total_price}</td>
                    </tr>
                </c:forEach>
            </table>
        </div>
    </div>
</c:if>
</body>
</html>

订单详情的展示

在上边查看详情页按钮的代码中可以看到,跳转的不是Controller层,而是跳转到了orderDetail.jsp页面。在详情页面,通过请求携带的订单Id,来获取数据库的中的订单项数据

不知道大家到这里会不会很奇怪?订单项是一个对象,订单也是一个对象。我们的订单和订单项的展示,都需要从数据库来获取,那么这两个对象是什么时候存储到数据库中的呢?

别急。下边就是答案:
JavaWeb购物系统(六)购物车订单模块的实现
当我们在购物车中点击结算按钮的时候,他就会生成订单对象以及和它对应的订单项
我们这里解释一下,它的前后端是如何处理的:

// 结算
$("#settlement").click(
    function () {
        $.post(
            "/MyProject/orderProcessServlet",
            {
                method: ""
            },
            function (data) {
                if ("success" == data){
                    alert("订单生成成功!");
                    location.reload(true);
                }
            },
            "json"
        )
    }
)

这段js代码在上一章节中,因为它属于购物车部分。是发送结算请求。请求地址是/orderProcessServlet,即我们的订单对应的Controller层

OrderProcessServlet(订单处理层)

package com.controller;
import com.entity.*;
import com.google.gson.Gson;
import com.myUtil.JdbcUtil;
import com.myUtil.ProcessUtil;
import com.service.OrderService;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class OrderProcessServlet extends HttpServlet {
    private OrderService orderService = new OrderService();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        Cart cart = (Cart) session.getAttribute("cart");
        String userId = ProcessUtil.getUserIdBySessionId(session);
        if (cart==null || cart.getItems().size()==0){
            response.sendRedirect("/MyProject/index.jsp");
            return;
        }
        if (cart!=null){
            // 对生成订单进行事务控制
            String orderId = null;
            try {
                orderId = orderService.saveOrder(cart, Integer.parseInt(userId));
                JdbcUtil.commit();
                session.setAttribute("orderId",orderId);
                response.getWriter().write(new Gson().toJson("success"));
            } catch (Exception e) {
                // 如果出错事务回滚
                JdbcUtil.rollback();
                e.printStackTrace();
            }
        }
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doPost(request, response);
    }
}

orederService(订单处理的业务层)

package com.service;
import com.dao.GoodsDao;
import com.dao.OrderDao;
import com.dao.OrderItemDao;
import com.entity.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
public class OrderService {
    private OrderDao orderDao = new OrderDao();
    private OrderItemDao orderItemDao = new OrderItemDao();
    private GoodsDao goodsDao = new GoodsDao();
    // 生成订单
    public String saveOrder(Cart cart, Integer userId){
        // 生成订单号
        String orderId = String.valueOf(System.currentTimeMillis());
        // 订单生成时间
        LocalDateTime createTime = LocalDateTime.now();
        Order order = new Order(orderId,createTime,cart.getTotalPrice(),0,userId);
        // 保存order表到数据库
        orderDao.addOrder(order);
        // 构建orderItem,并保存到数据库
        HashMap<Integer, ShopCarItem> items = cart.getItems();
        for (Integer goodId : items.keySet()) {
            ShopCarItem item = items.get(goodId);
            OrderItem orderItem =
                    new OrderItem(null, item.getImg(), item.getGoodsName(), item.getPrice(), item.getNumber(), item.getTotalPrice(), orderId);
            orderItemDao.addOrderItem(orderItem);
            // 得到商品信息
            Goods goods = goodsDao.showDataById(String.valueOf(goodId));
            // 更新库存
            goods.setInventory(goods.getInventory() - item.getNumber());
            // 更新到数据库
            goodsDao.updateStock(goods);
        }
        // 清空购物车
        cart.removeAll();
        return orderId;
    }
    // 显示所有订单
    public List<Order> showAllOrder(String userId){
        return orderDao.showAllOrder(userId);
    }
}

接着上边的逻辑,当生成订单请求发过来之后,在这Controller层调用Service层saveOrder()进行了订单的生成,而且在Service层我们进行了订单保存订单项的保存。而且我们注意到saveOrder()的参数是Cart购物车对象用户Id。因为订单和订单项的所有信息都是从购物车来获取的,而且每个订单得有对应的用户(我们得保证自己的订单不能出现在别人的页面上,对吧^_^)。
当然生成订单之后还得清空购物车。

orderItemService

package com.service;
import com.dao.OrderItemDao;
import com.entity.OrderItem;
import java.util.List;
public class OrderItemService {
    private OrderItemDao orderItemDao = new OrderItemDao();
    // 显示所有订单项
    public List<OrderItem> showAllOrderItem(String orderId){
        return  orderItemDao.showAllOrderItem(orderId);
    }
}

orderDao

package com.dao;
import com.entity.Order;
import java.util.List;
public class OrderDao extends BasicDao<Order> {
    // 生成订单
    public Boolean addOrder(Order order){
        String sql = "INSERT INTO `order` VALUES('"+order.getId()+"','"+order.getCreate_time()+"',"+order.getPrice()+","+order.getStatus()+","+order.getMember_id()+")";
        return dmlData(sql);
    }
    // 显示所有订单
    public List<Order> showAllOrder(String userId){
        String sql = "SELECT * FROM `order` WHERE `member_id`='" + userId + "'";
        List<Order> orders = queryMulti(sql, Order.class);
        if (orders!=null){
            return orders;
        }
        return null;
    }
}

orderItemDao

package com.dao;
import com.entity.OrderItem;
import java.util.List;
public class OrderItemDao extends BasicDao<OrderItem>{
    // 添加订单项
    public Boolean addOrderItem(OrderItem orderItem){
        String sql = "INSERT INTO `order_item` VALUES(NULL,'"+ orderItem.getImg() +"','"+ orderItem.getName() + "',"+ orderItem.getPrice() +","+ orderItem.getCount() +","+ orderItem.getTotal_price() +",'"+ orderItem.getOrder_id() +"')";
        return dmlData(sql);
    }
    // 显示所有订单项
    public List<OrderItem> showAllOrderItem(String orderId){
        String sql = "SELECT * FROM `order_item` WHERE `order_id`='"+ orderId +"'";
        List<OrderItem> orderItems = queryMulti(sql, OrderItem.class);
        if (orderItems != null){
            return orderItems;
        }
        return null;
    }
}

order(订单实体类)

订单实体类拥有属性:

  1. 订单号(订单结算的毫秒时间)
  2. 订单生成时间
  3. 订单金额
  4. 订单状态(这里我们没有太多的功能,所以默认是0)
  5. 订单所属者(每个订单属于哪个用户)
package com.entity;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class Order {
    // 订单号
    private String id;
    // 订单生成时间
    private LocalDateTime create_time;
    // 订单金额
    private BigDecimal price;
    // 订单状态
    private Integer status;
    // 该订单对应的用户id
    private Integer member_id;
    public Order() {
    }
    public Order(String id, LocalDateTime create_time, BigDecimal price, Integer status, Integer member_id) {
        this.id = id;
        this.create_time = create_time;
        this.price = price;
        this.status = status;
        this.member_id = member_id;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public LocalDateTime getCreate_time() {
        return create_time;
    }
    public void setCreate_time(LocalDateTime create_time) {
        this.create_time = create_time;
    }
    public BigDecimal getPrice() {
        return price;
    }
    public void setPrice(BigDecimal price) {
        this.price = price;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public Integer getMember_id() {
        return member_id;
    }
    public void setMember_id(Integer member_id) {
        this.member_id = member_id;
    }
    @Override
    public String toString() {
        return "Order{" +
                "id='" + id + '\'' +
                ", create_time=" + create_time +
                ", price=" + price +
                ", status=" + status +
                ", member_id=" + member_id +
                '}';
    }
}

orderItem(订单项实体类)

订单项的实体类拥有的属性:

  1. 订单项唯一的标识Id
  2. 商品图片
  3. 商品的价格
  4. 商品数量
  5. 订单项的总价格
  6. 订单项的所属的订单(订单项都是从属于某一个订单)
package com.entity;
import java.math.BigDecimal;
public class OrderItem {
    // 订单项的自增ID
    private Integer id;
    // 商品图片
    private String img;
    // 商品名
    private String name;
    // 商品价格
    private BigDecimal price;
    // 商品数量
    private Integer count;
    // 订单项的总价
    private BigDecimal total_price;
    // 对应的订单号
    private String order_id;
    public OrderItem() {
    }
    @Override
    public String toString() {
        return "OrderItem{" +
                "id=" + id +
                ", img='" + img + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", count=" + count +
                ", total_price=" + total_price +
                ", order_id='" + order_id + '\'' +
                '}';
    }
    public String getImg() {
        return img;
    }
    public void setImg(String img) {
        this.img = img;
    }
    public OrderItem(Integer id, String img, String name, BigDecimal price, Integer count, BigDecimal total_price, String order_id) {
        this.id = id;
        this.img = img;
        this.name = name;
        this.price = price;
        this.count = count;
        this.total_price = total_price;
        this.order_id = order_id;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public BigDecimal getPrice() {
        return price;
    }
    public void setPrice(BigDecimal price) {
        this.price = price;
    }
    public Integer getCount() {
        return count;
    }
    public void setCount(Integer count) {
        this.count = count;
    }
    public BigDecimal getTotal_price() {
        return total_price;
    }
    public void setTotal_price(BigDecimal total_price) {
        this.total_price = total_price;
    }
    public String getOrder_id() {
        return order_id;
    }
    public void setOrder_id(String order_id) {
        this.order_id = order_id;
    }
}

order表的设计

列名 数据类型 长度 主键? 非空? 自增?
id varchar 64
create_time datetime
price decimal 11,2
statue tinyint 32
member int

orderItem表的设计

列名 数据类型 长度 主键? 非空? 自增?
id int
img varchar 500
name varchar 500
price decimal 11,2
count int
total_price decimal 11,2
order_id varchar 64

这里我们得保证实体类的属性名要和对应表的字段名保持一致!!!

发表回复