1.项目架构
Spring Boot + Thymeleaf + Mybatis
2.功能模块
(1)前台功能模块
- 用户模块:注册(上传头像)、登录(后可以实时显示出用户上传的头像)、更新个人信息、退出登录
- 商品模块:商品首页、分类分页查看商品、查看商品的详情、模糊搜索商品、发布二手商品(上传商品的图片)并删除
- 购物车模块:购物车中添加商品、查看购物车(可以显示出购物车中的商品数量)、删除购物车中的某件商品
- 地址模块:添加地址、删除地址、查询地址、修改地址
- 订单模块:下单(增加订单、增加订单详情)、查询订单(卖出订单+购买订单)、订单发货收货、取消订单
- 支付模块:使用支付宝沙箱环境进行支付
- 收藏模块:添加收藏、查看收藏、删除收藏
- 留言模块:添加留言、查询留言
(2)后台功能模块
主要实现用户模块、商品模块、订单模块、分类模块、系统管理。
- 用户模块:分页查看所有用户(可以设置每页显示条数)(显示的用户按照注册时间降序排列)、按照用户的用户名或者学校对用户进行模糊搜索、单选或多选删除用户、修改某个用户的信息。
- 商品模块:分页查看所有发布的商品(可以设置每页显示条数)(显示的商品按照注册时间降序排列)、按照商品名称或者商品分类对商品进行模糊搜索、单选或多选删除商品(管理员删除是将商品的状态修改为3表示删除)、修改某个商品的信息。
- 订单模块:分页查看所有的订单(可以设置每页显示条数)(显示的订单按照下单时间降序排列)、按照订单编号对订单进行搜索、单选或多选删除订单信息、修改某个订单的信息、查看某个订单的详情。
- 分类模块:对已有商品分类进行修改、添加新的商品分类。
- 系统管理:登录、对个人的基本信息(登录名称和邮箱)进行修改。对密码进行修改。安全退出。
3.实现业务逻辑:
4.自定义拦截器
4.1.实现HandlerInterceotor接口
(1)preHandler(),return 的是boolean,true时,不拦截请求;false时,拦截请求。请求前执行。
(2)postHandler()请求后执行。
(3)afterCompletion()响应页面渲染后执行。
4.2.注册拦截器
自定义类:
(1)实现WebMvcConfigurer接口
(2)加上注解,@SpringBootConfiguration//Spring Boot的配置类,标注在某个类上,表示这是一个Spring Boot的配置类
(3)在addInterceptors(InterceptorRegistry registry)方法中注册拦截器,方法中通过registry.addInterceptor(portalLoginCheckInterceptor)//括号中的是拦截器
.addPathPatterns("/portal/user/**")//添加需要拦截的路径
.excludePathPatterns(excludeUrl);//排除不需要拦截的路径
注意:如果注册了多个拦截器,则按照拦截器的顺序执行,先注册的先执行
5.连接支付宝网站支付的沙箱环境
5.1.在pom.xml文件中添加依赖
5.2.创建AlipayConfig.java文件
5.3.把demo中alipay.trade.page.pay.jsp改成controller控制层代码
支付接口调用流程
调用顺序如下:
- 商户系统请求支付宝接口alipay.trade.page.pay,支付宝对商户请求参数进行校验,而后重新定向至用户登录页面。
- 用户确认支付后,支付宝通过get请求returnUrl(商户入参传入),返回同步返回参数。
- 交易成功后,支付宝通过post请求notifyUrl(商户入参传入),返回异步通知参数。
若由于网络等问题异步通知没有到达,商户可自行调用交易查询接口alipay.trade.query进行查询,根据查询接口获取交易以及支付信息(商户也可以直接调用查询接口,不需要依赖异步通知)。
6.图片上传
页面的三个条件:
(1)必须指定method属性为post
(2)必须指定enctype属性为multipart/form-data
(3)使用类型为file的<input>标签,供用户选择文件
在业务方法中添加MultipartFile类型的形参,调用transferTo()方法,并生成唯一的文件名。
//获取原始文件名
String originalFilename = file.getOriginalFilename(); // xxx.png
//获取扩展名
String extend = originalFilename.substring(originalFilename.lastIndexOf("."));
//重新生成唯一的文件名
String newName = UUID.randomUUID().toString();
File file1 = new File(uploadPath,newName + extend);
try {
file.transferTo(file1);//保存文件
user.setPhoto(newName + extend);
} catch (IOException e) {
e.printStackTrace();
}
静态资源的设置:
一、spring boot项目中上传图片到本项目的路径下,需要重启项目才能显示图片解决方法
(1)application-dev.yml文件中设置上传文件的路径
(2)在controller层,获取在yml文件中的路径
(3)在新建的配置文件中映射图片的静态路径
addResourceHandler()里配置需要映射的文件夹,此处代表映射文件夹img/commodity下的所有资源。
addResourceLocations()配置文件夹在系统中的路径,使用绝对路径,格式为“file:你的路径”
二、spring boot项目部署到阿里云服务器上显示上传图片
(1)application-prod.yml文件中设置上传文件的路径
上传的文件的路径写的是绝对路径,在Linux环境下,要创建出对应的目录
(2)在controller层,获取在yml文件中的路径(同上)
(3)在新建的配置文件中映射图片的静态路径(同上不需要修改)
7.Springboot中PageHelper分页查询使用方法(mybatis+thymeleaf)
一:导入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
二:配置yml文件中PageHelper的属性
pagehelper: #分页插件
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params:
三:在controller类中使用
1.在查询方法上调用PageHelper.startPage()方法,设置分页的页数和每页信息数,
2.将查询出来的结果集用PageInfo的构造函数初始化为一个分页结果对象
3.将分页结果对象存入model,返回前端页面
四:前端展示分页主要是thymeleaf的使用。
8.如何实现下单?
如何进行一些批量操作:
<delete id="deleteByIds" parameterType="list">
delete from collect
where collectionid in
<foreach collection="idList" item="collectionid" open="(" close=")" separator=",">
#{collectionid}
</foreach>
</delete>
9.地址的三级联动?
实现思路:
1.创建省市县三级地址,分别采用下列方式表示
省份:使用一维数组
城市:使用二维数组,和一维数组省份对应
区/县:使用三维数组,和二维数组城市对应
2.创建HTML文件,然后创建三个select选择框,分别代表省市县
3.创建实现联动的js文件,然后按照下面步骤实现
(1)获取三个select对象
- 设置省份
遍历省份数组
创建option对象
将option对象加追加到select中
- 设置城市
获取已经选择的省份下标
根据省份下标遍历城市数组
创建option对象
将option对象加追加到select中
- 设置县区
获取已经选择的省份和城市下标
根据省份和城市下标遍历区县数组
创建option对象
将option对象加追加到select中
4.网页加载完成应该将省市县显示在第一个
当省份改变城市的区县一起改变
当城市改变区县改变
三级联动的js实现:
// 当框框加载完成之后调用设置省份
window.onload = setProvince;
// 获取省市县/区的select选择框对象
var province = document.getElementsByTagName("select")[0];
var city = document.getElementsByTagName("select")[1];
var county = document.getElementsByTagName("select")[2];
// 设置省份
function setProvince() {
// 遍历省份数组, provinceArr在city.js中
for (var i = 0; i < provinceArr.length; i++){
// 创建省份option选项
var opt = document.createElement("option");
opt.value = provinceArr[i]; // 设置value-提交给服务器用
opt.innerHTML = provinceArr[i]; // 设置option文本显示内容
province.appendChild(opt); // 追加省份到下拉框
// 当省份发生变化更改城市
province.onchange = function(){
setCity(this.selectedIndex);
};
}
// 省份加载完成,默认显示第一个省份的城市
setCity(0);
}
// 设置城市
function setCity(provincePos) {
// 获取省份对象的城市,cityArr在city.js中
var citys = cityArr[provincePos];
city.length = 0; // 清空长度,重新从0开始赋值下拉框
for (var i = 0; i < citys.length; i++){
// 创建城市option选项
var opt = document.createElement("option");
opt.value = citys[i]; // 设置value-提交给服务器用
opt.innerHTML = citys[i]; // 设置option文本显示内容
city.appendChild(opt);
city.onchange = function() {
setCounty(provincePos, this.selectedIndex);
}
}
// 默认显示城市的第一个县/区
setCounty(provincePos, 0);
}
// 设置县/区, 县/区是三位数组,需要传入哪个省份和城市
function setCounty(provincePos, cityPos) {
// 获取县/区,countyArr在city.js中国
var countys = countyArr[provincePos][cityPos];
county.length = 0;
for (var i = 0; i < countys.length; i++){
// 创建县/区option选项
var opt = document.createElement("option");
opt.value = countys[i]; // 设置value-提交给服务器用
opt.innerHTML = countys[i]; // 设置option文本显示内容
county.appendChild(opt); // 追加到县/区选择框中
}
}
数据回响实现联动:
/*
* 获取某个元素下标
* arr: 传入的数组
* obj: 需要获取下标的元素
* */
function getArrayIndex(arr, obj) {
var i = arr.length;
while (i--) {
if (arr[i] === obj) {
return i;
}
}
return -1;
}
// 设置城市
function getCity(provincePos, receiverCity) {
// 获取省份对象的城市,cityArr在city.js中
var citys = cityArr[provincePos];
city.length = 0; // 清空长度,重新从0开始赋值下拉框
for (var i = 0; i < citys.length; i++){
// 创建城市option选项
var opt = document.createElement("option");
opt.value = citys[i]; // 设置value-提交给服务器用
opt.innerHTML = citys[i]; // 设置option文本显示内容
if (opt.value == receiverCity){
opt.selected = true; // 让回响的数据设置为选中
}
city.appendChild(opt);
city.onchange = function() {
setCounty(provincePos, this.selectedIndex);
}
}
// 默认显示城市的第一个县/区
setCounty(provincePos, 0);
}
// 设置县/区, 县/区是三位数组,需要传入哪个省份和城市
function getCounty(provincePos, cityPos, receiverDistrict) {
// 获取县/区,countyArr在city.js中国
var countys = countyArr[provincePos][cityPos];
county.length = 0;
for (var i = 0; i < countys.length; i++){
// 创建县/区option选项
var opt = document.createElement("option");
opt.value = countys[i]; // 设置value-提交给服务器用
opt.innerHTML = countys[i]; // 设置option文本显示内容
if (opt.value == receiverDistrict){
opt.selected = true; // 让回响的数据设置为选中
}
county.appendChild(opt); // 追加到县/区选择框中
}
}
10.后台登录时验证码的实现?
运用Google的Kaptcha
(1)首先引入依赖
<!-- 验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
(2)KaptchaConfig配置。
主要对图片边框、字体颜色、图片宽高、字体大小、验证码长度等进行设置。
package com.neuedu.controller.back;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Component
public class KaptchaConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha(){
com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
Properties properties = new Properties();
//图片边框
properties.put("kaptcha.border", "no");
//字体颜色
properties.put("kaptcha.textproducer.font.color", "black");
//图片宽
properties.put("kaptcha.image.width", "150");
//图片高
properties.put("kaptcha.image.height", "40");
//字体大小
properties.put("kaptcha.textproducer.font.size", "30");
//session key
properties.put("kaptcha.session.key", "verifyCode");
//验证码长度
properties.put("kaptcha.textproducer.char.space", "5");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
(3)CommonController,生产验证码字符串并保存到session中,使用生成的验证码字符串返回一个BufferedImage对象并转换为byte写入到byte数组中;定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组。
package com.neuedu.controller.back;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
@Controller
public class CommonController {
@Autowired
private DefaultKaptcha captchaProducer;
@GetMapping("/common/kaptcha")
public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
byte[] captchaOutputStream = null;
ByteArrayOutputStream imgOutputStream = new ByteArrayOutputStream();
try {
//生产验证码字符串并保存到session中
String verifyCode = captchaProducer.createText();
httpServletRequest.getSession().setAttribute("verifyCode", verifyCode);
// 使用生成的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
BufferedImage challenge = captchaProducer.createImage(verifyCode);
ImageIO.write(challenge, "jpg", imgOutputStream);
} catch (IllegalArgumentException e) {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
captchaOutputStream = imgOutputStream.toByteArray();
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
responseOutputStream.write(captchaOutputStream);
responseOutputStream.flush();
responseOutputStream.close();
}
}
11.模态框的实现?
SweetAlert
12.模糊搜索如何实现?排序如何实现?
<select id="findCommodityList" parameterType="Map" resultMap="BaseResultMap">
select shopid, shopname, description, picture, price, flag, userid, typeid, status,
create_time, update_time, sub_pictures, category
from commodity
where flag = 0
<if test="keyword!=null">
AND (shopname like CONCAT('%',#{keyword},'%' ) or category like CONCAT('%',#{keyword},'%' ))
</if>
<if test="status!=null">
AND status = #{status}
</if>
<if test="typeid!=null">
AND typeid = #{typeid}
</if>
order by shopid desc
<if test="start!=null and limit!=null">
limit #{start},#{limit}
</if>
</select>
13.项目部署
(1)购买阿里云服务器
(2)Xshell连接阿里云(Linux下应用Xshell通过SSH连接云服务器)
(3)开放端口
(4)配置线上环境
创建普通用户
安装jdk&tomcat&mysql
(5)线上部署
spring boot项目打包成jar包,运行jar包,通过java -jar xx.jar
在命令行中进行打包,首先删除掉项目中的target目录
mvn clean package -DskipTests //打包,跳过单元测试
等待打包成功,显示BUILD SUCCESS,在项目目录下生成一个target目录,找到xx.jar文件上传到Linux中,上传的位置,总是上传到Linux下所在的当前目录。
设置成一个后台进程,关闭Xshell后依然可以运行,执行命令nohup java -jar busines.jar &
将数据库部署到服务器上
进入Linux的mysql中,首先上传数据库文件
输入命令mysql -u root -p,输入密码。构建数据库。
连接支付宝的沙箱环境,进行网站支付,需要在支付宝开放平台的开发者中心的沙箱环境中修改回调地址。