IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot项目实战——书城项目 -> 正文阅读

[Java知识库]springboot项目实战——书城项目

一、主要技术

项目后端主要使用 springboot
数据库:mysql
使用mybaits操作数据库(xml方式)
缓存:redis
安全框架:shrio
前端:jq+bootstrap
数据库中密码字段使用 md5加密技术
前后端交互使用 ajax
thymeleaf模板引擎 显示某些变量 和引用相同代码
使用aop面向切面编程思想确定业务所需的的时间

二、项目描述

1、项目目录

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、项目思路

项目主要分为 用户、商品(书籍)、购物车、订单 四大模块。
(1)、用户:用户的登录、注册、收货地址的增删改查和设置默认收货地址、头像的修改
(2)、商品:书籍的查询、展示到页面
(3)、购物车:添加到购物车 删除购物车 增加数量 提交购物车
(4)、订单:创建订单、查询订单

在这里插入图片描述

项目编写主要先从 DAO持久层–>Service业务层–>控制层–>前端的思路
编写业务层和控制层都需要先规划异常

三、项目部分类

1、pojo实体类

所有实体类都继承于BaseEntity
BaseEntity有四个基本数据

用户类、地址类、区域类、商品类、购物车类、订单类、订单项类
区域类:

package com.atmae.store.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * 省市区的数据实体类
 * @author Mae
 */
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class District{
    private Integer id;
    private String parent;
    private String code;
    private String name;
}

对应的数据库数据(3523条):
在这里插入图片描述
主要用来动态获得省市区

2、拦截器

项目需要对没有登录的用户进行拦截,即其不能访问除登录、注册以外的其他页面

package com.atmae.store.config;

import com.atmae.store.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

/**
 * 处理器拦截器的注册
 * @author Mae
 */
@Configuration
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
    @Override
    /** 将自定义拦截器进行注册*/
    public void addInterceptors(InterceptorRegistry registry) {
        /** 自定义拦截器对象*/
        HandlerInterceptor interceptor=new LoginInterceptor();
        /** 配置白名单:存放在List集合中*/
        List<String> patterns =new ArrayList<>();
        patterns.add("/assets/**");
        patterns.add("/users/login");
        patterns.add("/users/register");
        patterns.add("/register");
        patterns.add("/login");
        patterns.add("/index");
        patterns.add("/district/**");
        /** 完成拦截器的注册*/
        /** 要拦截url  /**: 表示项目下所有的请求被拦截*/
        registry.addInterceptor(interceptor).addPathPatterns("/**")
                .excludePathPatterns(patterns);
    }
}
package com.atmae.store.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Mae
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 检测全局Session是否有uid数据,如果有则放行,没有重定向到登录页面
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param handler  处理器(url+Controller:映射)
     * @return true表示放行当前的请求 false表示拦截当前的请求
     * @throws Exception 异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("userId");
        if (user == null) {
            /** 说明用户没有登录 则重定向到登陆页*/
            response.sendRedirect("/login");
            return false;
        }
            /** 表示放行*/
        return true;
    }
}

3、Util工具类

JsonResult类:主要在控制层以JSON方式响应前端数据

package com.atmae.store.util;

import java.io.Serializable;

/**
 * Json格式的数据进行响应
 *
 * @author Mae
 */
public class JsonResult<E> implements Serializable {
    /**
     * 状态码
     */
    private Integer state;
    /**
     * 描述信息
     */
    private String message;
    /**
     * 数据
     */
    private E data;

    public JsonResult() {

    }

    public JsonResult(Integer state) {
        this.state = state;
    }

    public JsonResult(Integer state, String message, E data) {
        this.state = state;
        this.message = message;
        this.data = data;
    }

    public JsonResult(String message, E data) {
        this.message = message;
        this.data = data;
    }

    public JsonResult(Throwable e) {
        this.message = e.getMessage();
    }

    public JsonResult(Integer state, E data) {
        this.state = state;
        this.data = data;
    }

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public E getData() {
        return data;
    }

