效果图
有订单时的效果图
 
 无订单时的效果图
 
 订单详情页
 
功能
- 生成订单
 - 订单页的展示
 - 查看订单详情
 
正文
说明
和购物车同样的,首先得知道我们的订单对应的哪个实体对象。一个用户可能有多条订单记录,一个订单里边可以包含多个商品(也可以理解为多个购物项)。理清这个逻辑之后,我们就可以得到两个实体:订单实体类和详细的订单项
 对应到界面上就是如下图:
 
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,来获取数据库的中的订单项数据。
不知道大家到这里会不会很奇怪?订单项是一个对象,订单也是一个对象。我们的订单和订单项的展示,都需要从数据库来获取,
那么这两个对象是什么时候存储到数据库中的呢?
别急。下边就是答案:
 
 当我们在购物车中点击结算按钮的时候,他就会生成订单对象以及和它对应的订单项。
 我们这里解释一下,它的前后端是如何处理的:
// 结算
$("#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(订单实体类)
订单实体类拥有属性:
- 订单号(订单结算的毫秒时间)
 - 订单生成时间
 - 订单金额
 - 订单状态(这里我们没有太多的功能,所以默认是0)
 - 订单所属者(每个订单属于哪个用户)
 
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(订单项实体类)
订单项的实体类拥有的属性:
- 订单项唯一的标识Id
 - 商品图片
 - 商品的价格
 - 商品数量
 - 订单项的总价格
 - 订单项的所属的订单(订单项都是从属于某一个订单)
 
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 | √ | 
这里我们得保证实体类的属性名要和对应表的字段名保持一致!!!