springboot+bootstrap实现图书商城管理(大三下学期课程设计)

2022-12-14,,,

在csdn上记一次自己的课程设计过程(已经实习两个月了、感觉这个很容易做。支付可能需要多花点时间、):

在此框架基础之上权限认证管理设置成功:https://blog.csdn.net/weixin_43304253/article/details/121111530
动态搜索书籍信息(包括在一定价格范围内查询):https://blog.csdn.net/weixin_43304253/article/details/120920214
课程名称:企业项目实训II
设计题目:大学当图书商城

已知技术参数和设计要求:

1.问题描述(功能要求):
1.1 顾客端
1)注册登录:游客可浏览所有图书,只有注册用户才能添加购物车、下订单购买、评论;
2)图书分类浏览:图书分三个层级进行分类浏览;
3)动态搜索图书:可以按书名、作者、出版社及价格范围进行搜索,搜索的图书分页显示;
4)图书详情:可从图书列表中进一步看到图书的详细信息,包括书号、书名、作者、出版社、印次、内容简介等信息;
5)添加并管理购物车:登录用户可以选择图书加入到购物车,并可对购物车的图书进行增删改查的操作;
6)下订单:登录用户可对图书进行下订单,同时可查看订单当前的状态,订单状态包括待付款(模拟)、待发货(模拟)、待收货(模拟)、待评论;
7)模拟支付:所有注册用户都有一个余额钱包,可模拟充值,充值记录保存下来并可查看,利用余额钱包进行付款。或者使用支付宝的模拟支付接口进行支付;
8)商品评论:购买完图书后进行评价,给一个1-5等级及相应的评论内容。
1.2商家
1)商家注册登录:注册信息包括商家名称,商家地址、经营类型、注册资金、log图片等基本信息,刚注册的商家处于未审核状态,只有等待区域运营方审核后才能正常登录。区域运营方由管理员在后台直接添加;
2)商家管理自家商品:可对商品进行相应的增、删、改、查的操作;
3)商家管理自家店铺:商家可根据平台提供的模板,对店铺进行管理;
4)商家管理订单:商家可对顾客下的订单进行相应的操作,如查看订单、发货(模拟)、查看评论。

    运行环境要求:
    (1)客户端:
    Windows操作系统
    浏览器
    (2)服务器:
    windows操作系统
    Tomcat 服务器
    MySQL 数据库服务器

    技术要求:
    (1)需求分析规格说明书与用例规约
    (2)系统数据库设计,时序图,类图
    (3)系统采用SSM框架技术,完整编码(采用spring boot和vue)
    (4)为保证系统安全性,要求用到日志
    (5)在必要地方使用事务技术
    (6)在必要地方使用2个以上设计模式

设计工作量:
8周

工作计划:
共安排8周时间进行集中实训,软件开发步骤如下,
第1-2周:需求分析、数据库设计、系统设计
第3-7周:编码
第8周:系统测试、答辩、撰写设计说明书

工作进展:在开始编辑这篇文章的时候、距离课程设计已经过了两个周(本人在实习、只能抽出空闲时间来写和周六、周日的时间)现在这个系统的后台逻辑几乎都实现了、还有支付功能未实现。前端页面的美化后来进一步处理吧。

目前的目录结构、后续还需要改进

1、搭建框架
(我花了一天的时间将整个项目的框架搭建起来、使用spring boot整合mybatis、thymeleaf和shiro)
使用spring boot是真的爽到上天、spring简直就是配置地狱。

整合mybatis的目的是简化sql的编写管理
整合thymeleaf的目的是数据的交互
整合shiro的目的是简化权限认证以及授权…

使用到的maven依赖

    <dependencies>
<!--整合shiro
subject:用户
security manager:管理所有的用户
realm:连接数据库 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency> <!--整合mybatis-->
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> <!-- Mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency> <!-- 导入页面依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> --> <!-- thymeleaf,都是基于3.x开发的-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency> <dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <!-- 简化set和get方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<scope>provided</scope>
</dependency> <!-- 导入日志依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency> <!-- 日志文件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency> <!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency> </dependencies>

