家居网购项目

大家好呀!我是小笙~ 该家居网购的技术栈是javaweb开发,我觉得这个是对之前学习的知识的一个总结和输出,是个很好的自我学习反馈的过程.

家居网购项目

家居网购-程序框架图

使用分层模式: 分层的目的是为了解耦,降低代码的耦合度,有利项目的维护和升级

image-20220514233424142

三层架构MVC

MVC 全称∶ Model 模型、View 视图、 Controller 控制器

MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了解耦合), 也有很多落地的框架比如 SpringMVC

  • MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 WEB 层的代码如何有效 分离,单独工作
  • 后面业务复杂度越来越高, model 逐渐分层/组件化 (service + dao)

image-20220514235826151

项目准备

需要引入的jar包

image-20220528174844293

程序框架图

image-20220528173647527

页面展示

首页展示

image-20220528174902781

登录注册页面

image-20220528174955287

家居后台管理页面

image-20220528175039263

购物车页面

image-20220528175118244

订单生成页面

image-20220528175157636

功能实现

注册功能

1.设计用户表
# 创建数据库
DROP DATABASE IF EXISTS home_shopping; 
CREATE DATABASE home_shopping; 
USE home_shopping; 
# 创建用户表
CREATE TABLE member( 
		`id` INT PRIMARY KEY AUTO_INCREMENT, # id
		`username` VARCHAR(10) NOT NULL UNIQUE,  # 用户名
		`password` VARCHAR(32) NOT NULL, # 密码
		`email` VARCHAR(64), # 邮箱
		`user_state` TINYINT NOT NULL DEFAULT 0 # 用户模式 0 会员 1 管理员 -1 未登录
)CHARSET utf8 ENGINE INNODB;
INSERT INTO member(`username`,`password`,`email`,`user_state`) VALUES('Al_tair',MD5('luo1234567'),'lns@qq.com',1);
INSERT INTO member(`username`,`password`,`email`,`user_state`) VALUES('admins',MD5('1234567'),'1079936146@qq.com',0);
SELECT * FROM member;
2.Dao层对数据库的CRUD
public interface MemberDao {
    /**
     * 添加会员用户信息
     */
    public boolean addUserInfo(Member member);
    /**
     * 查询是否有该用户名的会员用户
     */
    public boolean queryUserInfo(String username);
    /**
     * 查询有该用户名和密码的会员用户信息
     */
    public Member queryUserInfo(String username,String password);
}
// 备注:实现代码略,记得每次写完实现代码使用Junit测试
3.Service层对业务的操作
public interface MemberService {
    /**
     * 注册用户
     */
    public boolean registerUser(Member member);
    /**
     * 是否有存在该用户
     */
    public boolean isExistUser(String username);
    /**
     * 用户登录
     */
    public Member login(String username,String password);
}
// 备注:实现代码略,记得每次写完实现代码使用Junit测试
4.前端页面效果
<!-- 截图部分重点 -->
<div class="login-register-form">
    <!-- 错误提醒 -->
    <span class="errorMsg"
          style="float: right; font-weight: bold; font-size: 20pt; margin-left: 10px;">
    </span>
    <form action="memberServlet" method="post">
        <input type="hidden" name="action" value="register">
        <input type="text" id="username" name="username" placeholder="Username"/>
        <input type="password" id="password" name="password" placeholder="输入密码"/>
        <input type="password" id="repwd" name="repassword" placeholder="确认密码"/>
        <input name="email" id="email" placeholder="电子邮件" type="email"/>
        <input type="text" id="code" name="code" style="width: 50%"  placeholder="验证码"/>
        <img class="codeImgRegister" alt="" src="">
        <div class="button-box">
            <button type="submit" id="register-btn"><span>会员注册</span></button>
        </div>
    </form>
</div>

image-20220516180009564

5.前端页面校验(JQuery库)
  1. 验证用户名:必须字母,数字下划线组成,并且长度为 6 到 10 位

    /^[a-zA-Z0-9_]{6,10}$/
    
  2. 验证密码:必须由字母,数字下划线组成,并且长度为 6 到 10 位

  3. 邮箱格式验证:常规验证即可 (正则表达式 )

    /^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
    
  4. 验证码:校验

    导入包

    image-20220523231754349

    表单重复提交情况:

    1. 提交完表单。服务器使用请求转发进行页面跳转。用户刷新(F5),会发起最后一 次的请求, 造成表单重复提交问题。解决:用重定向
    2. 用户正常提交,由于网络延迟等原因,未收到服务器的响应,这时,用户着急多 点了几次提交操作,也会造成表单重复提交。解决: 验证码
    3. 用户正常提交,服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提 交。也会造成表单重复提交,解决:验证码
    <!-- 点击校验码,更新校验码 -->
    $(".codeImgRegister").click(function(){
    	this.src="<%=request.getContextPath() + "/"%>kaptchaServlet?d=" + new Date();
    })
    
  5. 校验优化:Ajax异步请求

    <script type="text/javascript">    
        // Ajax异步请求:这里是登录用户的校验,同理注册也是可以的
        $("#LoginUsername").blur(function(){
            $.getJSON(
                "memberServlet",
                {
                    action:"IsExistUser",
                    username:$("#LoginUsername").val(),
                },
                function (data,state,XHttp) {
                    if(!data.IsExistUser){
                        $("span.errorMsg").text("用户名不存在!");
                    }else{
                        $("span.errorMsg").text("");
                    }
                }
            )
        })
    </script>
    
6.后端校验
if(username == null || password == null || email == null || repassword == null){
    response.sendRedirect(getServletContext().getContextPath() + "/views/member/register_fail.jsp");
    return;
}