    public void setData(E data) {
        this.data = data;
    }
}

4、VO层

package com.atmae.store.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;


/**
 * value object 值对象
 * 当进行select查询时 查询的结果数据时多张表中的内容,此时不能使用一个pojo实体类来接受
 * 则重新建一个新的对象存储查询出来的结果对应的映射
 * “ 多表操作 ”
 * @author Mae
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class CartVO implements Serializable {
    private Integer userId;
    private Integer cartId;
    private Integer productId;
    private Long price;
    private Integer num;
    private String title;
    private String image;
    private Long realPrice;
}

5、控制层的基类

主要用来规划异常响应前端

package com.atmae.store.controller;

import com.atmae.store.controller.ex.*;
import com.atmae.store.service.ex.*;
import com.atmae.store.util.JsonResult;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpSession;

/**
 * 控制层类的基类
 *
 * @author Mae
 */
public class BaseController {
    /**
     * 操作成功的状态码
     */
    public static final int OK = 200;

    /**
     * 请求处理方法
     * 当前项目产生了异常 被统一拦截到此方法中,
     * 这个方法就充当了请求处理的方法,方法的返回值直接返回到前端
     */
    @ExceptionHandler(value = {ServiceException.class, FileUploadException.class})  //用于统一处理抛出的异常
    public JsonResult<Void> handleException(Throwable e) {
        JsonResult<Void> result = new JsonResult<>();
        if (e instanceof UsernameDuplicatedException) {
            result.setState(4000);
            result.setMessage("用户名已经被占用");
        } else if (e instanceof AddressCountLimitException) {
            result.setState(4001);
            result.setMessage("用户收货地址超出上限异常");
        } else if (e instanceof AccessDeniedException) {
            result.setState(4002);
            result.setMessage("非法访问异常");
        } else if (e instanceof AddressNotFoundException) {
            result.setState(4003);
            result.setMessage("收货地址没有发现异常");
        } else if (e instanceof ProductNotFoundException) {
            result.setState(4004);
            result.setMessage("商品没有被发现异常");
        } else if (e instanceof CartNotFoundException) {
            result.setState(4005);
            result.setMessage("购物车商品没有找到");
        } else if (e instanceof InsertException) {
            result.setState(5000);
            result.setMessage("注册时产生未知的异常");
        } else if (e instanceof UserNotFoundException) {
            result.setState(5001);
            result.setMessage("用户没有找到的异常");
        } else if (e instanceof PasswordNotMatchException) {
            result.setState(5002);
            result.setMessage("用户名的密码不匹配的异常");
        } else if (e instanceof UpdateException) {
            result.setState(5003);
            result.setMessage("更新数据时产生未知的异常");
        } else if (e instanceof FileEmptyException) {
            result.setState(6001);
            result.setMessage("文件为空异常");
        } else if (e instanceof FileSizeException) {
            result.setState(6002);
            result.setMessage("文件大小异常");
        } else if (e instanceof FileStateException) {
            result.setState(6003);
            result.setMessage("文件状态异常");
        } else if (e instanceof FileUploadIOException) {
            result.setState(6004);
            result.setMessage("文件加载IO异常");
        } else if (e instanceof FileTypeException) {
            result.setState(6005);
            result.setMessage("文件类型异常");
        }
        return result;
    }

    /**
     * 获取Session对象中的Uid
     *
     * @param session session对象
     * @return 当前对象的Uid
     */
    protected final Integer getUidFromSession(HttpSession session) {
        return Integer.valueOf(session.getAttribute("userId")
                .toString());
    }

    /**
     * 获取Session对象的用户名
     *
     * @param session
     * @return
     */
    protected final String getUsernameFromSession(HttpSession session) {
        return session.getAttribute("username").toString();
    }
}

6、业务时间计算

主要使用面向切面编程思想

package com.atmae.store.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * @author Mae
 * 将当前类的对象创建使用维护交由spring容器
 * 将当前类标记为切面类
 */