2、接下来就是编写相应的代码(太简单了、几乎是无脑操作)

随便展示一个书籍的操作、从前台页面到后端代码的编写

先看效果图

前端页面代码


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>书籍列表</title>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head>
<body> <div class="container"> <!--导航栏部分-->
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<a class="navbar-brand" href="#">书籍商城</a>
</div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li>
<a th:href="@{/customer/toCustomerIndex}">个人信息</a>
</li>
<li>
<a href="#">购买记录</a>
</li>
<li>
<a href="#">购物车</a>
</li>
<li>
<a th:href="@{/book/bookList}">书籍商城</a>
</li> </ul> </div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
<hr> <div class="row ">
<form th:action="@{/book/bookList}" style="float: left"> <div class="col-md-2">
<input type="text" name="queryBookName" class="form-control" placeholder="书籍名">
</div>
<div class="col-md-2" >
<input type="text" name="queryBookAuthor" class="form-control" placeholder="作者名">
</div>
<div class="col-md-2" >
<input type="text" name="queryBookAddress" class="form-control" placeholder="出版社">
</div> <div class="col-md-4" >
<div class="col-row">
<div class="col-md-5">
<input type="text" name="minPrice" class="form-control" placeholder="最低价格">
</div>
<div class="col-md-1">
<span><strong>:</strong></span>
</div>
<div class="col-md-5">
<input type="text" name="maxPrice" class="form-control" placeholder="最高价格">
</div>
</div> </div>
<input type="submit" value="查询" class="btn btn-primary">
</form> </div>
<hr>
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<!-- <th>书籍编号</th>-->
<th>书籍名称</th>
<th>书籍作者</th>
<th>书籍价格</th>
<th>书籍出版社</th>
<th>操作</th>
</tr>
</thead>
<!--查询书籍处理-->
<tbody>
<tr th:each="book:${bookList}">
<!-- <td th:text="${book.getBookId()}">编号</td>-->
<td th:text="${book.getBookName()}">书名</td>
<td th:text="${book.getBookAuthor()}">作者</td>
<td th:text="${book.getPrice()}"></td>
<td th:text="${book.getAddress()}">出版社</td>
<td><a th:href="@{/book/todetail(bookId=${book.getBookId()})}" >详情</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a th:href="@{/shop/addshop(bookId=${book.getBookId()})}" >加入购物车</a></td> </tr>
</tbody>
</table>
<div class="col-md-3">
<a class="btn btn-primary" th:href="@{/book/bookList}">返回查询首页</a>
</div>
<div class="col-md-5">
<p ><strong>当前</strong> <span th:text="${pageInfo.pageNum}"></span><strong> 页,总 </strong><span th:text="${pageInfo.pages}"></span><strong> 页,共</strong> <span th:text="${pageInfo.total}"></span><strong> 条记录</strong></p> </div> <div class="col-md-4">
<div class="row">
<div class="col-md-12">
<a class="btn btn-primary" th:href="@{/book/bookList}">首页</a>
<a class="btn btn-primary" th:href="@{/book/bookList(pageNum=${pageInfo.hasPreviousPage}?${pageInfo.prePage}:1)}">上一页</a>
<a class="btn btn-primary" th:href="@{/book/bookList(pageNum=${pageInfo.hasNextPage}?${pageInfo.nextPage}:${pageInfo.pages})}">下一页</a>
<a class="btn btn-primary" th:href="@{/book/bookList(pageNum=${pageInfo.pages})}">尾页</a>
</div>
</div>
</div> </div>
</div> </div>
</body>
</html>

编写的书籍实体类

(没有使用lombox、失去了Java的灵魂、不喜欢用)

package com.example.zheng.pojo;

