JavaWeb购物系统(五)购物车模块的实现

效果图

未添加商品效果图
JavaWeb购物系统(五)购物车模块的实现_第1张图片
添加商品之后的效果图
JavaWeb购物系统(五)购物车模块的实现_第2张图片

功能

  1. 添加商品
  2. 购物车中商品的数量增加、减少、通过键盘输入改变数量
  3. 清空购物车
  4. 计算购物车商品的总价格

正文

我们的购物车采用的是在服务端,即:使用session来存储。这样做的
缺点:无法永久存储,当服务端关闭的时候,会销毁。
优点:不用在本地数据库建相应的表。

说明

在开始之前我们得明白一些概念:在Java里每个实体都对应一个Java类。所以我们这里的购物车就是一个类,每个用户的购物车就是一个具体的对象。而购物车里边的每个商品就是一个购物项
我这里的购物车类是Car,购物项类ShopCarItem
JavaWeb购物系统(五)购物车模块的实现_第3张图片

jsp页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="com.entity.ShopCarItem" %>
<%@ page import="java.util.List" %>
<%@ page import="com.entity.User" %>
<%@ page import="com.myUtil.ProcessUtil" %>
<%@ page import="com.entity.Cart" %><%--
  Created by IntelliJ IDEA.
  User: huawei
  Date: 2022/10/18
  Time: 13:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>购物车</title>
    <script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
    <!--Bootstrap5 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/css/bootstrap.min.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">
        $(function () {
            // 清空购物车
            $("#removeAll").click(
                function () {
                    if (confirm("是否清空购物车") == true) {
                        $.post(
                            "/MyProject/cartProcessServlet",
                            {
                                method: "removeAll"
                            },
                            function (data) {
                                if ("success" == data){
                                    location.reload(true);
                                }
                            },
                            "json"
                        )
                    } else {
                        return false;
                    }

                }
            )

            // 数量加按钮
            $("button[id^='numSum_']").click(
                function (e) {
                    var goodId = e.target.id;
                    // 获取对应的数量值
                    var $number = $(document.getElementById("number_" + goodId.split("_")[1]));
                    $number.val(parseInt($number.val()) + 1);
                    var val = $number.val();
                    // 将修改后的值写入数据库
                    sendAjax(goodId,val);
                }
            )

            // 数量减按钮
            $("button[id^='numSub_']").click(
                function (e) {
                    var goodId = e.target.id;
                    // 获取对应的数量值
                    var $number = $(document.getElementById("number_" + goodId.split("_")[1]));
                    if ((parseInt($number.val()) -1) >= 1){
                        $number.val((parseInt($number.val()) -1));
                        sendAjax(goodId,$number.val());
                    }
                }
            )

            // 数值框内容变化监听
            $("input[id^='number_']").blur(
                function(e){
                    var id = e.target.id;
                    var val = $(document.getElementById("number_" + e.target.id.split("_")[1])).val();
                    if (!((/^[1-9]*[1-9][0-9]*$/).test(val))){ //如果不是是正整数
                        $(document.getElementById("number_" + e.target.id.split("_")[1])).val(1)
                    }
                    sendAjax(id,val);
                })

            // 发送Ajax请求(用于更改购物项信息)
            var sendAjax = function (goodId,count) {
                $.post(
                    "/MyProject/cartProcessServlet",
                    {
                        method: "updateShopCartItem",
                        goodId: goodId,
                        number: count
                    },
                    function (data) {
                        if ("success" == data){
                            location.reload(true);
                        }
                    },
                    "json"
                )
            }

            // 删除购物项
            $("button[id^='delete_']").click(
                function (e) {
                    var goodId = e.target.id.split("_")[1];
                    $.post(
                        "/MyProject/cartProcessServlet",
                        {
                            method: "removeShopItem",
                            goodId: goodId
                        },
                        function (data) {
                            if ("success" == data){
                                location.reload(true);
                            }
                        },
                        "json"
                    )
                }
            )



            // 结算
            $("#settlement").click(
                function () {
                    $.post(
                        "/MyProject/orderProcessServlet",
                        {
                            method: ""
                        },
                        function (data) {
                            if ("success" == data){
                                alert("订单生成成功!");
                                location.reload(true);
                            }
                        },
                        "json"
                    )
                }
            )

        })
    </script>
</head>
<body>
<%
    session.setAttribute("totalPrice",0.0);
%>
<%--头部导航栏--%>
<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.cart.items}">
    <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.cart.items}">