// 用户名格式校对
String pattern = "^[a-zA-Z0-9_]{6,10}$";
if(Pattern.matches(pattern,username)){
    System.out.println("用户名格式正确");
}else{
    System.out.println("用户名格式不正确");
    response.sendRedirect(getServletContext().getContextPath() + "/views/member/register_fail.jsp");
    return;
}

// 密码格式校对
pattern = "^[a-zA-Z0-9_]{6,10}$";
if(Pattern.matches(pattern,password)){
    System.out.println("密码格式正确");
}else{
    System.out.println("密码格式不正确");
    response.sendRedirect(getServletContext().getContextPath() + "/views/member/register_fail.jsp");
    return;
}

// 输入两次密码是否相同
if(!password.equals(repassword)){
    System.out.println("输入两次密码不相同!");
    response.sendRedirect(getServletContext().getContextPath() + "/views/member/register_fail.jsp");
    return;
}

// 邮箱格式校对
pattern = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?";
if(Pattern.matches(pattern,email)){
    System.out.println("邮箱格式正确");
}else{
    System.out.println("邮箱格式不正确");
    response.sendRedirect(getServletContext().getContextPath() + "/views/member/register_fail.jsp");
    return;
}

// 获取验证码
HttpSession session = request.getSession();
Object realCode = session.getAttribute(KAPTCHA_SESSION_KEY);
// 立即删除session中该属性
session.removeAttribute(KAPTCHA_SESSION_KEY);
if(realCode == null || code == null || !code.equalsIgnoreCase((String)realCode)){
    System.out.println("验证码不正确");
    response.sendRedirect(getServletContext().getContextPath() + "/views/member/register_fail.jsp");
    return;
}
7.注册页面后端需求分析
  1. 会员注册信息,前端验证通过后,提交给服务器

    • 如果用户名在数据库中已经存在,后台给出提示信息,并返回重新注册

    • 如果用户名没有在数据库中,完成注册,并返回注册成功的页面

  2. 注册密码为 md5 加密


登录功能

1.前端页面效果
<div class="login-register-form">
    <!--  显示错误信息   -->
    <span style="font-size: 18pt;font-weight: bold;float: right;color: red">
        <span class="errorMsg"
              style="float: right; font-weight: bold; font-size: 20pt; margin-left: 10px;">
        </span>
    </span>
    <form action="memberServlet" method="post">
        <input type="hidden" name="action" value="login">
        <input type="text" id="LoginUsername" name="username" value="${requestScope.get("username")}" placeholder="Username"/>
        <input type="password" name="password" placeholder="Password"/>
        <input type="text" id="code2" name="code" style="width: 50%" placeholder="验证码"/>  
        <img class="codeImgLogin" alt="" src="kaptchaServlet">
        <div class="button-box">
            <div class="login-toggle-btn">
                <input type="checkbox"/>
                <a class="flote-none" href="javascript:void(0)">Remember me</a>
                <a href="#">Forgot Password?</a>
            </div>
            <button type="submit" id="login_btn"><span>Login</span></button>
        </div>
    </form>
</div>

image-20220523231647402

2.后端需求分析
  • 分别校验输入的用户名和密码是否符合格式

  • 通过用户名和密码去数据库查找数据

    • 如果找到数据,跳转到登录成功的页面

    • 如果没有找到数据或者用户名密码格式不对,都会返回登录页面并且显示

      image-20220516203314269