public class Books {
private String bookId;
private String bookName;
private String bookAuthor;
private Double price;
private String address;
private String impression;
private String introduce; //新增这两个字段的原因,将前台传入的价格范围封装、在编写sql语句的时候动态替换价格参数
private Double minPrice;//最小价格
private Double maxPrice;//最大价格 public Books(String bookId, String bookName, String bookAuthor, Double price, String address, String impression, String introduce, Double minPrice, Double maxPrice) {
this.bookId = bookId;
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.price = price;
this.address = address;
this.impression = impression;
this.introduce = introduce;
this.minPrice = minPrice;
this.maxPrice = maxPrice;
} public Double getPrice() {
return price;
} public void setPrice(Double price) {
this.price = price;
} public Double getMinPrice() {
return minPrice;
} public void setMinPrice(Double minPrice) {
this.minPrice = minPrice;
} public Double getMaxPrice() {
return maxPrice;
} public void setMaxPrice(Double maxPrice) {
this.maxPrice = maxPrice;
} public Books() { } public String getBookId() {
return bookId;
} public void setBookId(String bookId) {
this.bookId = bookId;
} public String getBookName() {
return bookName;
} public void setBookName(String bookName) {
this.bookName = bookName;
} public String getBookAuthor() {
return bookAuthor;
} public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
} public String getAddress() {
return address;
} public void setAddress(String address) {
this.address = address;
} public String getImpression() {
return impression;
} public void setImpression(String impression) {
this.impression = impression;
} public String getIntroduce() {
return introduce;
} public void setIntroduce(String introduce) {
this.introduce = introduce;
}
}

controller层

(这里的传输数据可以使用其他的方式ajax等等、我这里是暂时走通前后端。后期会进一步改造)

package com.example.zheng.controller;

import com.example.zheng.pojo.Books;
import com.example.zheng.service.BooksService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @Controller
public class BooksController { @Autowired
BooksService booksService; @RequestMapping("/book/bookList")
public String bookList(@RequestParam(defaultValue = "1",value = "pageNum") Integer pageNum,Double minPrice,Double maxPrice, String queryBookAuthor, String queryBookAddress, String queryBookName, Model model){
PageHelper.startPage(pageNum,10);
Books books =new Books();
books.setBookAuthor(queryBookAuthor);
books.setBookName(queryBookName);
books.setAddress(queryBookAddress);
books.setMinPrice(minPrice);
books.setMaxPrice(maxPrice); try {
List<Books> list = booksService.queryBookList(books);
PageInfo<Books> pageInfo = new PageInfo<Books>(list);
model.addAttribute("pageInfo",pageInfo);
if(list == null){
model.addAttribute("error","书籍列表为空");
}else{
model.addAttribute("bookList",list);
}
} catch (Exception e) {
e.printStackTrace();
}
return "allBook"; } @RequestMapping("/book/todetail")
public String bookDetail(String bookId,Model model){
try {
Books books = booksService.queryBookById(bookId);
if(books == null){
model.addAttribute("error","查询书籍详细信息失败");
}else{
model.addAttribute("books",books);
}
} catch (Exception e) {
e.printStackTrace();
} return "bookDetail"; } }

service层

package com.example.zheng.service;

import com.example.zheng.pojo.Books;

import java.util.List;

public interface BooksService {
/**
* 查询图书
*/
public List<Books> queryBookList(Books books); /**
* 查询书籍的详细信息通过书籍编号
*/ public Books queryBookById(String bookId);
}

实现类

package com.example.zheng.service.impl;

import com.example.zheng.mapper.BooksMapper;
import com.example.zheng.pojo.Books;
import com.example.zheng.service.BooksService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.List; @Service
public class BooksServiceImpl implements BooksService { @Autowired
BooksMapper booksMapper; //查询所有书籍
@Override
public List<Books> queryBookList(Books books) {
return booksMapper.queryBookList(books);
} //根据id查询具体书籍信息
@Override
public Books queryBookById(String bookId) {
return booksMapper.queryBookById(bookId);
}
}

Dao层

package com.example.zheng.mapper;

import com.example.zheng.pojo.Books;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository; import java.util.List; @Mapper //这个注解表示这个是mybatis的mapeper
@Repository
public interface BooksMapper { /**
* 查询图书
*/
public List<Books> queryBookList(Books books); /**
* 查询书籍的详细信息通过书籍编号
*/ public Books queryBookById(String id); }

