效果图
有订单时的效果图
无订单时的效果图
订单详情页
功能
- 生成订单
- 订单页的展示
- 查看订单详情
正文
说明
和购物车同样的,首先得知道我们的订单对应的哪个实体对象。一个用户可能有多条订单记录,一个订单里边可以包含多个商品(也可以理解为多个购物项)
。理清这个逻辑之后,我们就可以得到两个实体:订单实体类
和详细的订单项
对应到界面上就是如下图:
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 | √ |
这里我们得保证实体类的属性名要和对应表的字段名保持一致!!!