<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.cart.items}" var="shopCarItem">
                <tr style="vertical-align: middle !important;text-align: center;">
                    <td>
                        <img style="width: 100px;height: 100px" src="${shopCarItem.value.img}"/>
                        <span>${shopCarItem.value.goodsName}</span>
                    </td>
                    <td>${shopCarItem.value.price}</td>
                    <td class="col-lg-2">
                        <div class="input-group">
                             <span class="input-group-btn">
                                 <button class="btn btn-default" type="button" id="numSub_${shopCarItem.value.goodsId}">-</button>
                             </span>
                            <input type="text" class="form-control text-center" id="number_${shopCarItem.value.goodsId}" value="${shopCarItem.value.number}">
                            <span class="input-group-btn">
                               <button class="btn btn-default" type="button" id="numSum_${shopCarItem.value.goodsId}">+</button>
                             </span>
                        </div>
                    </td>
                    <td>${shopCarItem.value.totalPrice}</td>
                    <%--计算总价格--%>
                    <c:set scope="session" value="${sessionScope.totalPrice + shopCarItem.value.totalPrice}" var="totalPrice"></c:set>
                    <td><button class="btn btn-danger" id="delete_${shopCarItem.value.goodsId}">删除</button></td>
                </tr>
            </c:forEach>
        </table>
        <div class="row justify-content-between">
            <div class="col-4 m-auto">
                <a class="btn btn-primary btn-lg" href="/MyProject/index.jsp">
                    继续购物
                </a>
            </div>
            <div class="col-4">
                <button type="button" class="btn btn-warning btn-lg" id="removeAll">清空购物车</button>
                <span>
                    <span class="fs-3">
                        合计
                    </span>
                    <span class="fs-3 fw-bold" id="total" style="color: red">
                            ${sessionScope.totalPrice}
                            <button type="button" class="btn btn-dark " id="settlement">结算</button>
                    </span>
                </span>
            </div>
        </div>
    </div>
</div>
</c:if>

<div class="container">
    <%--footer--%>
    <div class="row align-items-end">
        <footer class="py-5" >
            <div class="row">
                <div class="col-2">
                    <h5>Section</h5>
                    <ul class="nav flex-column">
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Features</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Pricing</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">FAQs</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">About</a></li>
                    </ul>
                </div>

                <div class="col-2">
                    <h5>Section</h5>
                    <ul class="nav flex-column">
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Features</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Pricing</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">FAQs</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">About</a></li>
                    </ul>
                </div>

                <div class="col-2">
                    <h5>Section</h5>
                    <ul class="nav flex-column">
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Features</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Pricing</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">FAQs</a></li>
                        <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">About</a></li>
                    </ul>
                </div>
            </div>
            <div class="d-flex justify-content-between py-4 my-4 border-top">
                <p>&copy; 2021 Company, Inc. All rights reserved.</p>
                <ul class="list-unstyled d-flex">
                    <li class="ms-3"><a class="link-dark" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#twitter"/></svg></a></li>
                    <li class="ms-3"><a class="link-dark" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#instagram"/></svg></a></li>
                    <li class="ms-3"><a class="link-dark" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#facebook"/></svg></a></li>
                </ul>
            </div>
        </footer>
    </div>
</div>
</body>
</html>

购物商品的添加

这一部分,我们放到下一章节讲主页部分的时候在详细讲,因为商品购买是在主页。

商品数量的改变

公共部分函数的抽取

这里的参数,goodId也就是购物像的IDcount当前购物项的数量,如图:
JavaWeb购物系统(五)购物车模块的实现_第4张图片

// 发送Ajax请求(用于更改购物项信息)
var sendAjax = function (goodId,count) {
$.post(
   "/MyProject/cartProcessServlet",
   {
       method: "updateShopCartItem",
       goodId: goodId,
       number: count
   },
   function (data) {
       if ("success" == data){
           location.reload(true);
       }
   },
   "json"
)
}

这里我们因为都是局部数据的刷新,所以用到了大量的Ajax。废话不多说,我们上js代码解释:

商品数量的减(“-”按钮)

// 数量减按钮
$("button[id^='numSub_']").click(
    function (e) {
        var goodId = e.target.id;
        // 获取对应的数量值
        var $number = $(document.getElementById("number_" + goodId.split("_")[1]));
        if ((parseInt($number.val()) -1) >= 1){
            $number.val((parseInt($number.val()) -1));
            sendAjax(goodId,$number.val());
        }
    }
)

我们采用的思路是:获取对应购物项(每个商品是一个购物项)的唯一ID(这里ID的唯一性是通过商品的ID来确定的),然后删除购物车对象中的购物项。同样的,和上一章节留言板中删除留言一样,这里也是通过numSub_这样的前缀来标识这是一个“-”按钮。如下:

<span class="input-group-btn">
	<button class="btn btn-default" type="button" id="numSub_${shopCarItem.value.goodsId}">
		-
	button>
span>

补充:我们在减去购物车中购物项的数量时,购物项的数量不能为0,后边通过输入改变也是同样的道理。

商品数量的加(“+”按钮)