sql语句

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.zheng.mapper.BooksMapper"> <select id="queryBookList" parameterType="com.example.zheng.pojo.Books" resultType="com.example.zheng.pojo.Books">
select * from bookss
<where>
1=1
<if test="bookName != null and bookName != ''">
And bookName =#{bookName}
</if>
<if test="bookAuthor != null and bookAuthor != ''">
And bookAuthor =#{bookAuthor}
</if>
<if test="address != null and address !=''">
And address =#{address}
</if> <if test="minPrice !=null and minPrice !=''">
And price > #{minPrice}
<if test="maxPrice !=null and maxPrice !=''">
And price &lt; #{maxPrice}
</if>
</if> </where>
</select> <select id="queryBookById" resultType="com.example.zheng.pojo.Books">
select * from bookss where bookId=#{bookId}
</select> </mapper>

差不多整个流程就是这样。大差不差。

在开发中遇到的问题以及解决的办法?

一、在整合框架的时候遇到的奇葩问题:

1、跳转到指定页面出现问题?必须要在properties中配置,这样的目的是,说白了是个视图解析器。和我以前写的不一样,这个也太简单了
2、必须在pom依赖中添加试图解析的依赖。
3、图片路径的问题,添加static则不显示图片,这个是相对路径,找不出来。就很离谱。解决方法:把static删除,然后重启idea。
4、@Controller。跳转到指定页面中要使用这个注解,另外一个出现问题

二、使用thymeleaf在进行传输数据的时候遇到的奇葩

这里需要注意的是,怎样获取当前书籍的主键、以及怎样将这个主键传递到controller层对应的方法中。将这个主键作为查询条件。
解决的方法:<a th:href="@{/book/todetail(bookId=${book.getBookId()})}" >详情</a>
这个方法是thymleaf特有的传输形式

1、改进书籍展示的信息、将书籍编号隐藏。只给顾客展示、书名、作者、出版社、价格

解决办法:1、首先是想到隐藏该列信息、但是没能成功。2、直接将该列信息代码删除、在点击详情需要根据改行的图书主键搜索数据库。由于当前的对象中,已经包含该图书的所有信息。可以直接拿来使用。当前行图书主键存在的意义不是很大。

三、实现在一定价格区间内搜索图书。

遇到的问题:编写的问题sql语句中的价格参数是变动的。不能将其写死在sql中。

解决办法:直接在前端页面设置两个输入框,用来接受数据。同时在实体类书籍中添加最大价格和最小价格两个属性、将前端的数据封装到实体类中,然后将数据直接传输到sql中。

遇到的问题:在新增两个属性后、查询不到数据、爆空指针异常。

经过分析发现、在实体类中书籍的价格应使用Double而不是double.idea没有给我报错、离谱。这种错误我也是醉了。我还真找了好一会。

遇到的问题:获得了参数、在写入sql中,<号不能使用。总是爆红、上网搜索、需要使用转移字符:&lt;

四、遇到的问题? 怎样将查询到的用户权限放入shiro中、进行接下来的认证。

解决办法:将查询到的用户
已经查询到当前用户具有的权限

 //授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行了授权"); //拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
Customer currentCustomer = (Customer) subject.getPrincipal();//拿到对象
//设置当前用户的权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//设置集合
Collection<String> perStringCollection = new HashSet<String>(); //已经查询到当前用户具有的权限
Set<Roles> permissionSet =rolesService.queryRoles(currentCustomer.getUsercount());
for(Roles perm : permissionSet){
//将每一个当前用户的权限加入
perStringCollection.add(perm.getAuthName());
}
info.addStringPermissions(perStringCollection);
return info; }

五、根据登录用户、在查询购物车的时候、查询当前用户的购物车。在购买商品的时候加入到当前用户的购物车中

主要的代码是:

Customer parent = (Customer) SecurityUtils.getSubject().getPrincipal();

待编辑

springboot+bootstrap实现图书商城管理(大三下学期课程设计)的相关教程结束。

《springboot+bootstrap实现图书商城管理(大三下学期课程设计).doc》

下载本文的Word格式文档,以方便收藏与打印。