javaweb属实是比蓝桥杯好玩,算法都不学,话说,明天就要转线下上课了
在这个阶段,我们需要完成3个模块、用户模块
、购物车模块
、订单模块
我们找到登录的Servlet,src/main/java/com/flzj/web/UserServlet.java
在请求转发之前,把loginuser
存到Session
域中,request.getSession().setAttribute("user",loginUser )
public class UserServlet extends BaseServlet {
private UserService userService = new UserServiceImpl();
protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取请求的参数
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("接收到用户的账为"+username+"\t"+password);
// 2、调用XxxService. xxx()处理业务
// userService. login(登录
User loginUser = userService.login(new User(null, username, password, null));
// 3、根据login()方法返回结果判断登录是否成功
if(!(loginUser == null)) {
// 成功
// 跳到成功页面login_ success. html
// 保存用户登入的信息到Session域中
request.getSession().setAttribute("user",loginUser);
System.out.println(username + " 登入成功");
request.getRequestDispatcher("/pages/user/login_success.jsp").forward(request,response);
}else {
// 失败
...
}
在src/main/webapp/pages/common/login_success_menu.jsp
,我们修改展示出来的效果
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>
<span>欢迎<span class="um_span">${sessionScope.user.username}</span>光临尚硅谷书城</span>
<a href="pages/order/order.jsp">我的订单</a>
<a href="index.jsp">注销</a>
<a href="index.jsp">返回</a>
</div>
同时首页也需要更改,我们登入以后,一般的网站首页,没有 登入 | 注册
这一选项,效果如下图
我们需要来到src/main/webapp/pages/client/index.jsp
,进行一整猛改
<div id="header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
<span class="wel_word">网上书城</span>
<div>
<%--如果用户还没有登入,显示【登入 和 注册 的菜单】--%>
<c:if test="${empty sessionScope.user}">
<a href="pages/user/login.jsp">登录</a> |
<a href="pages/user/regist.jsp">注册</a>
</c:if>
<%--如果用户已经登入,显示登入成功的信息--%>
<c:if test="${not empty sessionScope.user}">
<span>欢迎<span class="um_span">${sessionScope.user.username}</span>光临尚硅谷书城</span>
<a href="pages/order/order.jsp">我的订单</a>
<a href="index.jsp">注销</a>
</c:if>
<a href="pages/cart/cart.jsp">购物车</a>
<a href="pages/manager/manager.jsp">后台管理</a>
</div>
</div>
1、销毁Session中用户登入的信息
2、重定向到首页(或登入界面)
又回到,我们的src/main/java/com/flzj/web/UserServlet.java
,这次,我们完善logout()
方法
protected void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、销毁Session中用户登入的信息
request.getSession().invalidate();
// 2、重定向到首页(或登入界面)
response.sendRedirect(request.getContextPath());
}
同时在src/main/webapp/pages/common/login_success_menu.jsp
和 src/main/webapp/pages/client/index.jsp
的
<a href="userServlet?action=logout">注销a>
表单重复提交有三种常见的情况:
一、提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键F5
,就会发起最后一次的请求。造成表单重复提交问题。
解决方法:使用重定向来进行跳转
二、用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败,就会着急,然后多点了几次提交操作,也会造成表单重复提交。
三、用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复提交。
这时候,家人们就会问了,二、三怎么解决,我们可以通过验证码,来解决,下面是图解
这里我们用谷歌 kaptcha 图片验证
1、导包
kaptcha-2.3.2.jar
2、在
web.xml
中去配置用于生成验证码的Servlet程序
<servlet>
<servlet-name>KaptchaServletservlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>KaptchaServletservlet-name>
<url-pattern>/kaptcha.jpgurl-pattern>
servlet-mapping>
3、在表当中使用img标签去显示验证图片并使用
我们来到src/main/webapp/pages/user/regist.jsp
,在验证码部分,修改src属性,和小改下style属性,使验证码更好看
<input class="itxt" type="text" style="width: 80px;" id="code"
name="code"
/>
<img alt="" src="kaptcha.jpg" style="float: right; margin-right: 40px ; width: 100px ; height: 28px">
4、在服务器获取谷歌生成的验证码,和客户端发来的验证码进行比较
protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取Session中的验证码
String token = (String)request.getSession().getAttribute(KAPTCHA_SESSION_KEY);
// 删除Session中的验证码
request.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
// 1获取请求的参数
...
// 2对验证码进行判断
if(token != null && token.equals(code)) {
// 正确
// 3、检查用户名是否可用
if(!userService.existsUsername(username)) {
// 可用
// 调用Sservice保存到数据库
//userService.registUser(new User(null,username,password,email));
// 跳到注册成功末面regist_success.jsp
request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request,response);
// ...
效果
在src/main/webapp/pages/user/regist.jsp
下,给图片一个class,并给它绑定上单击事件
<img id="code_img" src="kaptcha.jpg" style="float: right; margin-right: 40px ; width: 100px ; height: 28px">
$("#code_img").click(function (){
// 在事件响应的function函数中有一个this对象。这个this对象,是当前正在响应事件的dom对象
// src属性表示验证码的img标签的 图片路径 它可读可写
this.src = "${basePath}kaptcha.jpg?d=" + new Date();
});
这时候,好奇的家人们,会问了,为什么要加new Date()这个时间戳呢,我们是为了解决浏览器缓存,导致点击图片不更换验证码的事情发生,如下图所示
对购物车模块进行分析
购物车的商品项
public class CartItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
// construcet、getXxx、setXxx、toString 省略
}
购物车对象
public class Cart {
private Integer totalCount;
private BigDecimal totalPrice;
private List<CartItem> items = new ArrayList<CartItem>();
// getXxx、setXxx、toString 省略
}
实现
private Map<Integer,CartItem> items = new LinkedHashMap<Integer,CartItem>();
/**
* 添加商品
* @param cartItem
*/
public void addItem(CartItem cartItem){
// 先查看购物车是否已添加过此商品,如果已添加,则数量累加,总金额更新,如果没有添加过,直接放到集合中即可
CartItem item = items.get(cartItem.getId());
if(item == null){
// 之前没添加过此商品
items.put(cartItem.getId(),cartItem);
}else{
// 已经添加过的情况
item.setCount(item.getCount() + 1); // 数量累加
item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount()))); // 更新总金额
}
}
/**
* 删除商品
* @param id
*/
public void deleteItem(Integer id){
items.remove(id);
}
/**
* 清空购物车
*
*/
public void clear(){
items.clear();
}
/**
* 修改商品数量
*
*/
public void updateCount(Integer id,Integer count){
// 先查看购物车是否有此商品,如果有,修改商品数量,更新总金额
CartItem cartItem = items.get(id);
if(cartItem != null){
cartItem.setCount(count);
cartItem.setTotalPrice(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount()))); // 更新总金额
}
}
我们会发现,之前写的private List
,不方便维护,所以我们改成Map
测试
CTRL + SHIFT + T 熟练了嗷
@Test
public void addItem() {
Cart cart = new Cart();
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(2,"书本2",1,new BigDecimal(100),new BigDecimal(100)));
System.out.println(cart);
}
@Test
public void deleteItem() {
Cart cart = new Cart();
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(2,"书本2",1,new BigDecimal(100),new BigDecimal(100)));
cart.deleteItem(1);
System.out.println(cart);
}
@Test
public void clear() {
Cart cart = new Cart();
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(2,"书本2",1,new BigDecimal(100),new BigDecimal(100)));
cart.clear();
System.out.println(cart);
}
@Test
public void updateCount() {
Cart cart = new Cart();
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.updateCount(1,10);
System.out.println(cart);
}
这里经过测试,我们发现,购物车对象(Cart.java
)里的setTotalCount
和 setTotalPrice
不能存在,并且我们要对get
方法进行修改,因此上面类的字段,totalCount
和 totalPrice
, 可以变成局部变量
public Integer getTotalCount() {
Integer totalCount = 0;
for(Map.Entry<Integer,CartItem> entry : items.entrySet()){
totalCount += entry.getValue().getCount();
}
return totalCount;
}
public BigDecimal getTotalPrice() {
BigDecimal totalPrice = new BigDecimal(0);
for(Map.Entry<Integer,CartItem> entry : items.entrySet()){
totalPrice = totalPrice.add(entry.getValue().getTotalPrice());
}
return totalPrice;
}
编写CartServlet
完成 addItem()
方法 ,这里请求重定向,有一个细节,请求头,有一个参数Referer
,是浏览器搜索栏的地址 ,这样就可以让我们每次添加商品,跳转的页面,是当前的页面
protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("商品编号" + req.getParameter("id"));
// 获取请求的商品编号
int id = WebUtils.parseInt(req.getParameter("id"), 0);
// 调用bookService.queryBookById(id):Book得到图书的信息
Book book = bookService.queryBookById(id);
// 把图书信息,转换成为CartItem商品项
CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice());
// 调用Cart.addItem(CartItem) 添加商品项
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart == null){
cart = new Cart();
req.getSession().setAttribute("cart",cart);
}
// 重定向回原来商品所在列表页面
resp.sendRedirect(req.getHeader("Referer"));
}
<servlet>
<servlet-name>CartServletservlet-name>
<servlet-class>com.flzj.web.CartServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>CartServletservlet-name>
<url-pattern>/cartServleturl-pattern>
servlet-mapping>
给加入购物车按钮
,绑定单击事件
<button bookId="${book.id}" class="addToItem">加入购物车</button>
<script>
$(function (){
$("button.addToItem").click(function (){
var bookId = $(this).attr("bookId");
location.href = "http://localhost:8080/book/cartServlet?action=addItem&id="+bookId;
});
});
</script>
我们先来到src/main/webapp/pages/cart/cart.jsp
,小小修改下,循环读取Session域中的商品项,判断否为空,展示不同的页面效果
<div id="main">
<table>
<tr>
<td>商品名称</td>
<td>数量</td>
<td>单价</td>
<td>金额</td>
<td>操作</td>
</tr>
<%--商品为空--%>
<c:if test="${empty sessionScope.cart.items}">
<td colspan="5"><a href="index.jsp">当前购物车为空!,快去浏览商品吧</a>></td>
</c:if>
<%--商品非空--%>
<c:if test="${not empty sessionScope.cart.items}">
<c:forEach items="${sessionScope.cart.items}" var="entry">
<tr>
<td>${entry.value.name}</td>
<td>${entry.value.count}</td>
<td>${entry.value.price}</td>
<td>${entry.value.totalPrice}</td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>
</c:if>
</table>
<%--商品非空--%>
<c:if test="${not empty sessionScope.cart.items}">
<div class="cart_info">
<span class="cart_span">购物车中共有<span class="b_count">${sessionScope.cart.totalCount}</span>件商品</span>
<span class="cart_span">总金额<span class="b_price">${sessionScope.cart.totalPrice}</span>元</span>
<span class="cart_span"><a href="#">清空购物车</a></span>
<span class="cart_span"><a href="pages/cart/checkout.jsp">去结账</a></span>
</div>
</c:if>
</div>
src/main/webapp/pages/cart/cart.jsp
, 我们对删除的a标签,链接跳转到CartServlet
执行deleteItem()
方法,需要传商品的ID,同时添加提醒
<td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${entry.value.id}">删除</a></td>
<script>
$(function (){
// 给【删除】绑定单击事件
$("a.deleteItem").click(function (){
return confirm("你确定要删除【"+ $(this).parent().parent().find("td:first").text() +"】吗?")
})
});
</script>
deleteItem()
方法
protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取商品编号
int id = WebUtils.parseInt(req.getParameter("id"), 0);
// 获取购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart != null){
// 删除 了购物车商品项
cart.deleteItem(id);
// 重定向回原来的地址
resp.sendRedirect(req.getHeader("Referer"));
}
}
这里,我们又要在src/main/webapp/pages/cart/cart.jsp
下,就行修改,给清空购物车对应的标签,加上href和id,方便调用CartServlet
的clear()
方法和清空提醒
<span class="cart_span"><a id="clearCart" href="cartServlet?action=clear">清空购物车</a></span>
// 给【清空】添加单击事件
$("#clearCart").click(function (){
return confirm("你确定要,清空购物车吗?")
})
CartServlet
的clear()
方法
protected void clear(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart != null){
// 清空购物车
cart.clear();
// 重定向回原来购物车展示页面
resp.sendRedirect(req.getHeader("Referer"));
}
}
图解
src/main/webapp/pages/cart/cart.jsp
,添加个输入框,并给输入框添加一个onchange
事件
<td>
<input class="updateCount" style="width:80px;"
bookId="${entry.value.id}"
type="text" value="${entry.value.count}">
</td>
// 给输入框绑定onchange事件
$(".updateCount").change(function (){
// 获取商品名和id
var name = $(this).parent().parent().find("td:first").text();
var id = $(this).attr('bookId');
// 获取商品数量
var count = this.value;
if(confirm("你确定要将【"+ name +"】商品,修改数量为:" + count + "吗?")){
// 发起请求。服务器保存修改
location.href = "http://localhost:8080/book/cartServlet?action=updateItem&id="+id+"&count="+count;
}else{
// defaultValue属性是表单项Dom对象的属性。它表示默认的value属性值
this.value = this.defaultValue;
}
});
updateCount()
方法
protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数,商品编号,商品数量
int id = WebUtils.parseInt(req.getParameter("id"),0);
int count = WebUtils.parseInt(req.getParameter("count"), 1);
// 获取Cart购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart != null){
// 修改商品数量
cart.updateCount(id,count);
// 重定向回原来购物车展示页面
resp.sendRedirect(req.getHeader("Referer"));
}
}
我们修改CartServlet
的addItem()
方法,往seesion
域中存储最后添加的书
req.getSession().setAttribute("lastName",cartItem.getName());
然后在首页中显示
<div style="text-align: center">
<%-- 购物车为空 --%>
<c:if test="${empty sessionScope.cart.items}">
<span></span>
<div>
<span style="color: red">当前购物车为空</span>
</div>
</c:if>
<%-- 购物车非空 --%>
<c:if test="${not empty sessionScope.cart.items}">
<span>您的购物车中有${sessionScope.cart.totalCount}件商品</span>
<div>
您刚刚将<span style="color: red">${sessionScope.lastName}</span>加入到了购物车中
</div>
</c:if>>
</div>
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`)
);
public class Order {
private String orderId;
private Date createTime;
private BigDecimal price;
// 0未发货,1已发货,2已签收
private Integer status = 0;
private Integer userId;
// construct、getXxx、setXxx、toStirng 省略
}
public class OrderItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
private String orderId;
// construct、getXxx、setXxx、toStirng 省略
}
这里我们主要完成saveOrder(Order)
方法 和 saveOrderItem(OrderItem)
方法
编写
oderDao
public interface OrderDao {
public int saveOrder(Order order);
}
orderDaoImpl
public class OrderDaoImpl extends BaseDao implements OrderDao {
@Override
public int saveOrder(Order order) {
String sql = "insert into t_order(`order_id`,`create_time`,`price`,`status`,`user_id`) values(?,?,?,?,?)";
return update(sql,order.getOrderId(),order.getCreateTime(),order.getPrice(),order.getStatus(),order.getUserId());
}
}
orderItem
public interface OrderItemDao {
public int saveOrderItem(OrderItem orderItem);
}
orderItemImpl
public class OrderItemDaoImpl extends BaseDao implements OrderItemDao {
@Override
public int saveOrderItem(OrderItem orderItem) {
String sql = "insert into t_order_item(`name`,`count`,`price`,`total_price`,`order_id`) values(?,?,?,?,?)";
return update(sql,orderItem.getName(),orderItem.getCount(),orderItem.getPrice(),orderItem.getTotalPrice(),orderItem.getOrderId());
}
}
测试
CTRL + SHIFT + T
public class OrderDaoTest {
OrderDao orderDao = new OrderDaoImpl();
@Test
public void saveOrder() {
orderDao.saveOrder(new Order("1",new Date(),new BigDecimal(100),0,1));
}
}
public class OrderItemDaoTest {
OrderItemDao orderItemDao = new OrderItemDaoImpl();
@Test
public void saveOrderItem() {
orderItemDao.saveOrderItem(new OrderItem(null,"java速通",1,new BigDecimal(100),new BigDecimal(100),"1"));
}
}
这里我们只要实现createOrder(Cart,UserId)
编写
OrderService
public interface OrderService {
public String createOrder(Cart cart,Integer userId);
}
OrderServiceImpl
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao = new OrderDaoImpl();
private OrderItemDao orderItemDao = new OrderItemDaoImpl();
@Override
public String createOrder(Cart cart, Integer userId) {
// 订单号 === 唯一性
String orderId = System.currentTimeMillis()+""+ userId;
// 创建一个订单对象
Order order = new Order(orderId,new Date(),cart.getTotalPrice(),0,userId);
// 保存订单
orderDao.saveOrder(order);
// 遍历购物车中每一个商品项转换成为订单项保存到数据库
for(Map.Entry<Integer, CartItem>entry : cart.getItems().entrySet()){
// 获取每一个购物车中的商品项
CartItem cartItem = entry.getValue();
// 转换为每一个订单项
OrderItem orderItem = new OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(),orderId);
// 保存订单到数据库
orderItemDao.saveOrderItem(orderItem);
}
// 清空购物车
cart.clear();
return orderId;
}
}
测试
@Test
public void createOrder() {
Cart cart = new Cart();
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(1,"书本1",1,new BigDecimal(100),new BigDecimal(100)));
cart.addItem(new CartItem(2,"书本2",1,new BigDecimal(100),new BigDecimal(100)));
OrderService orderService = new OrderServiceImpl();
System.out.println("订单号是" + orderService.createOrder(cart,1));
}
OrderServlet
实现createOrder()
方法
public class OrderServlet extends BaseServlet {
OrderService orderService = new OrderServiceImpl();
/**
* 生成订单
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void createOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 先获取Cart购物车对象
Cart cart = (Cart) request.getSession().getAttribute("cart");
// 获取loginUser对象
User loginUser = (User) request.getSession().getAttribute("user");
// 如果还没有登入,就跳转到登入页面
if(loginUser == null){
request.getRequestDispatcher("/pages/user/login.jsp").forward(request,response);
return;
}
// 获取UserId
Integer userId = loginUser.getId();
// 调用OrderService.createOrder(Cart,UserId) 生成订单
String orderId = orderService.createOrder(cart,userId);
request.setAttribute("orderId",orderId);
// 请求转发到/pages/cart/checkout.jsp
request.getRequestDispatcher("/pages/cart/checkout.jsp").forward(request,response);
}
}
<servlet>
<servlet-name>OrderServletservlet-name>
<servlet-class>com.flzj.web.OrderServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>OrderServletservlet-name>
<url-pattern>/orderServleturl-pattern>
servlet-mapping>
来到src/main/webapp/pages/cart/checkout.jsp
修改,生成的订单号
你的订单已结算,订单号为${requestScope.orderId}
在src/main/webapp/pages/cart/cart.jsp
使得跳转调用createOrder()
方法
<span class="cart_span"><a href="orderServlet?action=createOrder">去结账</a></span>
为了防止订单重复提交
我们改用请求重定向,来到 com/flzj/web/OrderServlet.java
,同时存在request域的值也要改成session域
// request.setAttribute("orderId",orderId);
// request.getRequestDispatcher("/pages/cart/checkout.jsp").forward(request,response);
// 修改为
request.getSession().setAttribute("orderId",orderId);
response.sendRedirect(request.getContextPath() + "/pages/cart/checkout.jsp");
同时在src/main/webapp/pages/cart/checkout.jsp
的取值,也要改成从session域中取
<h1>你的订单已结算,订单号为${sessionScope.orderId}h1>
结算导致库存和销量变化
src/main/java/com/flzj/service/impl/OrderServiceImpl.java
在createOrder()
方法,加上变化
private BookDao bookDao = new BookDaoImpl();
...
// 修改库存和商品
Book book = bookDao.queryBookById(cartItem.getId());
book.setSales(book.getSales() + cartItem.getCount() );
book.setStock(book.getStock() - cartItem.getCount() );
bookDao.updateBook(book);
...
蓝桥杯都不学,算法都不学