@Aspect
@Component
@Slf4j
public class TimerAspect {
    /**
     * 映射到那个方法上
     */
    @Around("execution(* com.atmae.store.service.impl.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        /** 调用目标发发 login*/
        Object result = pjp.proceed();
        /** 后记录时间*/
        long end = System.currentTimeMillis();
        log.info("耗时" + (end - start) + "ms");
        return result;
    }
}

日志打印
在这里插入图片描述

四、功能演示

1、登录

登录页面:
在这里插入图片描述

2、注册页面

同登录页面差不多

3、首页:

在这里插入图片描述
首页的两列 新货和热销 主要是通过后台的查询商品操作 根据具体条件查询前四名
部分mapper文件:

<mapper namespace="com.atmae.store.mapper.ProductMapper">
    <resultMap id="ProductEntityMap" type="com.atmae.store.entity.Product">
        <id column="id" property="id"/>
        <result column="category_id" property="categoryId"/>
        <result column="item_type" property="itemType"/>
        <result column="sell_point" property="sellPoint"/>
        <result column="created_user" property="createdUser"/>
        <result column="created_time" property="createdTime"/>
        <result column="modified_user" property="modifiedUser"/>
        <result column="modified_time" property="modifiedTime"/>
    </resultMap>

    <!-- 查询热销商品的前四名:List<Product> findHostList() -->
    <select id="findHotList" resultMap="ProductEntityMap">
        SELECT *
        FROM t_book
        WHERE status = 1
        ORDER BY priority DESC LIMIT 0,4
    </select>

    <select id="findNewList" resultMap="ProductEntityMap">
        select *
        from t_book
        where status = 1
        order by created_time desc limit 0,4
    </select>

部分页面代码:

使用ajax响应数据

          <!--推荐栏目-->
            <div class="row">
                <div class="col-md-6">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <p class="panel-title">新到好货</p>
                        </div>
                        <div id="new-list" class="panel-body panel-item">
                        </div>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <p class="panel-title">热销排行</p>
                        </div>
                        <div id="hot-list" class="panel-body panel-item">
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<div th:replace="common::commonscipt"></div>