“+”按钮和“-”,差不多

 // 数量加按钮
$("button[id^='numSum_']").click(
        function (e) {
            var goodId = e.target.id;
            // 获取对应的数量值
            var $number = $(document.getElementById("number_" + goodId.split("_")[1]));
            $number.val(parseInt($number.val()) + 1);
            var val = $number.val();
            // 将修改后的值写入数据库
            sendAjax(goodId,val);
        }
    )

商品数量的改变(键盘修改)

// 数值框内容变化监听
$("input[id^='number_']").blur(
    function(e){
        var id = e.target.id;
        var val = $(document.getElementById("number_" + e.target.id.split("_")[1])).val();
        if (!((/^[1-9]*[1-9][0-9]*$/).test(val))){ //如果不是是正整数
            $(document.getElementById("number_" + e.target.id.split("_")[1])).val(1)
        }
        sendAjax(id,val);
    })

到这里我们会发现,有关购物项数量的改变(“+”、“-”、“键盘修改”),count购物项数量的获取都是通过输入框中的数据来获取的。

购物项删除功能

和前面讲的留言删除功能的思路差不多,都是通过Ajax将对应购物项的ID发送给后端,后端来删除对应的购物项。和留言删除部分不同的是,这里是通过removeAll()方法来删除Cart对象Map属性中的对应的购物项

清空购物车功能

这里使用Ajax来向后端发送请求,后端在Controller层来调用相应的方法,先获取session域中的Cart购物车对象,清空所有购物项,然后必须将处理后的Cart对象重新放入session域中。这里贴出相关代码:

// 清空购物车
protected void removeAll(HttpServletRequest request,HttpServletResponse response){
      HttpSession session = request.getSession();
      Cart cart = (Cart)session.getAttribute("cart");
      if (cart!=null){
          cart.removeAll();
          session.setAttribute("cart",cart);
          try {
              response.getWriter().write(new Gson().toJson("success"));
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }

购物车合计功能

我们这里的思路就是在jsp页面渲染的时候,我们在for-Each循环之中,每次将每个购物项的总价相加,最后保存到session域中。
这里说明一下,有两个总价格。
JavaWeb购物系统(五)购物车模块的实现_第5张图片


<td>${shopCarItem.value.totalPrice}td> 

<c:set scope="session" value="${sessionScope.totalPrice + shopCarItem.value.totalPrice}" var="totalPrice">
c:set>

这里我们还得在页面的开头将购物车的总价格session域中的值重置为0,防止每次刷新,在原来购物车的总价格上相加。

CarProcessServlet

package com.controller;

import com.entity.Cart;
import com.entity.Goods;
import com.entity.ShopCarItem;
import com.google.gson.Gson;
import com.service.GoodsService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;

public class CartProcessServlet extends BasicServlet {
    private GoodsService goodsService = new GoodsService();
    // 添加购物车
    protected void addShopCartItem(HttpServletRequest request,HttpServletResponse response){
        // 得到商品id
        String goodId = request.getParameter("goodId").split("_")[1];
        // 得到商品信息
        Goods good = goodsService.getGoodById(goodId);
        // 将商品信息写入购物项
        ShopCarItem item =
                new ShopCarItem(Integer.parseInt(goodId),good.getImg(),good.getName(),good.getPrice(),1,good.getPrice());
        // 从session中获取购物车
        HttpSession session = request.getSession();
        Cart cart = (Cart)session.getAttribute("cart");
        if (cart == null){ // 还未创建购物车
            cart = new Cart();
            session.setAttribute("cart",cart);
        }
        cart.addShopCartItem(item);
        try {
            response.getWriter().write(new Gson().toJson("success"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 修改购物项信息
    protected void updateShopCartItem(HttpServletRequest request,HttpServletResponse response){
        // 得到商品id
        String goodId = request.getParameter("goodId").split("_")[1];
        // 得到商品数量
        String number = request.getParameter("number");
        // 从session中获取购物车
        HttpSession session = request.getSession();
        Cart cart = (Cart)session.getAttribute("cart");
        ShopCarItem item = cart.getItems().get(Integer.parseInt(goodId));
        // 修改数量
        item.setNumber(Integer.parseInt(number));
        // 修改总价格
        item.setTotalPrice(item.getPrice().multiply(new BigDecimal(Integer.parseInt(number))));
        // 更新购物车
        cart.getItems().put(Integer.parseInt(goodId),item);
        try {
            response.getWriter().write(new Gson().toJson("success"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 清空购物车
    protected void removeAll(HttpServletRequest request,HttpServletResponse response){
        HttpSession session = request.getSession();
        Cart cart = (Cart)session.getAttribute("cart");
        if (cart!=null){
            cart.removeAll();
            session.setAttribute("cart",cart);
            try {
                response.getWriter().write(new Gson().toJson("success"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 根据购物项Id(商品Id)删除购物项
    protected void removeShopItem(HttpServletRequest request,HttpServletResponse response){
        String goodId = request.getParameter("goodId");
        HttpSession session = request.getSession();
        Cart cart = (Cart)session.getAttribute("cart");
        if (cart!=null){
            HashMap<Integer, ShopCarItem> items = cart.getItems();
            items.remove(Integer.parseInt(goodId));
            cart.setItems(items);
            session.setAttribute("cart",cart);
            try {
                response.getWriter().write(new Gson().toJson("success"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

因为我们没有连接数据库,所以这里没有ServiceDao这两层。

Cart购物车类

Cart类对应购物车对象,购物车有购物项集合总商品数两个属性。
这里需要注意的是:购物项属性是一个HashMap,它的Key是购物项的ID(这里是将商品的ID来作为购物项ID的),Value是对应的购物项。
除此之外还提供了:

  1. 添加购物项方法
  2. 删除购物项方法(根据其对应的Id)
  3. 清空购物车方法
  4. 获取商品总数方法
  5. 获得总价格方法
package com.entity;
import java.math.BigDecimal;
import java.util.HashMap;
public class Cart {
    private HashMap<Integer,ShopCarItem> items =  new HashMap<>();
    private Integer totalCount;
    public Cart() {
    }

    // 添加购物项
    public void addShopCartItem(ShopCarItem shopCarItem){
        ShopCarItem item = items.get(shopCarItem.getGoodsId());
        if (item == null){ // 购物车没有相同的商品
            items.put(shopCarItem.getGoodsId(),shopCarItem);
        }else { // 如果购物车有相同的商品
            Integer number = item.getNumber();
            number++;
            item.setNumber(number);
            item.setTotalPrice(item.getPrice().multiply(new BigDecimal(number)));
            items.put(shopCarItem.getGoodsId(),item);
        }
    }

    // 删除特定的Id购物项
    public void removeShopCartItem(String goodId){
        this.items.remove(goodId);
    }

    // 清空购物车
    public void removeAll(){
        this.items.clear();
    }

    // 获得总价格
    public BigDecimal getTotalPrice(){
        BigDecimal totalPrice = new BigDecimal(0);
        for (Integer integer : items.keySet()) {
            ShopCarItem item = items.get(integer);
            totalPrice = totalPrice.add(item.getTotalPrice());
        }
        return totalPrice;
    }

    // 获得商品的总数
    public Integer getTotalCount(){
        totalCount = 0;
        for (Integer integer : items.keySet()) {
            ShopCarItem item = this.items.get(integer);
            totalCount += item.getNumber();
        }
        return totalCount;
    }
    public HashMap<Integer, ShopCarItem> getItems() {
        return items;
    }

    public void setItems(HashMap<Integer, ShopCarItem> items) {
        this.items = items;
    }
}

ShopCartItem购物项类

ShopCartItem类对应购物项对象,购物项的属性有:

  1. 商品编号
  2. 商品图片
  3. 商品名称
  4. 商品单价
  5. 商品数量
  6. 购物项的总价格
package com.entity;

import java.math.BigDecimal;

/**
 * 此类对应 shopcar表
 */
public class ShopCarItem {
    // 商品编号
    private Integer goodsId;
    // 商品图片
    private String img;
    // 商品名称
    private String goodsName;
    // 单价
    private BigDecimal price;
    // 总量
    private Integer number;
    // 总价格
    private BigDecimal totalPrice;

    public ShopCarItem() {
    }

    public ShopCarItem(Integer goodsId, String img, String goodsName, BigDecimal price, Integer number, BigDecimal totalPrice) {
        this.goodsId = goodsId;
        this.img = img;
        this.goodsName = goodsName;
        this.price = price;
        this.number = number;
        this.totalPrice = totalPrice;
    }

    public Integer getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(Integer goodsId) {
        this.goodsId = goodsId;
    }

    public String getImg() {
        return img;
    }

    public void setImg(String img) {
        this.img = img;
    }

    public String getGoodsName() {
        return goodsName;
    }

    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public BigDecimal getTotalPrice() {
        return totalPrice;
    }

    public void setTotalPrice(BigDecimal totalPrice) {
        this.totalPrice = totalPrice;
    }

    @Override
    public String toString() {
        return "ShopCarItem{" +
                "goodsId=" + goodsId +
                ", img='" + img + '\'' +
                ", goodsName='" + goodsName + '\'' +
                ", price=" + price +
                ", number=" + number +
                ", totalPrice=" + totalPrice +
                '}';
    }
}

这里我们的结算功能使用了事务,在保证有库存的情况下生成订单,否则进行回滚。这一块内容我们放到后边的章节来讲解。

你可能感兴趣的:(JavaWeb购物系统,java,json,前端)