3.优化Servlet,将处理请求和分发请求分开
  • 问题提出:其实不管是会员注册还是会员登录都是对会员用户的数据操作,但是却分出了两个Servlet,接下来我们尝试解决web层的优化

    • 方案一:通过隐藏域的值来判断是login还是register

      <input type="hidden" name="action" value="register">
      
      String action = request.getParameter("action");
      if("register".equals(action)){
          RegisterServlet(request,response);
      }else if("login".equals(action)){
          LoginServlet(request,response);
      }
      

      问题存在:虽然分开了处理请求和分发请求,但是本质还是在一个Servlet操作,同时随着业务的拓展,会存在代码复用的问题

    • 方案二:反射 + 模板设计模式 + 动态绑定

      • 就是将接收http请求和分发请求交给父类BasicServlet,将处理请求交给子类

        public abstract class  BasicServlet extends HttpServlet {
            @Override
            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                 // 注意action值要和方法名一致
                String action = request.getParameter("action");
                try {
                    Method declaredMethod = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
                    declaredMethod.invoke(request,response);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        

后台家居管理

1.设计家居表
CREATE TABLE furn(
            `id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, # id
            `name` VARCHAR(32) NOT NULL DEFAULT ' ',  # 家居名
            `price` DECIMAL(11,2) NOT NULL, # 价格
            `vendor` VARCHAR(32) NOT NULL, # 制造商
            `sales` INT UNSIGNED NOT NULL, # 销量
            `inventory` INT UNSIGNED NOT NULL, # 库存
            `img_path` VARCHAR(256) NOT NULL # 存放图片的路径
)CHARSET utf8 ENGINE INNODB 
# 增
INSERT INTO furns(`name`,`price`,`vendor`,`sales`,`inventory`,`img_path` ) VALUES ('Simple 北欧小桌',2030.00,'瑞典',668,20,'http://localhost:9999/homeShopping/shop-left-sidebar.html');
# 改
UPDATE furn SET `name`='花盆',`price`=200,`vendor`='中国',`sales` =20,
		`inventory`=12,`img_path`='assets/images/product-image/2.jpg' WHERE `id` = 2;
# 删	
DELETE FROM furn WHERE `id` = 1; 
# 查
SELECT *FROM furns;

实体类

private Integer id;
private String name;
private BigDecimal price;
private String vendor;
private Integer sales;
private Integer inventory;
private String imgPath;
2.设计Page分页数据模型
public class Page<T> {
    public final static Integer PAGE_SIZE = 3;
    // 表示当前页号
    private Integer pageNo;
    // 表示每页显示最大记录数
    private Integer pageSize = PAGE_SIZE;
    // 表示当前页要显示的数据
    private List<T> items;
    // 表示共有多少页
    private Integer totalPage;
    // 表示共有多少记录
    private Integer totalRow;
    // 分页导航url
    private String url;
}
3.Dao对数据库的操作
public interface FurnDao {
    /**
     * 查询所有家居信息
     */
    public List<Furn> queryAllFurns();
    /**
     * 通过id查询家居数据
     */
    public Furn queryFurnById(Integer id);
    /**
     * 添加家居
     */
    public boolean addFurns(Furn furn);
    /**
     * 删除家居
     */
    public boolean deleteFurnById(int id);
    /**
     * 修改家居
     */
    public boolean updateFurn(Furn furn);
    /**
     * 表示记录数量
     */
    public int rowNum();
    /**
     * 分页数据获取
     */
    public List<Furn> pageItems(int begin,int rowNum);
    /**
     * 表示记录数量(通过家具名过滤)
     */
    public int rowNumByName(String name);
    /**
     * 分页数据获取(通过家具名过滤)
     * @param begin 开始的记录位置 从0开始取
     * @param rowNum 取记录数
     */
    public List<Furn> pageItemsByName(int begin,int rowNum,String name);
}
4.Service层对业务的操作
public interface FurnService {
    /**
     * 通过id查询家居数据
     */
    public Furn queryFurnById(Integer id);
    /**
     * 显示家居
     */
    public List<Furn> showFurns();
    /**
     * 添加家居
     */
    public boolean addFurns(Furn furn);
    /**
     * 删除家居
     */
    public boolean deleteFurnById(int id);
    /**
     * 修改家居
     */
    public boolean updateFurn(Furn furn);
    /**
     *  家居显示分页管理
     * @param pageNo 页号
     * @param pageSize 一个页面最大记录数
     */
    public Page<Furn> pageManage (int pageNo,int pageSize);
    /**
     *  家居显示分页管理(通过家具名过滤)
     * @param pageNo 页号
     * @param pageSize 一个页面最大记录数
     */
    public Page<Furn> pageManageByName (int pageNo,int pageSize,String name);
}

分页算法

@Override
public Page<Furn> pageManage(int pageNo,int pageSize) {
    Page<Furn> page = new Page<>();
    page.setPageNo(pageNo);

    page.setPageSize(pageSize);

    int totalRow = furns.rowNum();
    page.setTotalRow(totalRow);

    int totalPage = (int)Math.ceil(totalRow * 1.0 / pageSize);
    page.setTotalPage(totalPage);

    int begin = pageSize*(pageNo-1);
    page.setItems(furns.pageItems(begin, (totalRow-begin)/pageSize>0?pageSize:totalRow%pageSize));
    page.setUrl("url");
    return page;
}
5.后台家居管理页面
<!-- 家居显示 -->
<c:forEach items="${requestScope.get('page').items}" var="furn">
    <tr>
        <td class="product-thumbnail">
            <a href="#"><img class="img-responsive ml-3" src="${furn.imgPath}"
                             alt=""/></a>
        </td>
        <td class="product-name"><a href="#">${furn.name}</a></td>
        <td class="product-name"><a href="#">${furn.vendor}</a></td>
        <td class="product-price-cart"><span class="amount">${furn.price}</span></td>
        <td class="product-quantity">
            ${furn.sales}
        </td>
        <td class="product-quantity">
            ${furn.inventory}
        </td>
        <td class="product-remove">
            <a class="updateFurn" href="views/furn/furn_update.jsp?id=${furn.id}&name=${furn.name}&price=${furn.price}&vendor=${furn.vendor}&sales=${furn.sales}&inventory=${furn.inventory}&imgPath=${furn.imgPath}
&pageNo=${requestScope.get("page").pageNo}&furnName=${requestScope.name}">
                <i class="icon-pencil"></i>
            </a>
            <a class="deleteFurn" href="manage/furnServlet?action=deleteFurn&id=${furn.id}&pageNo=${requestScope.get("page").pageNo}&furnName=${requestScope.name}"><i class="icon-close"></i></a>
        </td>
    </tr>
</c:forEach>

分页显示代码

<ul>
    <li><a href="${requestScope.page.url}">首页</a></li>
    <c:if test="${requestScope.get('page').pageNo > 1}">
        <li><a href="${requestScope.page.url}&pageNo=${requestScope.get('page').pageNo-1}">上页</a></li>
    </c:if>

    <%--设置显示分页号数--%>
    <%--页面总数大于5的时候,显示5个页码--%>
    <c:if test="${requestScope.get('page').totalPage > 5}">
        <c:set var="begin" value="${requestScope.get('page').pageNo-2}"/>
        <c:set var="end" value="${requestScope.get('page').pageNo+2}"/>
    </c:if>
    <%--页面位置在前面2个--%>
    <c:if test="${requestScope.get('page').pageNo <= 3}">
        <c:set var="begin" value="1"/>
        <c:set var="end" value="5"/>
    </c:if>
    <%--页面位置在后面2个--%>
    <c:if test="${(requestScope.get('page').totalPage - requestScope.get('page').pageNo) <= 3}">
        <c:set var="begin" value="${requestScope.get('page').totalPage-4}"/>
        <c:set var="end" value="${requestScope.get('page').totalPage}"/>
    </c:if>
    <%--页面总数小于5的时候,默认全部显示--%>
    <c:if test="${requestScope.get('page').totalPage  <= 5}">
        <c:set var="begin" value="1"/>
        <c:set var="end" value="${requestScope.get('page').totalPage}"/>
    </c:if>

    <%--循环遍历显示页号数--%>
    <c:forEach begin="${begin}" end="${end}" var="pageNum">
        <c:if test="${pageNum == requestScope.get('page').pageNo}">
            <li><a class="active" href="${requestScope.page.url}&pageNo=${pageNum}">${pageNum}</a></li>
        </c:if>
        <c:if test="${pageNum != requestScope.get('page').pageNo}">
            <li><a href="${requestScope.page.url}&pageNo=${pageNum}">${pageNum}</a></li>
        </c:if>
    </c:forEach>

    <c:if test="${requestScope.get('page').pageNo < requestScope.get('page').totalPage}">
        <li><a href="${requestScope.page.url}&pageNo=${requestScope.get('page').pageNo+1}">下页</a></li>
    </c:if>
    <li><a href="${requestScope.page.url}&pageNo=${requestScope.get('page').totalPage}">末页</a></li>
    <li><a>共${requestScope.get('page').totalPage}页</a></li>
    <li><a>共${requestScope.get('page').totalRow}记录</a></li>
</ul>

image-20220530212956926

6.控制层对家居的增删改查

BeanUtils自动封装Bean

  1. BeanUtils 工具类,它可以一次性的把所有请求的参数注入到 JavaBean 中
  2. BeanUtils 工具类,经常用于把 Map 中的值注入到 JavaBean 中,或者是对象属性值的拷贝操作
  3. 需要导入需要的 jar 包: commons-beanutils-1.8.0.jar commons-logging-1.1.1.jar
// 使用工具包前
String name = request.getParameter("name");
String price = request.getParameter("price");
String vendor = request.getParameter("vendor");
String sales = request.getParameter("sales");
String inventory = request.getParameter("inventory");
String imgPath = request.getParameter("imgPath");
try {
    Furn furn = new Furn(name, new BigDecimal(price), vendor,
                         Integer.valueOf(sales), Integer.valueOf(inventory), img);
    furns.addFurns(furn);
}catch (Exception e){
    request.setAttribute("errorMsg","添加家居数据格式错误!");
    request.getRequestDispatcher("/views/furn/furn_add.jsp").forward(request,response);
    return;
}

// 使用工具包之后
//1.会使用工具类 DataUtils 来完成自动封装 JavaBean 
//2.注意表单提交的数据对应的 name 需要和 JavaBean 的属性名保持一致
Furn furn = new Furn();
try {
    // 本质:通过反射来为Javabean对象set方法添加属性值
    BeanUtils.populate(furn,request.getParameterMap());
    furns.addFurns(furn);
}catch (Exception e){
    request.setAttribute("errorMsg","添加家居数据格式错误!");
    request.getRequestDispatcher("/views/furn/furn_add.jsp").forward(request,response);
    return;
}

搜索框
<div class="header_account_list">
    <a href="javascript:void(0)" class="header-action-btn search-btn"><i class="icon-magnifier"></i></a>
    <div class="dropdown_search">
        <form class="action-form" action="customerFurnServlet">
            <input type="hidden" name="action" value="customerFurnByName">
            <input class="form-control" name="name" placeholder="输入家居名搜索" type="text">
            <button class="submit" type="submit"><i class="icon-magnifier"></i></button>
        </form>
    </div>
</div>

image-20220530215259802

通过page对象的url来携带搜索的家具名,起到点击分页栏,仍旧处在搜索结果的条件下

protected void customerFurnByName(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 页号
    String pageNo = request.getParameter("pageNo");
    // 搜索框内容:家居名
    String name = request.getParameter("name");
    // session记录登录情况以及身份
    HttpSession session = request.getSession();
    Object username = session.getAttribute("username");
    Object userState = session.getAttribute("userState");

    if(username == null){
        username = "登录|注册";
        userState = -1;
    }
    if(name == null){
        name = "";
    }
    if(pageNo == null){
        pageNo = "1";
    }
    // 分页(根据家具名来分页搜索)
    Page<Furn> pages = furns.pageManageByName(Integer.parseInt(pageNo), 4,name);
    StringBuffer url = new StringBuffer("customerFurnServlet?action=customerFurnByName");
    if(!"".equals(name)){
        url.append("&name=").append(name);
    }else{
        url.append("&name=").append("");
    }
    pages.setUrl(url.toString());
    request.setAttribute("page",pages);
    request.setAttribute("username",username);
    request.setAttribute("userState",userState);
    request.getRequestDispatcher("/views/customer/index.jsp").forward(request,response);
}

添加家居
<tr>
    <td class="product-thumbnail">
        <input type="hidden" name="imgPath" value="assets/images/product-image/1.jpg">
        <a href="#"><img class="img-responsive ml-3" src="assets/images/product-image/1.jpg" alt=""/></a>
    </td>
    <td class="product-name"><input name="name" style="width: 60%" type="text" value=""/></td>
    <td class="product-name"><input name="vendor" style="width: 90%" type="text" value=""/></td>
    <td class="product-price-cart"><input name="price" style="width: 90%" type="text" value=""/></td>
    <td class="product-quantity">
        <input name="sales" style="width: 90%" type="text" value=""/>
    </td>
    <td class="product-quantity">
        <input name="inventory" style="width: 90%" type="text" value=""/>
    </td>
    <td>
        <input type="submit" style="width: 90%;background-color: silver;border: silver;border-radius: 20%;" value="添加家居"/>
    </td>
</tr>

image-20220518211524375

protected void addFurn(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String pageNo = request.getParameter("pageNo");
    Furn furn = new Furn();
    try {
        BeanUtils.populate(furn,request.getParameterMap());
    }catch (Exception e){
        request.setAttribute("errorMsg","添加家居数据格式错误!");
        request.getRequestDispatcher("/views/furn/furn_add.jsp").forward(request,response);
        return;
    }
    furns.addFurns(furn);
    response.sendRedirect(getServletContext().getContextPath() + "/manage/furnServlet?action=showPageDisplayByName&pageNo=" + pageNo);
}

删除家居

前端判断是否删除

<script type="text/javascript">
    $(function(){
        $("a.deleteFurn").click(function(){
            var furnName = $(this).parent().parent().find("td:eq(1)").text();
            if(confirm("确定删除家居【"+ furnName + "】?")){
                return true;
            }
            return false;
        })
    })
</script>

image-20220519111319741

protected void deleteFurn(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String pageNo = request.getParameter("pageNo");
    String id = request.getParameter("id");
    String name = URLEncoder.encode(request.getParameter("furnName"), "utf-8");
    boolean deleteFlag = false;
    try {
        // 通过id删除家居
        deleteFlag = furns.deleteFurnById(Integer.parseInt(id));
    }catch (Exception e){
        throw new RuntimeException();
    }
    System.out.println(deleteFlag?"删除成功":"删除失败");
    response.sendRedirect(getServletContext().getContextPath() + "/manage/furnServlet?action=showPageDisplayByName&name=" + name + "&pageNo=" + pageNo);
}

修改家居
<tr>
    <td class="product-thumbnail">
        <div id="pic">
            <img id="prevView" name="imgPath"  class="img-responsive ml-3" src=<%=request.getParameter("imgPath")%> alt=""/>
            <input id="upload" type="file" name="pic" onchange="prev(this)"/>
        </div>
    </td>
    <td class="product-name"><input name="name" style="width: 60%" type="text" value=<%=request.getParameter("name")%>></td>
    <td class="product-name"><input name="vendor" style="width: 90%" type="text" value=<%=request.getParameter("vendor")%>></td>
    <td class="product-price-cart"><input name="price" style="width: 90%" type="text" value=<%=request.getParameter("price")%>></td>
    <td class="product-quantity">
        <input name="sales" style="width: 90%" type="text" value=<%=request.getParameter("sales")%>>
    </td>
    <td class="product-quantity">
        <input name="inventory" style="width: 90%" type="text" value=<%=request.getParameter("inventory")%>>
    </td>
    <td>
        <input type="submit" style="width: 90%;background-color: silver;border: silver;border-radius: 20%;" value="修改家居"/>
    </td>
</tr>

image-20220519111621282

<!-- furn_manage.jsp -->
<a class="updateFurn" href="views/furn/furn_update.jsp?id=${furn.id}&name=${furn.name}&price=${furn.price}                            &vendor=${furn.vendor}&sales=${furn.sales}&inventory=${furn.inventory}&imgPath=${furn.imgPath}">
    <i class="icon-pencil"></i>
</a>


<!-- furn_update.jsp -->
<tr>
    <td class="product-thumbnail">
        <input type="hidden" name="imgPath" value="assets/images/product-image/default.jpg">
        <a href="#"><img class="img-responsive ml-3" src=<%=request.getParameter("imgPath")%>
                         alt=""/></a>
    </td>
    <td class="product-name"><input name="name" style="width: 60%" type="text" value=<%=request.getParameter("name")%>></td>
    <td class="product-name"><input name="vendor" style="width: 90%" type="text" value=<%=request.getParameter("vendor")%>></td>
    <td class="product-price-cart"><input name="price" style="width: 90%" type="text" value=<%=request.getParameter("price")%>></td>
    <td class="product-quantity">
        <input name="sales" style="width: 90%" type="text" value=<%=request.getParameter("sales")%>>
    </td>
    <td class="product-quantity">
        <input name="inventory" style="width: 90%" type="text" value=<%=request.getParameter("inventory")%>>
    </td>
    <td>
        <input type="submit" style="width: 90%;background-color: silver;border: silver;border-radius: 20%;" value="修改家居"/>
    </td>
</tr>

购物车功能

1.设计购物车数据模型
// 数据库种每一件商品的信息
public class CarItem {
    private Integer id;
    private String name;
    private BigDecimal singlePrice;
    private Integer count;
    private BigDecimal singleTotalPrice;
    private String imgPath;
}
/**
 * 含有多个CarItem,用来管理购物车内多个商品
 */
public class Cart {

    /**
     * 存储 id 对应的商品
     */
    private Map<Integer,CarItem> items = new HashMap<>();
    /**
     * 用来有序存放加入购物车的顺序
     */
    private List<CarItem> itemsList = new ArrayList<>();

    public List<CarItem> getItemsList() {
        return itemsList;
    }

    /**
     * 添加商品到购物车
     */
    public void addItem(CarItem carItem){
        Integer id = carItem.getId();
        CarItem originalCarItem = items.get(id);
        if(originalCarItem == null){ // 购物车内没有相同的该商品
            items.put(id,carItem);
            itemsList.add(carItem);
        }else{
            originalCarItem.setCount(carItem.getCount() + originalCarItem.getCount());
            originalCarItem.setSingleTotalPrice(carItem.getSingleTotalPrice().add(originalCarItem.getSingleTotalPrice()));
        }
    }

    /**
     * 返回商品的总金额
     */
    public BigDecimal getCartItemsPrice(){
        BigDecimal totalPrice = new BigDecimal(0.0);
        Set<Integer> itemsId = items.keySet();
        for (Integer id : itemsId) {
            totalPrice = totalPrice.add((items.get(id)).getSingleTotalPrice());
        }
        return totalPrice;
    }

    /**
     * 获取商品的总数
     */
    public Integer getCartItemsCount(){
        Integer totalCount = 0;
        if(items == null){
            return totalCount;
        }
        Set<Integer> itemsId = items.keySet();
        for (Integer id : itemsId) {
            totalCount += items.get(id).getCount();
        }
        return totalCount;
    }

    /**
     * 返回指定商品列表
     */
    public List<CarItem> listItem(Integer begin,Integer end){
        if(items == null){
            return null;
        }
        List<CarItem> carItems = new ArrayList<>();
        for(int i = begin;i<begin+end;i++){
            carItems.add(itemsList.get(i));
        }
        return carItems;
    }

    /**
     * 购物车商品分页管理
     */
    public Page<CarItem> pageManageCartItems(int pageNo, int pageSize) {
        Page<CarItem> page = new Page<>();
        page.setPageNo(pageNo);

        page.setPageSize(pageSize);

        int totalRow = itemsList.size();
        page.setTotalRow(totalRow);

        int totalPage = (int)Math.ceil(totalRow * 1.0 / pageSize);
        page.setTotalPage(totalPage);

        int begin = pageSize*(pageNo-1);
        page.setItems(listItem(begin, (totalRow-begin)/pageSize>0?pageSize:totalRow%pageSize));
        page.setUrl("url");
        return page;
    }

    /**
     * 更新商品的数量
     */
    public void updateItem(Integer id,Integer count){
        if(id == null || count == null){
            return;
        }
        CarItem originalCarItem = items.get(id);
        if(originalCarItem == null){
            return;
        }
        if(count == 0){
            deleteItem(id);
            return;
        }
        originalCarItem.setCount(count);
        originalCarItem.setSingleTotalPrice(new BigDecimal(originalCarItem.getCount()).multiply(originalCarItem.getSinglePrice()));
    }

    /**
     * 删除某个商品
     */
    public void deleteItem(Integer id){
        CarItem originalCarItem = items.get(id);
        if(originalCarItem == null){
            return;
        }
        for (CarItem carItem : itemsList) {
            if(carItem.getId().equals(id)){
                itemsList.remove(carItem);
                items.remove(id);
                return;
            }
        }
    }

    /**
     * 清空购物车
     */
    public void deleteCart(){
        items = new HashMap<>();
        itemsList = new ArrayList<>();
    }
}
2.购物车显示页面
<c:forEach items="${requestScope.pages.items}" var="cartItem">
    <tr>
        <td class="product-thumbnail">
            <a><img class="img-responsive ml-3" src="${cartItem.imgPath}" alt=""/></a>
        </td>
        <td class="product-name"><a>${cartItem.name}</a></td>
        <td class="product-price-cart"><span class="amount">¥${cartItem.singlePrice}</span></td>
        <td class="product-quantity">
            <div class="cart-plus-minus">
                <input readonly="true" ItemId="${cartItem.id}" class="cart-plus-minus-box" id="count" type="text" name="qtybutton" value=${cartItem.count} >
            </div>
        </td>
        <td class="product-subtotal">¥${cartItem.singleTotalPrice}</td>
        <td class="product-remove">
            <a id="cleanCartItem" href="cart/cartServlet?action=deleteCartItem&id=${cartItem.id}&pageNo=${requestScope.pages.pageNo}"><i class="icon-close"></i></a>
        </td>
    </tr>
</c:forEach>
<div class="cart-shiping-update-wrapper">
    <c:if test="${sessionScope.cart.getCartItemsCount() != 0}">
        <h4>共${sessionScope.cart.getCartItemsCount()}件商品 总价${sessionScope.cart.getCartItemsPrice()}元</h4>
    </c:if>
    <h4></h4><h4></h4><h4></h4><h4></h4><h4></h4>
    <div class="cart-shiping-update">
        <a href="order/orderServlet?action=generateOrder">购 物 车 - 生 成 订 单</a>
    </div>
    <div class="cart-clear">
        <button type="submit">继 续 购 物</button>
        <a href="cart/cartServlet?action=deleteCart" id="cleanCart">清 空 购 物 车</a>
    </div>
</div>

image-20220531235206305

3.对购物车的增删操作
/**
  * 清空购物车
  */
protected void deleteCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    HttpSession session = request.getSession();
    Object cart = session.getAttribute("cart");
    if(cart != null){
        ((Cart)cart).deleteCart();
    }
    Page<CarItem> pages = ((Cart)cart).pageManageCartItems(1,4);
    request.setAttribute("pages",pages);
    request.getRequestDispatcher("/views/cart/cart.jsp").forward(request,response);
}

/**
  * 在购物车里删除某个商品
  */
protected void deleteCartItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String pageNo = request.getParameter("pageNo");
    String id = request.getParameter("id");

    HttpSession session = request.getSession();
    Object originalCart = session.getAttribute("cart");
    Cart cart = null;
    if(originalCart == null){
        cart = new Cart();
    }else{
        cart = (Cart) originalCart;
    }
    if(pageNo == null){
        pageNo = "1";
    }

    cart.deleteItem(Integer.parseInt(id));
    Page<CarItem> pages = cart.pageManageCartItems(Integer.parseInt(pageNo),4);
    request.setAttribute("pages",pages);
    request.getRequestDispatcher("/views/cart/cart.jsp").forward(request,response);
}

/**
  * 分页展示商品
  */
protected void showCartPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String pageNo = request.getParameter("pageNo");
    HttpSession session = request.getSession();
    Object originalCart = session.getAttribute("cart");
    Cart cart = null;
    if(originalCart == null){
        cart = new Cart();
    }else{
        cart = (Cart) originalCart;
    }
    if(pageNo == null){
        pageNo = "1";
    }
    Page<CarItem> pages = cart.pageManageCartItems(Integer.parseInt(pageNo),4);
    request.setAttribute("pages",pages);
    request.getRequestDispatcher("/views/cart/cart.jsp").forward(request,response);
}

/**
  * 更新商品数据(+ - 商品数量)
  */
protected void updateCartPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String pageNo = request.getParameter("pageNo");
    String id = request.getParameter("id");
    String count = request.getParameter("count");

    HttpSession session = request.getSession();
    Object originalCart = session.getAttribute("cart");
    Cart cart = null;
    if(originalCart == null){
        cart = new Cart();
    }else{
        cart = (Cart) originalCart;
    }
    if(pageNo == null){
        pageNo = "1";
    }

    cart.updateItem(Integer.parseInt(id),Integer.parseInt(count));
    Page<CarItem> pages = cart.pageManageCartItems(Integer.parseInt(pageNo),4);
    request.setAttribute("pages",pages);
    request.getRequestDispatcher("/views/cart/cart.jsp").forward(request,response);
}

首页家居功能

1.首页页面效果
<c:forEach items="${requestScope.get('page').items}" var="furn">
    <div class="col-lg-3 col-md-6 col-sm-6 col-xs-6 mb-6" data-aos="fade-up" data-aos-delay="200">
        <!-- Single Prodect -->
        <div class="product">
            <div class="thumb">
                <a href="#" class="image">
                    <img src=${furn.imgPath} alt="Product"/>
                    <img class="hover-image" src="assets/images/product-image/5.jpg" alt="Product"/>
                </a>
                <span class="badges"> <span class="new">New</span> </span>
                <div class="actions">
                    <a href="#" class="action wishlist" data-link-action="quickview"
                       title="Quick view" data-bs-toggle="modal" data-bs-target="#exampleModal">
                        <i class="icon-size-fullscreen"></i>
                    </a>
                </div>
                <form action="#" method="get">
                    <input name="pageNo" type="hidden" value=${requestScope.get('page').pageNo}>
                    <input name="action" type="hidden" value="addItem">
                    <input name="id" type="hidden" value="${furn.id}">
                    <input name="name" type="hidden" value="${furn.name}">
                    <input name="singlePrice" type="hidden" value="${furn.price}">
                    <input name="count" type="hidden" value="1">
                    <input name="name" type="hidden" value="${furn.name}">
                    <input name="singleTotalPrice" type="hidden" value="${furn.price}">
                    <input name="imgPath" type="hidden" value="${furn.imgPath}">
                    <!-- 根据存货判断是否能添加到购物车-->
                    <c:if test="${furn.inventory == 0}">
                        <button title="Add To Cart" furnInventory="0" class="add-to-cart" type="button">
                            Add To Cart【暂时缺货】
                        </button>
                    </c:if>
                    <c:if test="${furn.inventory > 0}">
                        <button furnId="${furn.id}" furnInventory="${furn.inventory}" title="Add To Cart" class="add-to-cart" type="button">
                            Add To Cart
                        </button>
                    </c:if>
                </form>
            </div>
            <div class="content">
                <h5 class="title">
                    <a href="shop-left-sidebar.html">${furn.name} </a></h5>
                <span class="price">
                    <span class="new">家居: ${furn.name}</span>
                </span>
                <span class="price">
                    <span class="new">厂商: ${furn.vendor}</span>
                </span>
                <span class="price">
                    <span class="new">价格: ¥${furn.price}</span>
                </span>
                <span class="price">
                    <span class="new">销量: ${furn.sales}</span>
                </span>
                <span class="price">
                    <span class="new">库存: ${furn.inventory}</span>
                </span>
            </div>
        </div>
    </div>
</c:forEach>

image-20220530215846731

protected void showPageDisplayByName(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 页号
    String pageNo = request.getParameter("pageNo");
    // 搜索框内容:家居名
    String name = request.getParameter("name");
    if(name == null){
        name = "";
    }
    if(pageNo == null){
        pageNo = "1";
    }
    // 分页(根据家具名来分页搜索)
    Page<Furn> pages = furns.pageManageByName(Integer.parseInt(pageNo), Page.PAGE_SIZE,name);
    StringBuffer url = new StringBuffer("manage/furnServlet?action=showPageDisplayByName");
    if(!"".equals(name)){
        url.append("&name=").append(name);
    }else{
        url.append("&name=").append("");
    }
    pages.setUrl(url.toString());
    request.setAttribute("page",pages);
    request.setAttribute("name",name);
    request.getRequestDispatcher("/views/manage/furn_manage.jsp").forward(request,response);
}
2.点击首页商品加入购物车(Ajax请求)
/**
  * 通过Ajax请求来添加商品
  */
protected void AddItemByAjax(HttpServletRequest request, HttpServletResponse response) throws InvocationTargetException, IllegalAccessException, IOException {
    HttpSession session = request.getSession();
    Object originalCart = session.getAttribute("cart");
    Cart cart = null;
    if(originalCart == null){
        cart = new Cart();
    }else{
        cart = (Cart) originalCart;
    }

    // 添加的商品数据生成商品对象
    int id = Integer.parseInt(request.getParameter("id"));
    CarItem carItem = new CarItem();
    Furn furn = furnService.queryFurnById(id);
    carItem.setId(furn.getId());
    carItem.setName(furn.getName());
    carItem.setCount(1);
    carItem.setImgPath(furn.getImgPath());
    carItem.setSinglePrice(furn.getPrice());
    carItem.setSingleTotalPrice(furn.getPrice());
    cart.addItem(carItem);
    session.setAttribute("cart",cart);

    Gson gson = new Gson();
    HashMap<String, Object> map = new HashMap<>();
    map.put("carItemNum",cart.getCartItemsCount());
    response.getWriter().write(gson.toJson(map));
}

订单管理

1.设计订单表以及细节订单
# 订单表
CREATE TABLE `order`(
	  	`id`  VARCHAR(32) NOT NULL UNIQUE, # 订单号
		`create_time` DATETIME NOT NULL,  # 订单日期
    	`price`  DECIMAL(11,2) NOT NULL, # 价格
		`status` VARCHAR(16) NOT NULL, # 订单状态
		`member_id`INT NOT NULL # 关联用户id
)CHARSET utf8 ENGINE INNODB;
INSERT INTO `order`(`id`,`create_time`,`price`,`status`,`member_id`) 
VALUES('12',NOW(),203,'未发货',3);
SELECT `id`,`create_time` AS createTime,`price`,`status`,`member_id` AS memberId FROM `order`;
SELECT *FROM `order`;

# 订单细节表(属于订单表)
CREATE TABLE `order_detail`(
		`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, #id
	     `name` VARCHAR(32) NOT NULL, # 家居名
         `price` DECIMAL(11,2) NOT NULL, # 价格
		`count` INT UNSIGNED NOT NULL, # 数量
		`total_price` DECIMAL(11,2) NOT NULL, # 金额
		`order_id` VARCHAR(32) NOT NULL # 订单号
)CHARSET utf8 ENGINE INNODB; 
INSERT INTO `order_detail`(`name`,`price`,`count`,`total_price`,`order_id`) VALUES('123',123.0,12,456,"456789");
SELECT *FROM `order_detail` WHERE `order_id` = 456789;

2.Dao对数据库的操作
public interface OrderDao {
    /**
     * 添加订单
     */
    public boolean saveOrder(Order order);

    /**
     * 查询所有订单
     */
    public List<Order> listOrder();
}

public interface OrderItemDao {
    /**
     * 存订单详细信息
     */
    public boolean saveOrderItem(OrderItem orderItem);
    /**
     * 生成对应订单的商品列表哦
     */
    public List<OrderItem> listOrderItem(String orderId);
}

3.Service对业务的操作
public interface OrderService {
    /**
     * 保存订单数据
     *
     * @param cart 购物车
     * @param memberId 登录用户id
     * @return 订单号
     */
    public String saveOrder(Cart cart,int memberId);
    /**
     * 订单列表
     */
    public List<Order> listOrder();
    /**
     * 商品列表
     */
    public List<OrderItem> listOrderItem(String orderId);
}
4.订单页面以及订单细节页面

订单页面

<c:forEach items="${requestScope.orders}" var="order">
    <tr>
        <td class="product-name">${order.id}</td>
        <td class="product-name">${order.createTime}</td>
        <td class="product-price-cart"><span class="amount">${order.price}</span></td>
        <td class="product-name"><a href="#">${order.status}</a></td>
        <td class="product-remove">
            <a href="order/orderServlet?action=listOrderItem&orderId=${order.id}" ><i class="icon-eye"></i></a>
        </td>
    </tr>
</c:forEach>

image-20220601210655756

订单细节页面

c:forEach items="${requestScope.orderItems}" var="orderItem">
<tbody>
    <tr>
        <td class="product-name"><a>${orderItem.name}</a></td>
        <td class="product-price-cart"><span class="amount">$${orderItem.price}</span></td>
        <td class="product-quantity">${orderItem.count}</td>
        <td class="product-subtotal">$${orderItem.totalPrice}</td>
    </tr>
</tbody>
</c:forEach>
<div class="col-lg-12">
    <div class="cart-shiping-update-wrapper">
        <h4>共${requestScope.totalCount}件商品 总价${requestScope.totalPrice}元</h4>
        <div class="cart-clear">
            <a href="#">继 续 购 物</a>
        </div>
    </div>
</div>

image-20220601210712601

5.控制层对订单的数据操作
@WebServlet(name = "OrderServlet",urlPatterns = {"/order/orderServlet"})
public class OrderServlet extends BasicServlet {
    OrderService orderService = new OrderServiceImpl();
    MemberService memberServlet = new MemberServiceImpl();
    FurnService furnService = new FurnServiceImpl();
    
    /**
     * 显示订单
     */
    protected void listOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Order> orders = orderService.listOrder();
        request.setAttribute("orders",orders);
        request.getRequestDispatcher("/views/order/order.jsp").forward(request,response);
    }
    
    /**
     * 显示订单细节
     */
    protected void listOrderItem (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String orderId = request.getParameter("orderId");
        List<OrderItem> orderItems = orderService.listOrderItem(orderId);
        int totalCount = 0;
        BigDecimal totalPrice = new BigDecimal(0.0);
        for (OrderItem orderItem : orderItems) {
            totalCount += orderItem.getCount();
            totalPrice = totalPrice.add(orderItem.getTotalPrice());
        }
        request.setAttribute("orderItems",orderItems);
        request.setAttribute("totalCount",totalCount);
        request.setAttribute("totalPrice",totalPrice);
        request.getRequestDispatcher("/views/order/order_detail.jsp").forward(request,response);
    }

    /**
     * 生成一个订单
     */
    protected void generateOrder(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        HttpSession session = request.getSession();
        Object username = session.getAttribute("username");
        Object password = session.getAttribute("password");

        if(username == null){
            System.out.println("用户未登录!");
            response.sendRedirect(getServletContext().getContextPath() + "/views/member/login.jsp");
            return;
        }
        Object originalCart = session.getAttribute("cart");
        if(originalCart == null){
            System.out.println("购物车为空!");
            response.sendRedirect(request.getHeader("Referer"));
            return;
        }
        Cart cart = null;
        cart = (Cart)originalCart;
        // 遍历购物车的商品数量有没有超过该商品的库存
        List<CarItem> itemsList = cart.getItemsList();
        for (CarItem carItem : itemsList) {
           if(carItem.getCount() > furnService.queryFurnById(carItem.getId()).getInventory()){
               System.out.println("有商品已经超出库存限制!");
               response.sendRedirect(request.getHeader("Referer"));
               return;
           }
        }
        // 这里通过用户名和密码查找用户并不是登录(Service层名字设计有问题)
        Member member = memberServlet.login((String) username, (String) password);
        String orderId = orderService.saveOrder(cart, member.getId());
        session.setAttribute("cart",null);
        session.setAttribute("orderId",orderId);
        response.sendRedirect(getServletContext().getContextPath() + "/views/order/checkout.jsp");
    }
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Al_tair

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值