<script type="text/javascript">
    $(function () {
        showNewList();
        showHotList();
    });

    function showNewList() {
        $("#new-list").empty();
        $.ajax({
            url: "/products/newList",
            type: "GET",
            dataType: "JSON",
            success: function (json) {
                let list = json.data;
                console.log("count=" + list.length);
                for (let i = 0; i < list.length; i++) {
                    console.log(list[i].title);
                    let html = '<div class="col-md-12">'
                        + '<div class="col-md-7 text-row-2"><a href="/product?id=#{id}">#{title}</a></div>'
                        + '<div class="col-md-2">¥#{price}</div>'
                        + '<div class="col-md-3"><img src="assets/#{image}collect.png" class="img-responsive" /></div>'
                        + '</div>';

                    html = html.replace(/#{id}/g, list[i].id);
                    html = html.replace(/#{title}/g, list[i].title);
                    html = html.replace(/#{price}/g, list[i].price);
                    html = html.replace(/#{image}/g, list[i].image);

                    $("#new-list").append(html);
                }
            }
        });

    }

    function showHotList() {
        $("#hot-list").empty();
        $.ajax({
            url: "/products/hotList",
            type: "GET",
            dataType: "JSON",
            success: function (json) {
                let list = json.data;
                console.log("count=" + list.length);
                for (let i = 0; i < list.length; i++) {
                    console.log(list[i].title);
                    let html = '<div class="col-md-12">'
                        + '<div class="col-md-7 text-row-2"><a href="/product?id=#{id}">#{title}</a></div>'
                        + '<div class="col-md-2">¥#{price}</div>'
                        + '<div class="col-md-3"><img src="assets/#{image}collect.png" class="img-responsive" /></div>'
                        + '</div>';

                    html = html.replace(/#{id}/g, list[i].id);
                    html = html.replace(/#{title}/g, list[i].title);
                    html = html.replace(/#{price}/g, list[i].price);
                    html = html.replace(/#{image}/g, list[i].image);

                    $("#hot-list").append(html);
                }
            }
        });
    }
</script>

4、个人基本信息

在这里插入图片描述
加载页面即显示数据:ajax实现.

  $.ajax({
            url: "/users/getByUserId",
            data: $("#form-date").serialize(),
            type: "GET",
            dataType: "JSON",
            success: function (json) {
                if (json.state === 200) {
                    //将查询到的数据重新设置到控件中
                    $("#phone").val(json.data.phone);
                    $("#email").val(json.data.email);
                    if (json.data.gender === 0) {
                        let radio = $("#gender-female");
                        radio.prop("checked", "checked");
                    } else if (json.data.gender === 1) {
                        let radio = $("#gender-male");
                        radio.prop("checked", "checked");
                    }
                    // prop:给某个元素添加属性及属性值!
                } else {
                    alert("数据不存在!");
                }
            },
            error: function (xhr) {
                alert("查询用户信息时产生未知的异常!" + xhr.status);
            }
        });
        return false;
    });

5、头像修改

在这里插入图片描述
控制层代码:

 /**
     * 上传文件的最大值
     */
    private static final long AVATAR_MAX_SIZE = 10 * 1024 * 1024;

    /**
     * 限制上传文件的类型
     */
    private static final List<String> AVATAR_TYPE = new ArrayList<>();

    /** 静态块给集合初始化*/
    static {
        AVATAR_TYPE.add("image/png");
        AVATAR_TYPE.add("image/jpeg");
        AVATAR_TYPE.add("image/bmp");
        AVATAR_TYPE.add("image/gif");
    }

    @RequestMapping("/changeAvatar")
    public JsonResult<String> changeAvatar(HttpServletResponse response, HttpSession session, MultipartFile file) throws FileNotFoundException {
        /** 判断文件是否为空*/
        if (file.isEmpty()) {
            throw new FileEmptyException("文件为空");
        }
        /** 判断文件是否超出大小*/
        if (file.getSize() > AVATAR_MAX_SIZE) {
            throw new FileSizeException("文件超出限制");
        }
        /** 判断文件类型是否是规定的和后缀类型*/
        String contentType = file.getContentType();
        /** 如何集合包含某个元素则返回true*/
        if (!AVATAR_TYPE.contains(contentType)) {
            throw new FileTypeException("文件类型不支持");
        }
        /** 上传的文件放在 .../assets/img/avatar/  .(后缀)下*/
        String property = System.getProperty("user.dir");
        String path = property + "\\store\\src\\main\\resources\\static\\assets\\img\\avatar";
        /** File对象指向这个路径,File是否存在*/
        File dir = new File(path);
        /** 检测目录是否存在*/
        if (!dir.exists()) {
            /** 创建当前的目录*/
            dir.mkdirs();
        }
        /** 获取到这个文件名称 UUID工具来将生成一个新的字符串作为文件名*/
        String originalFilename = file.getOriginalFilename();
        /** 文件后缀*/
        int index = originalFilename.lastIndexOf(".");
        String suffix = originalFilename.substring(index);
        String filename = UUID.randomUUID().toString().toUpperCase() + suffix;
        /** 这是一个空文件*/
        File dest = new File(dir, filename);
        /** 参数file中数据写入到这个空文件中*/
        try {
            /** 将file文件中的数据写入到dest文件中*/
            file.transferTo(dest);
        } catch (IOException e) {
            throw new FileUploadIOException("文件读写异常");
        } catch (FileStateException e) {
            throw new FileSizeException("文件状态异常");
        }
        Integer userId = getUidFromSession(session);
        String username = getUsernameFromSession(session);
        /** 返回头像的路径*/
        String avatar = "assets/img/avatar/" + filename;
        userService.changeAvatar(userId, avatar, username);
        /** 存储cookie 响应给客户端*/
        Cookie cookie = new Cookie("img", avatar);
        response.addCookie(cookie);
        /** 返回用户头像的路径给前端页面,将来用于头像展示使用*/
        return new JsonResult<>(OK, avatar);
    }

将头像存储到cookie中,而用户的id和用户名存储在session中
头像会存放在服务器中,数据库中存放的是头像当前项目的相对地址

6、地址信息

在这里插入图片描述
可以设置默认收货地址和增加收货地址
在这里插入图片描述
页面的省市区通过ajax方式加载

没有选择大的区域则不显示小区域

   $(function () {
        /*value 属性用于表示当前区域的code值*/
        let defaultOption = "<option value='0'>---- 请选择 ----</option>";
        showProvinceList();
        $("#city-list").append(defaultOption);
        $("#area-list").append(defaultOption);
        /** change():监听某个控件一旦发生改变 就会触发*/
        $("#province-list").change(function () {
            // 获取行政区的父代码
            let parent = $("#province-list").val();
            // 清空select下拉列表的所有 option元素
            $("#city-list").empty();
            $("#area-list").empty();
            //填充默认值
            $("#city-list").append(defaultOption);
            $("#area-list").append(defaultOption);
            if (parent === 0) {
                return;
            }
            $.ajax({
                url: "/district",
                type: "POST",
                data: "parent=" + parent,
                dataType: "JSON",
                success: function (json) {
                    if (json.state === 200) {
                        let list = json.data;
                        for (let i = 0; i < list.length; i++) {
                            let opt = "<option value='" + list[i].code + "'>" + list[i].name + "</option>";
                            $("#city-list").append(opt);
                        }
                    } else {
                        alert("信息加载失败!");
                    }
                }
            });
        });

        $("#city-list").change(function () {
            // 获取行政区的父代码
            let parent = $("#city-list").val();
            // 清空select下拉列表的所有 option元素
            $("#area-list").empty();
            //填充默认值
            $("#area-list").append(defaultOption);
            if (parent === 0) {
                return;
            }
            $.ajax({
                url: "/district",
                type: "POST",
                data: "parent=" + parent,
                dataType: "JSON",
                success: function (json) {
                    if (json.state === 200) {
                        let list = json.data;
                        for (let i = 0; i < list.length; i++) {
                            let opt = "<option value='" + list[i].code + "'>" + list[i].name + "</option>";
                            $("#area-list").append(opt);
                        }
                    } else {
                        alert("信息加载失败!");
                    }
                }
            });
        });

        function showProvinceList() {
            $.ajax({
                url: "/district",
                type: "POST",
                data: "parent=86",
                dataType: "JSON",
                success: function (json) {
                    if (json.state === 200) {
                        let list = json.data;
                        for (let i = 0; i < list.length; i++) {
                            let opt = "<option value='" + list[i].code + "'>" + list[i].name + "</option>";
                            $("#province-list").append(opt);
                        }
                    } else {
                        alert("信息加载失败!");
                    }
                }
            });
            return false;
        }

        $("#btn-add-new-address").click(function () {
            $.ajax({
                url: "/address/addNewAddress",
                type: "POST",
                data: $("#form-add-new-address").serialize(),
                dataType: "JSON",
                success: function (json) {
                    if (json.state === 200) {
                        alert("新增收货地址成功!");
                        location.href = "/addAddress";
                    } else {
                        alert("新增收货地址失败!");
                    }
                },
                error: function (xhr) {
                    alert("新增收货地址产生未知的异常!" + xhr.status);
                }
            });
            return false;
        });
    });

在这里插入图片描述
在这里插入图片描述

7、购物页面

查询所有书籍即可
在这里插入图片描述

8、购物车

在这里插入图片描述
在这里插入图片描述
也可以增加数量和删除

9、结算

在这里插入图片描述
在这里插入图片描述

五、总结

纸上得来终觉浅
用来练手,很好的复习了学过的知识

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 22:56:58  更:2022-01-29 22:57:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:33:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码