文章目录
昨天实现了订单模型的全部基本功能,这里首先简单介绍订单模型,然后以订单模型的查看所有订单功能为例介绍分页模型,然后以发货功能为例介绍ajax局部更新页面,同时回顾我在写代码时遇到并解决的几个bug。
1. 订单模型
订单基本功能
订单模型的基本功能包括:
- 生成订单(用户)
- 查看所有订单(管理员)
- 查看我的订单(用户)
- 查看订单详情(管理员,用户)
- 发货(管理员)
- 签收(用户)
实体类和数据库表建立
建立订单类 Order:除了订单号,还包括根据用户结账的购物车信息获取商品总价,根据登录信息获得用户id(外键),根据结账时间获取建立订单的时间,以及一个唯一的订单号。
建立订单项类 OrderItem:一个购物车包含多个商品项,对应地,一个订单类有多个订单项,订单项的属性(商品id,名称,单价,购买数量,总价格)都来自购物车里的商品项。一个订单的多个订单项使用相同的订单号(外键)。
CREATE TABLE t_order(
`order_id` VARCHAR(50) PRIMARY KEY,
`create_time` DATETIME,
`price` DECIMAL(11,2),
`status` INT,
`user_id` INT,
FOREIGN KEY(`user_id`) REFERENCES t_user(`id`)
);
CREATE TABLE t_order_item(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(100),
`count` INT,
`price` DECIMAL(11,2),
`total_price` DECIMAL(11,2),
`order_id` VARCHAR(50),
FOREIGN KEY(`order_id`) REFERENCES t_order(`order_id`)
);
DESC t_order;
订单servlet
订单的servlet代码,这里的每一个方法对应上面的一个功能。
package indi.huishi.web;
import com.google.gson.Gson;
import indi.huishi.pojo.*;
import indi.huishi.service.OrderService;
import indi.huishi.service.impl.OrderServiceImpl;
import indi.huishi.utils.JdbcUtils;
import indi.huishi.utils.WebUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class OrderServlet extends BaseServlet{
OrderService orderService = new OrderServiceImpl();
/**
* 去结账
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("结账");
// cart和用户user都在session里
User user = (User) req.getSession().getAttribute("user");
// 如果User==null没有登录,转到登录页面
if(user==null){
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
return;
}
// System.out.println("OrderServlet当前线程"+Thread.currentThread().getName());
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart!=null){
// 使用事务
String orderId = null;
orderId = orderService.createOrder(cart, user.getId());
req.getSession().setAttribute("orderId",orderId);
resp.sendRedirect(req.getContextPath()+"/pages/cart/checkout.jsp");
}
}
/**
* 查询所有订单(未使用)
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showAllOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("查询所有订单");
// 获得日期 金额 详情(订单项) 发货状态
List<Order> orders = orderService.showAllOrders();
req.setAttribute("orders",orders);
// 请求转发
req.getRequestDispatcher("/pages/manager/order_manager.jsp").forward(req,resp);
}
/**
* 查询所有订单--分页
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showAllOrdersByPage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("查询所有订单分页");
// 获取当前页数和每页的条目数量
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = 4;
// 生成当前页的对象
Page<Order> page = orderService.page(pageNo, pageSize);
// 将请求跳转页面(点击下一页跳回到本页)的地址写入url
page.setUrl("orderServlet?action=showAllOrdersByPage");
// 保存到请求域
req.setAttribute("pageOrder",page);
// 请求转发
req.getRequestDispatcher("/pages/manager/order_manager.jsp").forward(req,resp);
}
/**
* 查看订单详情
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("查看订单详情...");
// 获取订单id
String orderId = req.getParameter("orderId");
// 获取所有订单项
List<OrderItem> orderItems = orderService.showOrderDetail(orderId);
// 保存到请求域
req.setAttribute("orderItems", orderItems);
// 请求转发
req.getRequestDispatcher("/pages/manager/order_manager_detail.jsp").forward(req,resp);
}
/**
* 修改发货状态,发货
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void sendOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("修改发货状态...");
// 获取订单id,当前状态
String orderId = req.getParameter("orderId");
String status = req.getParameter("status");
int stat = WebUtils.parseInt(status, -1);
// 结果
if (stat!=0){
resp.getWriter().write(new Gson().toJson("已发货"));
} else{
stat = 1;
int res = orderService.updateStatus(orderId, stat);
if (res!=0){
System.out.println("发货成功");
resp.getWriter().write(new Gson().toJson("发货成功 代签收"));
}else{
resp.getWriter().write(new Gson().toJson("发货失败"));
}
}
}
/**
* 查看我的订单(分页)
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showMyOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("查看我的订单");
// 获取用户id 保存在Session域对象
User user = (User)req.getSession().getAttribute("user");
Integer userId = user.getId();
// 获取当前页和每页条目数
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),1);
int pageSize = 4;
// 分页查找该用户的订单
Page<Order> orderPage = orderService.pageForMyOrder(userId, pageNo, pageSize);
System.out.println(orderPage);
// 将其保存到请求域
req.setAttribute("pageOrder", orderPage);
// 跳转到页面
req.getRequestDispatcher("/pages/order/order.jsp").forward(req,resp);
}
/**
* 修改订单状态:用户签收
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void receiveOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("用户签收");
// 和商家发货一样,本质上还是获取订单id和发货状态
String orderId = req.getParameter("orderId");
// 获取发货状态status 正常应该是1(已发货才能签收)
int status = WebUtils.parseInt(req.getParameter("status"), -1);
if(status!=1){
resp.getWriter().write(new Gson().toJson("状态异常,签收失败"));
}else{
status = status + 1;
// 修改
int result = orderService.updateStatus(orderId, status);
if (result!=0){
resp.getWriter().write(new Gson().toJson("已签收"));
}else{
resp.getWriter().write(new Gson().toJson("签收失败"));
}
}
}
}
2. 分页模型实现显示全部订单
在查看所有订单和查看我的订单中,可能很多条记录,这里使用分页模型实现。
分页模型:建立Page类,属性包括每页显示的订单条目数pageSize,展示给用户的页数pageNo,在数据库中查询订单条目总数pageTotalCount,需要的总页数pagetotal,以及当前页的显示内容items。
package indi.huishi.pojo;
import java.util.List;
/**
* 分页模型对象
* @param <T>泛型是具体javaBean类
*/
public class Page<T> {
//当前页显示数量
public static Integer PAGE_SIZE=4;
// 总页数
private Integer pageTotal;
// 总记录数
private Integer pageTotalCount;
// 当前页
private Integer pageNo;
// 当前页数据
private List<T> items;
// 分页条请求地址
private String url;
public Integer getPageTotal() {
return pageTotal;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public void setPageTotal(Integer pageTotal) {
this.pageTotal = pageTotal;
}
public Integer getPageTotalCount() {
return pageTotalCount;
}
public void setPageTotalCount(Integer pageTotalCount) {
this.pageTotalCount = pageTotalCount;
}
public Integer getPageNo() {
return pageNo;
}
public void setPageNo(Integer pageNo) {
// 数据边界有效检查
// 如果页码小于1 就显示第一页
if (pageNo < 1){
pageNo = 1;
}else if (pageNo > pageTotal){
pageNo = pageTotal;
}
this.pageNo = pageNo;
}
public List<T> getItems() {
return items;
}
public void setItems(List<T> items) {
this.items = items;
}
public static Integer getPageSize() {
return PAGE_SIZE;
}
public void setPageSize(Integer pageSize) {
PAGE_SIZE = pageSize;
}
}
分页service方法
以下以查看所有订单为例,其service层方法实现根据当前页数pageNo和每页显示条目数pageSize,获取该页的订单信息。首先创建Page类的对象,然后根据dao层方法查询得到pageTotal和items,给page所有属性赋值完成后,再返回给servlet。
/**
* 查看所有订单(分页)
*/
@Override
public Page<Order> page(int pageNo, int pageSize) {
Page<Order> page = new Page<Order>();
//设置5属性
// 每页数量
page.setPageSize(pageSize);
// 总记录数
Integer pageTotalCount = orderDao.queryForPageTotalCount();
page.setPageTotalCount(pageTotalCount);
// 总页码
Integer pageTotal = pageTotalCount / pageSize;
if (pageTotalCount % pageSize > 0){
pageTotal++;
}
page.setPageTotal(pageTotal);
// 当前页
page.setPageNo(pageNo);
// 当前页数据
Integer begin = (pageNo-1)*pageSize;//开始索引
List<Order> items = orderDao.queryForPageItems(begin,pageSize);
page.setItems(items);
return page;
}
分页servlet方法
servlet获取用户要跳转的页数pageNo,以及固定的pageSize,调用service方法,service方法将查询好的page对象返回给servlet,将其保存到request域中(pageOrder),请求转发jsp。
这里的url属性是jsp页面上点击跳转页码时的访问当前servlet方法的路径,因为除了pageNo不同以外均相同,可以封装起来。
/**
* 查询所有订单--分页
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showAllOrdersByPage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("查询所有订单分页");
// 获取当前页数和每页的条目数量
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = 4;
// 生成当前页的对象
Page<Order> page = orderService.page(pageNo, pageSize);
// 将请求跳转页面(点击下一页跳回到本页)的地址写入url
page.setUrl("orderServlet?action=showAllOrdersByPage");
// 保存到请求域
req.setAttribute("pageOrder",page);
// 请求转发
req.getRequestDispatcher("/pages/manager/order_manager.jsp").forward(req,resp);
}
jsp页面
jsp根据请求域中保存的pageOrder,使用jstl进行遍历,显示当前页面每个订单的信息。
<div id="main">
<table>
<tr>
<td>日期</td>
<td>金额</td>
<td>详情</td>
<td>发货</td>
</tr>
<c:forEach items="${requestScope.pageOrder.items}" var="order">
<tr>
<td>${order.createTime}</td>
<td>${order.price}</td>
<td><a href="orderServlet?action=showOrderDetail&orderId=${order.orderId}">查看详情</a></td><%--根据 订单id 查看--%>
<td><span>
<input class="sendBtn" type="button" orderId="${order.orderId}" status="${order.status}" value="点击发货">
</span>
</td>
</tr>
</c:forEach>
</table>
<%--分页--%>
<%@include file="/pages/common/page_order_naiv.jsp"%>
</div>
这样显示全部订单的功能就完成了。但是我们希望“点击发货”按钮之后,这个位置会显示发货成功或失败的状态,这就是下面介绍的发货功能。
3. Ajax实现发货功能
接下来使用Ajax实现发货功能。
发货:订单有未发货、已发货、已签收三个状态,分别用0,1,2表示。在上面jsp的发货一栏,管理员可以选择点击发货,也就是更改订单的状态。通过“点击发货”按钮,servlet获取该订单的订单id以及当前的状态,在数据库更改该订单的状态(变成已发货状态),然后跳转到当前页面,将“点击发货”的按钮变成“发货成功,待签收”的提示。
jQuery的Ajax代码
因为只涉及页面局部的变化,使用Ajax可以方便的实现。这里需要注意在 Ajax里面无法直接使用$(this) ,需要先定义变量document保存$(this)。
<script type="text/javascript">
$(function (){
$(".sendBtn").click(function(){/*修改发货*/
var document = $(this);// ajax里面不能直接用$(this)
// 点击发货
var orderId = $(this).attr("orderId");
var status = $(this).attr("status");
// alert("ss"+status);
$.getJSON("http://localhost:8080/07_book/orderServlet?action=sendOrder","orderId="+orderId+"&status="+status,function(data){
// 传过来的是字符串
// alert(document.parent().html())
document.parent().html(data);//在按钮的位置写新的状态
});
});
});
</script>
servlet方法返回json字符串
json是一种轻量级的数据交换格式。如果发货成功,就传一个“发货成功 待签收”json字符串给客户端。浏览器接收到之后,点击发货按钮的位置会变成“发货成功 待签收”。
protected void sendOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("修改发货状态...");
// 获取订单id,当前状态
String orderId = req.getParameter("orderId");
String status = req.getParameter("status");
int stat = WebUtils.parseInt(status, -1);
// 结果
if (stat!=0){
resp.getWriter().write(new Gson().toJson("已发货"));
} else{
stat = 1;
int res = orderService.updateStatus(orderId, stat);
if (res!=0){
System.out.println("发货成功");
resp.getWriter().write(new Gson().toJson("发货成功 待签收"));
}else{
resp.getWriter().write(new Gson().toJson("发货失败"));
}
}
}
刷新页面的问题
如果此时刷新页面,就会重新显示“点击发货”按钮。刷新会重新请求查询所有订单方法,数据库内容(发货状态)发生了变化,所以请求域的page对象发生了变化,但jsp仍然是原来的“点击发货”按钮。
所以在jsp显示订单的时候,需要判断当前的发货状态来显示不同状态的内容,也就是用jstl的条件判断order.status。
<div id="main">
<table>
<tr>
<td>日期</td>
<td>金额</td>
<td>详情</td>
<td>发货</td>
</tr>
<c:forEach items="${requestScope.pageOrder.items}" var="order">
<tr>
<td>${order.createTime}</td>
<td>${order.price}</td>
<td><a href="orderServlet?action=showOrderDetail&orderId=${order.orderId}">查看详情</a></td><%--根据 订单id 查看--%>
<td><span class="sendOrder">
<c:if test="${order.status==0}"><%--发货--%>
<input class="sendBtn" type="button" orderId="${order.orderId}" status="${order.status}" value="点击发货">
</c:if>
<c:if test="${order.status==1}">
发货成功 待签收
</c:if>
<c:if test="${order.status==2}">
已签收
</c:if>
</span>
</td>
</tr>
</c:forEach>
</table>
<%--分页--%>
<%@include file="/pages/common/page_order_naiv.jsp"%>
</div>