day10-功能实现09
家居网购项目实现09
以下皆为部分代码,详见 https://github.com/liyuelian/furniture_mall.git
21.功能20-修改购物车
21.1需求分析/图解
![image-20221226211829037](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221226211829037.png)
- 进入购物车页面,可以修改购买数量
- 更新该商品的金额
- 更新购物车商品数量和总金额
21.2思路分析
![](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/%E4%BF%AE%E6%94%B9-%E5%88%A0%E9%99%A4-%E6%B8%85%E7%A9%BA%E8%B4%AD%E7%89%A9%E8%BD%A6.png)
21.3代码实现
21.3.1entity层
Cart.java
增加方法updateCount()
/**
* 根据家居id和count,修改指定cartItem的数量和总价
*
* @param id 家居id
* @param count 指定id的家居的数量
*/
public void updateCount(int id, int count) {
//获取指定的cartItem
CartItem item = items.get(id);
if (null != item) {//如果cartItem不为空
//更新数量
item.setCount(count);
//某家居总价 = 单价 * 数量(为了安全使用get方法获取数量count)
item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));
}
}
21.3.2web层
CartServlet.java
增加方法updateCount()
/**
* 更新购物车的某个家居数量
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int id = DataUtils.parseInt(req.getParameter("id"), 0);
int count = DataUtils.parseInt(req.getParameter("count"), 1);
//获取session中的购物车
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (null != cart) {
cart.updateCount(id, count);
}
//回到请求更新家居购物车的页面
resp.sendRedirect(req.getHeader("Referer"));
}
21.3.3前端
cart.jsp
添加绑定事件,当点击修改对应的家居数量时,向服务器发送更新家居信息的请求
<script type="text/javascript">
$(function () {
/*--------------------------
Cart Plus Minus Button
----------------------------*/
var CartPlusMinus = $(".cart-plus-minus");
CartPlusMinus.prepend('<div class="dec qtybutton">-</div>');
CartPlusMinus.append('<div class="inc qtybutton">+</div>');
$(".qtybutton").on("click", function () {
var $button = $(this);
var oldValue = $button.parent().find("input").val();
if ($button.text() === "+") {
var newVal = parseFloat(oldValue) + 1;
} else {
// Don't allow decrementing below zero
if (oldValue > 1) {
var newVal = parseFloat(oldValue) - 1;
} else {
newVal = 1;
}
}
$button.parent().find("input").val(newVal);
var furnId = $button.parent().find("input").attr("furnId");
//发出修改购物车的请求
location.href =
"cartServlet?action=updateCount&count=" + newVal + "&id=" + furnId;
});
})
</script>
21.4完成测试
![image-20221227180751201](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227180751201.png)
修改家居数量:
![image-20221227180837612](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227180837612.png)
22.功能21-删除/清空购物车
22.1需求分析/图解
![image-20221227185033474](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227185033474.png)
- 进入购物车,可以删除某商品
- 可以清空购物车
- 要求该出适当的确认信息
22.2思路分析
见21.2思路分析图
22.3代码实现
22.3.1entity层
Cart.java添加删除购物车家居项的方法delItem()
/**
* 根据家居id删除对应的cartItem
*
* @param id 家居id
*/
public void delItem(int id) {
items.remove(id);
}
增加清空方法clear()
/**
* 清空items
*/
public void clear() {
items.clear();
}
22.3.2web层
CartServlet.java添加方法delItem()
/**
* 根据id删除购物车的某个家居信息
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void delItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int id = DataUtils.parseInt(req.getParameter("id"), 0);
//获取session中的购物车
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (null != cart) {
cart.delItem(id);
}
//回到请求删除家居项的购物车页面
resp.sendRedirect(req.getHeader("Referer"));
}
增加clear()方法
/**
* 清空购物车
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void clear(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session的购物车
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (null != cart) {
cart.clear();
}
//回到请求清空家居项的购物车页面
resp.sendRedirect(req.getHeader("Referer"));
}
22.3.3前端
cart.jsp
//清空购物车绑定确认事件
$("a.clearCart").click(function () {
return window.confirm("你确认要清空购物车吗?")
})
//给删除购物车绑定事件
$("a.delItem").click(function () {
//获取要删除的家居名
var furnName = $(this).parent().parent().find("td:eq(1)").text();
//使用确认弹窗
//点击确认,返回true,点击取消,返回false
return window.confirm("你确认要删除" + furnName + "吗?")
})
![image-20221227192117207](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227192117207.png)
![image-20221227194032831](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227194032831.png)
22.4完成测试
删除购物车项:
![image-20221227192245013](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227192245013.png)
![image-20221227192257075](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227192257075.png)
清空购物车:
![image-20221227194405872](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227194405872.png)
![image-20221227194415362](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227194415362.png)
23.功能22-生成订单
23.1需求分析/图解
![image-20221227195655878](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227195655878.png)
![image-20221227204858133](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227204858133.png)
![image-20221227201044836](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227201044836.png)
![image-20221227201115765](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221227201115765.png)
- 进入购物车,点击购物车结账
- 生成订单和订单项
- 如果会员没有登录,则先进入登录页面,完成登录后再结账
23.2思路分析
![](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/%E7%94%9F%E6%88%90%E8%AE%A2%E5%8D%952.png)
23.3代码实现
23.3.1设计order和order_item表
创建订单表和订单项表
###
-- 设计 订单表order
-- 字段参考前端给的界面
-- mysql每个字段应尽量使用not null约束
-- 字段类型的设计应当和关联的表的字段保持一致
-- 外键是否要给?
-- 因为外键对效率有影响,应当从程序的业务保证数据的一致性,而不是db层保证
CREATE TABLE `order`(
`id` VARCHAR(64) PRIMARY KEY, #订单号,主键
`create_time` DATETIME NOT NULL, #订单生成时间
`price` DECIMAL(11,2) NOT NULL, #订单的总金额,类型应该和furn表的price类型对应
`status` TINYINT NOT NULL, #订单状态 0-未发货 1-已发货 2-已结账
`member_id` INT NOT NULL #从属的会员id -不需要外键约束
)CHARSET utf8 ENGINE INNODB;
###
-- 设计 订单项表order_item
CREATE TABLE `order_item`(
`id` INT PRIMARY KEY AUTO_INCREMENT, #订单项id,自增长
`name` VARCHAR(64) NOT NULL, #家居名
`price` DECIMAL(11,2) NOT NULL, #家居单价
`count` INT NOT NULL, #某项的家居数量
`total_price` DECIMAL(11,2) NOT NULL, #订单项 的总价
`order_id` VARCHAR(64) NOT NULL #从属的订单号
)CHARSET utf8 ENGINE INNODB;
23.3.2entity层
Order.java
package com.li.furns.entity;
import java.math.BigDecimal;
import java.util.Date;
/**
* Order表示一个订单
*
* @author 李
* @version 1.0
*/
public class Order {
//属性
private String id;
private Date createTime;
private BigDecimal price;
private Integer status;
private Integer memberId;
public Order() {
}
public Order(String id, Date createTime, BigDecimal price, Integer status, Integer memberId) {
this.id = id;
this.createTime = createTime;
this.price = price;
this.status = status;
this.memberId = memberId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
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 getMemberId() {
return memberId;
}
public void setMemberId(Integer memberId) {
this.memberId = memberId;
}
@Override
public String toString() {
return "Order{" +
"id='" + id + '\'' +
", createTime=" + createTime +
", price=" + price +
", status=" + status +
", memberId=" + memberId +
'}';
}
}
OrderItem.java
package com.li.furns.entity;
import java.math.BigDecimal;
/**
* @author 李
* @version 1.0
*/
public class OrderItem {
private Integer id;
private String name;
private BigDecimal price;
private Integer count;
private BigDecimal totalPrice;
private String orderId;
public OrderItem() {
}
public OrderItem(Integer id, String name, BigDecimal price, Integer count, BigDecimal totalPrice, String orderId) {
this.id = id;
this.name = name;
this.price = price;
this.count = count;
this.totalPrice = totalPrice;
this.orderId = orderId;
}
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 getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
@Override
public String toString() {
return "OrderItem{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", count=" + count +
", totalPrice=" + totalPrice +
", orderId='" + orderId + '\'' +
'}';
}
}
23.3.3dao层
OrderDAO接口
package com.li.furns.dao;
import com.li.furns.entity.Order;
/**
* @author 李
* @version 1.0
*/
public interface OrderDAO {
/**
* 将传入的order对象保存到数据表order表
*
* @param order order对象
* @return 返回操作影响的行数
*/
public int saveOrder(Order order);
}
OrderDAOImpl实现类
package com.li.furns.dao.impl;
import com.li.furns.dao.BasicDAO;
import com.li.furns.dao.OrderDAO;
import com.li.furns.entity.Order;
/**
* @author 李
* @version 1.0
*/
public class OrderDAOImpl extends BasicDAO<Order> implements OrderDAO {
@Override
public int saveOrder(Order order) {
String sql =
"INSERT INTO `order`(`id`,`create_time`,`price`,`status`,`member_id`) " +
"VALUES(?,?,?,?,?)";
return update(sql, order.getId(), order.getCreateTime(),
order.getPrice(), order.getStatus(), order.getMemberId());
}
}
test包-OrderDAOImplTest测试
package com.li.furns.test;
import com.li.furns.dao.OrderDAO;
import com.li.furns.dao.impl.OrderDAOImpl;
import com.li.furns.entity.Order;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author 李
* @version 1.0
*/
public class OrderDAOImplTest {
private OrderDAO orderDAO = new OrderDAOImpl();
@Test
public void saveOrder() {
Order order = new Order("sn000002", new Date(),
new BigDecimal(300), 0, 1);
System.out.println(orderDAO.saveOrder(order));
}
}
![image-20221228183909748](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228183909748.png)
OrderItemDAO接口
package com.li.furns.dao;
import com.li.furns.entity.OrderItem;
/**
* OrderItemDAO表示一个订单项
*
* @author 李
* @version 1.0
*/
public interface OrderItemDAO {
/**
* 将传入的orderItem对象保存到order_item表中
*
* @param orderItem
* @return
*/
public int saveOrderItem(OrderItem orderItem);
}
OrderItemDAOImpl
package com.li.furns.dao.impl;
import com.li.furns.dao.BasicDAO;
import com.li.furns.dao.OrderItemDAO;
import com.li.furns.entity.OrderItem;
/**
* @author 李
* @version 1.0
*/
public class OrderItemDAOImpl extends BasicDAO<OrderItem> implements OrderItemDAO {
@Override
public int saveOrderItem(OrderItem orderItem) {
String sql =
"INSERT INTO `order_item`(`id`,`name`,`price`,`count`,`total_price`,`order_id`) " +
"VALUES(?,?,?,?,?,?);";
return update(sql, orderItem.getId(), orderItem.getName(), orderItem.getPrice(),
orderItem.getCount(), orderItem.getTotalPrice(), orderItem.getOrderId());
}
}
test包-OrderItemDAOImplTest
package com.li.furns.test;
import com.li.furns.dao.OrderItemDAO;
import com.li.furns.dao.impl.OrderItemDAOImpl;
import com.li.furns.entity.OrderItem;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
/**
* @author 李
* @version 1.0
*/
public class OrderItemDAOImplTest {
private OrderItemDAO orderItemDAO = new OrderItemDAOImpl();
@Test
public void saveOrderItem() {
OrderItem orderItem = new OrderItem(null, "北欧小沙发", new BigDecimal(200),
3, new BigDecimal(600), "sn000001");
System.out.println(orderItemDAO.saveOrderItem(orderItem));
}
}
![image-20221228190713106](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228190713106.png)
23.3.4service层
OrderService接口
package com.li.furns.service;
import com.li.furns.entity.Cart;
/**
* @author 李
* @version 1.0
*/
public interface OrderService {
/**
* 1.生成订单
* 2.订单是根据购物车来生成的,cart对象在session,通过web层,传入saveOrder
* 3.订单和会员id关联
*
* @param cart 购物车
* @param memberId 会员id
* @return 返回生成的订单号
*/
public String saveOrder(Cart cart, int memberId);
}
OrderServiceImpl实现类
package com.li.furns.service.impl;
import com.li.furns.dao.FurnDAO;
import com.li.furns.dao.OrderDAO;
import com.li.furns.dao.OrderItemDAO;
import com.li.furns.dao.impl.FurnDAOImpl;
import com.li.furns.dao.impl.OrderDAOImpl;
import com.li.furns.dao.impl.OrderItemDAOImpl;
import com.li.furns.entity.*;
import com.li.furns.service.OrderService;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author 李
* @version 1.0
*/
public class OrderServiceImpl implements OrderService {
private OrderDAO orderDAO = new OrderDAOImpl();
private OrderItemDAO orderItemDAO = new OrderItemDAOImpl();
private FurnDAO furnDAO = new FurnDAOImpl();
@Override
public String saveOrder(Cart cart, int memberId) {
//这里的业务相对而言比较综合
//任务:将cart购物车的数据以order和orderItem的形式保存到数据库中
/**
* 因为生成订单会操作多张表,设计到多表事务问题,使用ThreadLocal+MySQL事务机制+过滤器Filter
* 因为事务处理考虑的点比较多,这里先不处理,后面专门处理
*/
//1.构建一个对应的order对象
//先生成一个UUID,表示当前的订单号。要保证订单号是唯一的
String orderId = System.currentTimeMillis() + "" + memberId;
Order order = new Order(orderId, new Date(), cart.getCartTotalPrice(), 0, memberId);
//将order对象保存到数据库order表中
orderDAO.saveOrder(order);
//2.通过cart对象,遍历出CartItem家居项,构建orderItem对象
HashMap<Integer, CartItem> items = cart.getItems();
//遍历hm
/**
* 遍历方法一:entrySet
* 通过entrySet的方法遍历items,取出cartItem
* for(Map.Entry<Integer, CartItem> entry : items.entrySet()) {
* CartItem item = entry.getValue();
* ...
* }
*/
//遍历方法二:keySet
Set<Integer> keys = items.keySet();
for (Integer id : keys) {
//获取家居项
CartItem item = items.get(id);
//构建orderItem对象
OrderItem orderItem = new OrderItem(null, item.getName(), item.getPrice(),
item.getCount(), item.getTotalPrice(), orderId);
//将orderItem对象保存到数据库表order_item中
orderItemDAO.saveOrderItem(orderItem);
//更新furn表的对应记录的sales销量,stock存量
//(1)获取到furn对象
Furn furn = furnDAO.queryFurnById(id);
//(2)更新furn对象的sales销量,stock存量
furn.setSales(furn.getSales() + item.getCount());
furn.setStock(furn.getStock() - item.getCount());
//(3)更新到数据表中
furnDAO.updateFurn(furn);
}
//清空购物车
cart.clear();
//返回生成的订单号
return orderId;
}
}
test包-OrderServiceImplTest测试
package com.li.furns.test;
import com.li.furns.entity.Cart;
import com.li.furns.entity.CartItem;
import com.li.furns.service.OrderService;
import com.li.furns.service.impl.OrderServiceImpl;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
/**
* @author 李
* @version 1.0
*/
public class OrderServiceImplTest {
private OrderService orderService = new OrderServiceImpl();
@Test
public void saveOrder() {
//构建cart对象
Cart cart = new Cart();
cart.addItem(new CartItem(1, "北欧风格小桌子", new BigDecimal(180), 10, new BigDecimal(1800)));
cart.addItem(new CartItem(26, "简约风格小椅子", new BigDecimal(100), 5, new BigDecimal(500)));
String orderId = orderService.saveOrder(cart, 2);
System.out.println(orderId);
}
}
![image-20221228201239274](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228201239274.png)
furn表对应的家居项:
-测试前
![image-20221228200854786](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228200854786.png)
-测试后
![image-20221228200942578](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228200942578.png)
order表:
![image-20221228201410295](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228201410295.png)
order_item表:
![image-20221228201455336](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228201455336.png)
23.3.5web层
配置OrderServlet
<servlet>
<servlet-name>OrderServlet</servlet-name>
<servlet-class>com.li.furns.web.OrderServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>OrderServlet</servlet-name>
<url-pattern>/orderServlet</url-pattern>
</servlet-mapping>
OrderServlet:
package com.li.furns.web;
import com.li.furns.entity.Cart;
import com.li.furns.entity.Member;
import com.li.furns.service.OrderService;
import com.li.furns.service.impl.OrderServiceImpl;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
/**
* @author 李
* @version 1.0
*/
public class OrderServlet extends BasicServlet {
private OrderService orderService = new OrderServiceImpl();
/**
* 生成订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void saveOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取购物车cart
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (null == cart || cart.isEmpty()) {//购物车不存在session或者购物车没有任何东西
// 转发到首页
req.getRequestDispatcher("/index.jsp").forward(req, resp);
return;//结束业务
}
//获取当前登录的memberId
Member member = (Member) req.getSession().getAttribute("member");
if (null == member) {//如果没有登录
//转发到登录页面
req.getRequestDispatcher("/views/member/login.jsp").forward(req, resp);
return;//结束业务
}
//生成订单
String orderId = orderService.saveOrder(cart, member.getId());
req.getSession().setAttribute("orderId", orderId);
//使用重定向到checkout.jsp页面
resp.sendRedirect(req.getContextPath() + "/views/order/checkout.jsp");
}
}
修改前端接口:略
23.4完成测试
未登录状态下添加家居到购物车,点击生成账单,请求转发登录页面
![image-20221228221535569](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228221535569.png)
![image-20221228221609192](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228221609192.png)
点击登录后重新返回购物车页面,点击生成订单,生成订单成功
![image-20221228221748394](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228221748394.png)
数据库插入成功
order表
![image-20221228221841883](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228221841883.png)
order_item表
![image-20221228221939433](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228221939433.png)
furn表
![image-20221228222027320](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221228222027320.png)