目录
部分图片展示
application.properties
创建数据库并且验证是否静态资源能够正常访问
创建用户表
实体类
持久层
业务层
控制层
拦截器
单元测试
部分图片展示
以下是大体上的代码
application.properties
首先我们先在application.properties中配置好数据库的连接以及mapper的位置(就是dao对应的mybatis文件)
spring.datasource.url=jdbc:mysql://localhost:3306/store?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=Wuyuhang
spring.datasource.password=2002514wyh11
mybatis.mapper-locations=classpath:mapper/*.xml
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=15MB
创建数据库并且验证是否静态资源能够正常访问
静态资源都放在static目录下,这是默认好的(可以通过实现webMvcConfigurer进行配置路径,或者通过yaml);
创建用户表
最下面那四个是基础字段——>我们将其放到基类中;
盐值:加密——>方便后续的修改密码;
isDelete:验证是否被删除,因为你可能操作的时候被管理员删了;
实体类
package com.cy.computerstore.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author diao 2022/3/20
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends BaseEntity implements Serializable {
/*这里我们都用包装类,因为他里面有一些api方法,方便之后的业务逻辑
* */
private Integer uid ;
private String username;
private String password;
private String salt;
private String phone;
private String email;
private Integer gender;
private String avatar;
private Integer isDelete;
}
基类:
package com.cy.computerstore.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
/**
* @author diao 2022/3/20
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseEntity implements Serializable {
private String createdUser;
private Date createdTime;
private String modifiedUser;
private Date modifiedTime;
}
持久层
首先规划最底层的功能——>dao接口,然后再mybatis中写对应的sql
package com.cy.computerstore.mapper;
import com.cy.computerstore.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
/**
* @author diao 2022/3/20
*/
/*用户模块持久层接口*/
public interface UserMapper {
//这里用包装类可以利用它的api进行业务判断:比如根据插入条数判断成功之类的
/**
* 插入用户的数据
* @param user 用户的数据
* @return 受影响的行数(增删改影响的行数作为返回值,根据返回值进行判断是否成功)
*/
Integer insert(User user);
/**
* 根据用户名查询用户数据
* @param username 用户名
* @return 如果找到对应用户就返回这个用户数据,否则返回null值
*/
User findByUsername(String username);
/**
* 根据uid来修改用户密码
* @param uid
* @param password:用户输入的新密码
* @param modifiedUser:修改的执行者
* @param modifiedTime:修改数据的时间
* @return
*/
Integer updatePasswordByUid(@Param("uid") Integer uid,
@Param("password") String password,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime
);
/**
* 更改密码首先要判断该用户是否存在,不排除管理员误删的情况
* @param uid 用户id
* @return 返回对象
*/
User findByUid(Integer uid);
/**
* 更改用户信息
* @param user:user对象(用户数据)
* @return:返回整数条数
*/
Integer updateInfoByUid(User user);
/**
* @Param("SQL映射文件#{}占位符的变量名")
* 和映射的接口的参数名不一致时,需要将某个参数强行注入到门口个占位符变量上
* 根据用户的uid值修改用户头像
* @param uid
* @param avatar
* @param modifiedUser
* @param modifiedTime
* @return
*/
Integer updateAvatarByUid(
@Param("uid") Integer uid,
@Param("avatar") String avatar,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime);
}
对应的mapper.xml
提一嘴,mybatis利用的是两步映射——>1.一个namespace指定接口,通过接口利用JDK动态代理实现代理类的作用并将其加载到容器中 ;2.还一个id来指定方法名
insert into t_user(username, password, salt, phone,
email, gender, avatar, is_delete,
created_user, created_time,
modified_user, modified_time)
values (
#{username}, #{password}, #{salt}, #{phone},
#{email}, #{gender}, #{avatar}, #{isDelete},
#{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime}
)
update t_user set
password=#{password},
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
where uid=#{uid}
update t_user
set
-- if表示条件标签,test接收的是一个boolean判断条件
phone=#{phone},
email=#{email},
gender=#{gender},
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
where
uid=#{uid}
update t_user
set
avatar=#{avatar},
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
where
uid=#{uid}
项目中的接口位置需要设定——>我们可以在Application类中利用@MapperScan()来设定
业务层
建议的标准建包形式:
1.首先先写业务接口IUserService——>规范大体的业务方法
package com.cy.computerstore.service;
import com.cy.computerstore.entity.User;
/**
* @author diao 2022/3/22
*/
/*用户模块业务层接口*/
public interface IUserService {
/**
* 定义一个user类型的操作列表:用户注册方法
* @param user 用户的数据对象,参数是根据底层dao来的
*/
void reg(User user);
/**
* 登录功能
* @param username
* @param password
* @return 返回一个User对象,因为我们登录之后,页面会有用户信息,比如右上角那种
* 所以我们在登录成功之后要把当前用户数据以用户对象的形式进行返回
* 状态管理:我们可以将数据保存在cookie或者session中,可以减少代码冗余
* 避免重复很高的数据进行频繁的数据操作(比如说登录之后买东西还对数据库进行查询看你地理位置对不对之类的)
* 一些常用的数据已经保存起来
*/
User login(String username,String password);
/**
* 修改密码
* @param uid
* @param username
* @param oldPassword
* @param newPassword
*/
void changePassword(Integer uid,
String username,
String oldPassword,
String newPassword);
/**
* 获取当前登录的用户信息
* @param uid
* @return 返回当前用户信息展示到页面表单中
* 声明一下,其实在我们登录之后,可以将用户信息放入到session中进行调用
* 也可以获取我们需要的信息展示出来,但是session过期了就另外一码事了
* 问题又来了,过期了不直接重新登录嘛
*/
User getByUid(Integer uid);
/**
* 修改用户资料
* @param uid 当前登录的用户id:uid和username都是可以从session中获取
* 在你登录的时候,uid和username就在session中
* @param username 当前登录的用户名
* @param user 修改用户对象:接收客户端个人资料能够提交的数据(用户phone,性别...)
*/
void changeInfo(Integer uid,String username,User user);
/**
* 修改用户头像
* @param uid 用户id
* @param avatar 用户头像路径
* @param username 用户名称
*/
void changeAvatar(Integer uid,String avatar,String username);
}
2.业务实现类——>需要根据具体需求定义异常出现的可能
实现业务接口,调用mapper中的方法(mapper与mybatis进行一个映射)
具体异常出现——>一般也就是增删改查中,比如insert时候,会发现用户已经存在了,所以要先利用查询方法然后进行判断,还有修改数据也是——>首先得判断是否有数据吧;等等...
package com.cy.computerstore.service.impl;
import com.cy.computerstore.entity.User;
import com.cy.computerstore.mapper.UserMapper;
import com.cy.computerstore.service.IUserService;
import com.cy.computerstore.service.ex.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.Date;
import java.util.UUID;
/**
* @author diao 2022/3/22
*/
/*用户模块业务层的实现类*/
@Service//将当前累的对象交给Spring来管理
public class UserServiceImpl implements IUserService {
//调用底层mapper实现user插入
@Autowired
private UserMapper userMapper;
/**
* 注册功能
* @param user 用户的数据对象,参数是根据底层dao来的
*/
@Override
public void reg(User user) {
//1、先通过传过来的user参数获取username
String username = user.getUsername();
//2、通过底层接口方法findByUsername判断用户是否被注册过
User result = userMapper.findByUsername(username);
//3.判断是否为null
if(result!=null){
//说明注册过了,抛出用户名占用异常
throw new UsernameDuplicatedException("用户名被占用");
}
String oldPassword = user.getPassword();
//将密码和盐值作为一个整体作为一个整体进行加密
String salt = UUID.randomUUID().toString().toUpperCase();
//补全数据:盐值的记录( 因为每次调用这个方法,salt是不一样的),
// 方便登录,因为你登录的话密码验证只能验证加密后的密码,而盐值又是随机的,所以得保存
user.setSalt(salt);
//将获取的老密码以及盐值封装到MD5加密方法中,并且将新密码设置到用户user中
String md5Password = getMD5Password(oldPassword, salt);
user.setPassword(md5Password);
//补全的数据:is_delete 设置为0,不删除,说明不会对用户进行拦截
user.setIsDelete(0);
// 补全数据:4个日志字段
user.setCreatedUser(user.getUsername());
user.setModifiedUser(user.getUsername());
Date date = new Date();
user.setCreatedTime(date);
user.setModifiedTime(date);
//4、为null,说明没有被注册过,我们将用户插入
Integer rows = userMapper.insert(user);
if(rows!=1){
throw new InsertException("插入用户信息发生异常");
}
}
/**
*
* @param username:先验证用户名,如果不存在就抛出异常
* @param password:验证密码:1.先从数据库中取得老密码,和盐值
* 2.盐值和输入的密码进行MD5加密,最后再与数据库中的老密码进行比对
* @return
*/
@Override
public User login(String username, String password) {
//1.根据用户名称查询用户数据是否存在,不存在抛出UserNotFoundException
User result = userMapper.findByUsername(username);
if(result==null){
throw new UserNotFoundException("用户名不存在");
}
//2.检测密码是否匹配:得到数据库中的老密码
String oldPassword = result.getPassword();
String salt = result.getSalt();
//将输入密码与用户盐值进行相同规则MD5加密
String newMD5Password = getMD5Password(password, salt);
//进行比对
if(!newMD5Password.equals(oldPassword)){
throw new PasswordNotMatchException("用户密码错误");
}
//3.判断is_delete字段是否为1标记已被删除
if(result.getIsDelete()==1){
throw new UserNotFoundException("用户数据不存在");
}
//4.调用mapper中的查询方法,这里我们进行数据压缩(user里面数据太多了,我们只要需要的几个)
User user = new User();
user.setUid(result.getUid());
user.setUsername(result.getUsername());
//返回有用户的头像,只要你一登录将avatar信息放入cookie中
user.setAvatar(result.getAvatar());
//6.返回已经封装好的User信息
return user;
}
/**
*
* @param uid:用户id
* @param username:用户名
* @param oldPassword:用户输入的老密码(原密码)
* @param newPassword:用户输入的新密码
*/
@Override
public void changePassword(Integer uid, String username, String oldPassword, String newPassword) {
User result = userMapper.findByUid(uid);
//判断是否有用户
if(result==null||result.getIsDelete()==1){
throw new UserNotFoundException("用户数据不存在");
}
//将用户输入的老密码与数据库中的盐值进行加密与数据库中的实际老密码进行比较
String md5Password = getMD5Password(oldPassword, result.getSalt());
if (!result.getPassword().equals(md5Password)){
throw new PasswordNotMatchException("密码错误");
}
//将新的密码设置到数据库中,将新的密码进行加密再去更新
String newMd5Password = getMD5Password(newPassword, result.getSalt());
Integer rows = userMapper.updatePasswordByUid(uid, newMd5Password, username, new Date());
if(rows!=1){
throw new UpdateException("更新数据时产生异常");
}
}
/**
* 通过uid获取用户信息
* @param uid
* @return 返回用户信息(个人资料所需要的用户信息)
*/
@Override
public User getByUid(Integer uid) {
User result = userMapper.findByUid(uid);
//对用户信息进行判断,可能出现异常
if(result==null){
throw new UserNotFoundException("用户数据不存在");
}
if(result.getIsDelete()==1){//可能出现用户被管理员删的情况
throw new UserNotFoundException("用户不存在");
}
//然后我们将个人资料的信息封装到新的User对象中,传输需要的数据通过ajax显示前端上
User user = new User();
user.setUsername(result.getUsername());
user.setPhone(result.getPhone());
user.setEmail(result.getEmail());
user.setGender(result.getGender());
return user;
}
/**
*
* @param uid 当前登录的用户id
* @param username 当前登录的用户名
* @param user 当前用户的新的个人资料数据+时间、修改用户以及uid
*/
@Override
public void changeInfo(Integer uid, String username, User user) {
//调用findByUid()方法,根据Uid查询具体的用户数据(个人资料数据)
User result = userMapper.findByUid(uid);
//对数据进行判断,因为你修改是按钮触碰才会发生事件,所以说可能数据已经显示出来了
//但是你修改确定之后发现原来的数据被管理员删除了,所以这里还需要判断
if(result==null){
throw new UserNotFoundException("用户数据不存在");
}
if(result.getIsDelete().equals(1)){
throw new UserNotFoundException("用户数据不存在");
}
//向参数user中补全数据:修改时间,修改的人以及uid
user.setUid(uid);
user.setModifiedUser(username);
user.setModifiedTime(new Date());
//用updateInfoByUid(User user)方法进行修改
Integer rows = userMapper.updateInfoByUid(user);
//可能修改数据时候出现异常
if(rows!=1){
throw new UpdateException("更新用户数据时出现未知错误,请联系系统管理员");
}
}
@Override
public void changeAvatar(Integer uid, String avatar, String username) {
//查询当前用户数据是否存在
User result = userMapper.findByUid(uid);
//进行异常判断
if(result==null||result.getIsDelete().equals(1)){
throw new UserNotFoundException("用户数据不存在");
}
Integer rows = userMapper.updateAvatarByUid(uid, avatar, username, new Date());
if(rows!=1){
throw new UpdateException("更新用户头像产生未知的异常");
}
}
/**
* 定义一个MD5算法加密
*/
private String getMD5Password(String password,String salt){
//进行加密(三次)
for(int i=0;i<3;i++){
password=DigestUtils.md5DigestAsHex((salt+password+salt).getBytes()).toUpperCase();
}
return password;
}
}
具体异常实现
控制层
1.首先先定义一个传递后端数据的类(传递JSON数据)
将状态码以及状态描述信息and数据封装到里面,方便传给前端
package com.cy.computerstore.util;
/**
* @author diao 2022/3/22
*/
import java.io.Serializable;
/**
* Json格式的数据进行响应
* 将状态码、状态描述信息、以及数据封装到一个类中
* 将这个类作为方法的返回值返回给前端浏览器
*/
public class JsonResult implements Serializable {
/*状态码*/
private Integer state;
/*描述信息*/
private String message;
/*数据(类型是不确定的)*/
private E data;
public JsonResult() {
}
public JsonResult(Integer state) {
this.state = state;
}
//构造一个状态码+数据的对象
public JsonResult(Integer stat,E data){
this.state=stat;
this.data=data;
}
//异常信息的捕获,将捕获的信息给到message
public JsonResult(Throwable e){
this.message=e.getMessage();
}
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;
}
}
2.控制器:
实现业务功能,并且将数据封装到JsonResult中(进行数据响应)
2.1为了简便,我们可以设置一个控制器的基类(专门处理异常的)——>根据具体情况来封装数据
package com.cy.computerstore.controller;
import com.cy.computerstore.controller.ex.*;
import com.cy.computerstore.entity.Address;
import com.cy.computerstore.service.ex.*;
import com.cy.computerstore.util.JsonResult;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.nio.file.AccessDeniedException;
/**
* @author diao 2022/3/23
*/
//这也是处理前端请求的,处理前端抛出的异常,并且将方法返回值传递给前端
@Controller
public class BaseController {
/*操作成功的状态码*/
public static final int OK=200;
/*用于统一处理该异常,方法充当请求处理的方法,返回给到前端*/
@ExceptionHandler({ServiceException.class,FileUploadException.class})
public JsonResult handleException(Throwable e){
JsonResult result = new JsonResult<>(e);
//根据不同异常的抛出,设置不同的响应码和message
if(e instanceof UsernameDuplicatedException){
result.setState(4000);
result.setMessage("用户名已经被占用");
}else if(e instanceof AddressCountLimitException){
result.setState(4001);
result.setMessage("用户收货地址超出上限的异常");
}else if(e instanceof AddressNotFoundException){
result.setState(4002);
result.setMessage("用户的收货地址不存在");
}else if(e instanceof AccessDeniedException){
result.setState(4004);
result.setMessage("非法访问收货地址");
}else if(e instanceof ProductNotFoundException){
result.setState(4005);
result.setMessage("查不到商品数据");
}else if(e instanceof CartNotFoundException){
result.setState(4006);
result.setMessage("购物车中数据不存在异常");
}else if(e instanceof InsertException){
result.setState(5000);
result.setMessage("插入数据时产生未知的异常");
// 关于登录的两个异常
}else if(e instanceof PasswordNotMatchException){
result.setState(5002);
result.setMessage("用户名的密码错误");
}else if(e instanceof UserNotFoundException){
result.setState(5001);
result.setMessage("用户数据不存在异常");
// 更新数据得到异常
}else if(e instanceof UpdateException){
result.setState(5001);
result.setMessage("更新数据时产生未知异常");
}else if(e instanceof DeleteException){
result.setState(5002);
result.setMessage("删除数据时产生未知的异常");
}else if(e instanceof FileEmptyException){
result.setState(6000);
}else if(e instanceof FileSizeException){
result.setState(6001);
}else if(e instanceof FileTypeException){
result.setState(6002);
}else if(e instanceof FileStateException){
result.setState(6003);
}else if(e instanceof FileUploadIOException){
result.setState(6004);
}
return result;
}
}
2.2控制器:调用业务类方法返回Json数据
这里提个问:为什么注入的是接口而不是实现类?:这是多态的体现,你注入实现类也是可以的,但是万一你有点多个实现类实现这个接口,岂不是都要注入,麻烦了——>其实就是根据对象引用的实际类来执行方法,实际类就是这里的实现类(10条消息) 多态_一大三千@大千世界的博客-CSDN博客_多态
package com.cy.computerstore.controller;
import com.cy.computerstore.controller.ex.*;
import com.cy.computerstore.entity.User;
import com.cy.computerstore.service.IUserService;
import com.cy.computerstore.service.ex.InsertException;
import com.cy.computerstore.service.ex.UsernameDuplicatedException;
import com.cy.computerstore.service.impl.UserServiceImpl;
import com.cy.computerstore.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author diao 2022/3/22
*/
//这里是返回json数据(被JsonResult类型的)
@RestController
@RequestMapping("users")
public class UserController extends BaseController{
@Autowired
private IUserService userService;
/**1、接收数据方式:请求处理方法的参数列表设置为pojo类型来接收前端数据
* SpringBoot会将前端的URL地址上的参数名和pojo类的属性名进行比较,如果相同
* 就会将值注入到pojo类的对应属性上
* @param user
* @return
*/
@RequestMapping("reg")
public JsonResult reg(@RequestBody User user){
userService.reg(user);
return new JsonResult(OK);
}
/**2、登录功能,每次登录会将数据放入session
* 接收数据形式:如果参数是非pojo类型
* 那么SpringBoot会将请求的参数名和方法的参数名直接进行比较,
* 如果相同则自动注入
* @param username
* @param password
* @return
*/
@RequestMapping("login")
public JsonResult login(String username,String password,HttpSession session){
//1.业务login方法,将封装的user信息放到JsonResult中;
User data = userService.login(username, password);
//3.向session对象中完成数据绑定(这里session是全局的)
session.setAttribute("uid",data.getUid());
session.setAttribute("username",data.getUsername());
//4.打印获取session中的数据
System.out.println(getuidFromSession(session));
System.out.println(getUsernameFromSession(session));
//2.将状态码数据封装到JsonResult中
return new JsonResult(OK,data);
}
/**@Session 获取session对象
* 相当于是一个工具类,获取session中的uid,有利于减少代码冗余
* @return
*/
public final Integer getuidFromSession(HttpSession session){
return Integer.valueOf(session.getAttribute("uid").toString());
}
public final String getUsernameFromSession(HttpSession session){
return session.getAttribute("username").toString();
}
/**
*
* @param oldPassword
* @param newPassword
* @param session
* @return
*/
@RequestMapping("change_password")
public JsonResult changePassword(String oldPassword,String newPassword,
HttpSession session){
//uid和username我们直接从session里面获取就好了
Integer uid = getuidFromSession(session);
String username = getUsernameFromSession(session);
//执行业务操作
userService.changePassword(uid,username,oldPassword,newPassword);
return new JsonResult<>(OK);
}
@RequestMapping("get_by_uid")
public JsonResult getByUid(HttpSession session){
User result = userService.getByUid(getuidFromSession(session));
return new JsonResult(OK,result);
}
/**
* 这个user对象由前端传递
* @param user
* @param session
* @return
*/
@RequestMapping("change_info")
public JsonResult changeInfo(User user, HttpSession session){
//要完成修改资料需要的数据:username、phone、email、gender
//uid需要再次封装到user对象中
Integer uid = getuidFromSession(session);
String username = getUsernameFromSession(session);
userService.changeInfo(uid,username,user);
return new JsonResult<>(OK);
}
/*设置上传文件最大值*/
public static final int AVATAR_MAX_SIZE=10*1024*1024;
/*限制上传文件的类型*/
public static final List AVATAR_TYPE=new ArrayList<>();
static{
//将文件类型放入集合中
AVATAR_TYPE.add("image/jpeg");
AVATAR_TYPE.add("image/png");
AVATAR_TYPE.add("image/bmp");
AVATAR_TYPE.add("image/gif");
}
/**
* MultipartFile接口是SpringMvc提供的一个接口,这个接口包装了文件数据(任何类型的file都可以接收)
* @param session:里面取出username,uid
* @param file:MultipartFile类型,名字必须是file,前端页面有个name叫file的会自动寻找,
* 将数据包给到控制层参数名字叫file的
* @RequestParam("xxx"):与前端表单的数据进行绑定
* @return
*/
@RequestMapping("change_avatar")
public JsonResult changeAvatar(HttpSession session,
@RequestParam("file") MultipartFile file){
//1.判断文件是否为null
if(file.isEmpty()){
throw new FileEmptyException("文件为空");
}
if(file.getSize()>AVATAR_MAX_SIZE){
throw new FileSizeException("文件超出限制,不得超过"+(AVATAR_MAX_SIZE/1024)+"KB的头像文件");
}
//2.判断上传的文件类型是否在AVATAR_TYPE里面
String contentType = file.getContentType();
System.out.println(contentType);
if(!AVATAR_TYPE.contains(contentType)){
throw new FileTypeException("不支持使用该类型文件作为头像,运行文件类型:\n"+AVATAR_TYPE);
}
//3.获取当前项目的绝对磁盘路径(就是项目路径)
String parent = session.getServletContext().getRealPath("upload");
System.out.println(parent);
//4.dir表示保存头像文件的文件夹
File dir = new File(parent);
if(!dir.exists()){
//如果不存在的话,就会创建一个
dir.mkdirs();
}
//保存头像文件的文件名
String originalFilename = file.getOriginalFilename();
//得到文件后缀名称:先取得.的索引,然后再利用substring
int beginIndex = originalFilename.lastIndexOf(".");
String suffix = originalFilename.substring(beginIndex);
//5.得到全新的文件名
String filename = UUID.randomUUID().toString() + suffix;
//6.在指定的目录下创建全新的文件,dest表示保存头像文件
File dest = new File(dir, filename);
//7.执行保存的头像文件,将之前的文件中的数据放到新的文件中
try {
file.transferTo(dest);
} catch (IOException e) {
throw new FileUploadIOException("上传文件时出现读写错误");
} catch (FileStateException e){
throw new FileStateException("文件状态异常");
}
//8.获取uid以及username和头像路径方便执行业务层的操作
Integer uid = getuidFromSession(session);
String username = getUsernameFromSession(session);
//头像相对路径,作为文件夹upload需要//
String avatar="/upload/"+filename;
userService.changeAvatar(uid,avatar,username);
//9.返回用户头像路径给到前端,用于头像展示
return new JsonResult<>(OK,avatar);
}
/**
* //需要调用业务层的接口
* @Autowired
* private IUserService userService;
*
* @RequestMapping("reg")
* public JsonResult reg(User user){
* //1、创建响应结果集
* JsonResult result = new JsonResult<>();
*
* //2.进行业务操作,若出现异常设置响应状态码和信息
* try {
* userService.reg(user);
* result.setState(200);
* result.setMessage("用户注册成功");
* } catch (UsernameDuplicatedException e) {
* result.setState(4000);
* result.setMessage("用户名被占用");
* }catch (InsertException e){
* result.setState(5000);
* result.setMessage("注册时产生未知异常导致注册失败");
* }
*
* //3.返回json格式数据
* return result;
* }
*/
}
2.2控制器下的异常
控制器其实就是与前端进行对接的,拿到前端的给的数据完成传来的请求;可以理解为往下递归
前端页面
这里只举例:
大体上:当请求正常,前端将data给到指定处理请求的controller,并且将服务器响应内容给到到前端的 success(响应的内容以参数的形式给到success里function方法参数上)
注册:
当页面起不来的时候,可以尝试刷新idea缓存重新启动,因为idea对js兼容性不是很好;
登录:
上传头像的前端ajax:
修改密码:
拦截器
其实拦截器也是用的aop思想,进行切面处理,prehandle方法在DispatcherServelet之前处理
1.先通过实现HandlerInterceptor自定义一个拦截器
根据session中是否有uid进行判断——>false就是拦截
package com.cy.computerstore.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author diao 2022/3/24
*/
/*自定义一个拦截器*/
//加载当前的拦截器并且将其注册到容器中
public class LoginInterceptor implements HandlerInterceptor {
/**
* 检测session对象中是否含有uid数据,如果没有就是未完成登录,重定向到登录页面
* @param request 请求对象
* @param response 响应对象
* @param handler 处理器(url+controller:作映射的)
* @return 返回true表示放行当前请求,否则拦截请求
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object obj = request.getSession().getAttribute("uid");
if(obj==null) {
//session中无uid说明未登录,重定向到登录页面
response.sendRedirect("/web/login.html");
return false;
}
//放行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2.通过实现WebMvcConfigurer接口重写addInterceptors()方法——>将自定义的拦截器进行注册
并且指定白名单,将需要放行(静态资源、登录页面、首页)放入集合;
package com.cy.computerstore.config;
import com.cy.computerstore.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 diao 2022/3/25
*/
//实现webMvcConfigurer接口,完成自定义一些功能配置
@Configuration//这个需要注册到容器中需要Configuration注解
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//1.创建自定义的拦截器对象
HandlerInterceptor interceptor = new LoginInterceptor();
//2.配置白名单,将其放入list集合中
List patterns = new ArrayList<>();
patterns.add("/bootstrap3/**");
patterns.add("/css/**");
patterns.add("/images/**");
patterns.add("/js/**");
patterns.add("/web/register.html");
patterns.add("/web/login.html");
patterns.add("/web/index.html");
patterns.add("/web/product.html");
patterns.add("/users/reg");
patterns.add("/users/login");
//将省市区列表信息放入白名单中
patterns.add("/districts/**");
//将商品类的信息放入白名单中
patterns.add("/products/**");
//3.完成拦截器的注册:addPathPatterns代表拦截路径,excludePathPatterns代表白名单
registry.addInterceptor(interceptor)
.addPathPatterns("/**")
.excludePathPatterns(patterns);
}
}
单元测试
mapper接口测试
package com.cy.computerstore.mapper;
import com.cy.computerstore.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
/**
* @author diao 2022/3/21
*/
//@SpringBootTest表示这个类是一个测试类(特点:不会随同项目打包操作)
@SpringBootTest
public class UserMapperTests {
//idea有自动检测功能,接口被mybatis动态代理实现放入容器了;
@Autowired
private UserMapper userMapper;
/**单元测试方法可以独立运行,提高代码运行效率
* 1.必须被@Test修饰
* 2.返回值必须是void
* 3.方法的参数列表不指定任何类型
* 4.访问修饰符必须是public
*/
@Test
public void insert(){
User user = new User();
user.setUsername("Fairy");
user.setPassword("2002514wyh11");
Integer rows = userMapper.insert(user);
System.out.println(rows);
}
@Test
public void findByUsername(){
User user = userMapper.findByUsername("Fairy");
// 打印user信息
System.out.println(user);
}
@Test
public void updatePasswordByUid(){
userMapper.updatePasswordByUid(7,"123",
"管理员",new Date());
}
@Test
public void findByUid(){
System.out.println(userMapper.findByUid(7));
}
@Test
public void updateInfoByUid(){
//1.将uid,phone,email,gender,modifiedUser,modifiedTime封装到user中
User user = new User();
user.setUid(7);
user.setPhone("18175143063");
user.setEmail("[email protected]");
user.setGender(1);
user.setModifiedUser("系统管理员");
user.setModifiedTime(new Date());
//2.调用userMapper底层方法进行修改用户信息
Integer rows = userMapper.updateInfoByUid(user);
System.out.println("rows="+rows);
}
@Test
public void updateAvatarByUid(){
userMapper.updateAvatarByUid(
8,
"/upload/avatar.png",
"管理员",
new Date()
);
}
}
业务类测试
package com.cy.computerstore.service;
import com.cy.computerstore.entity.User;
import com.cy.computerstore.service.ex.ServiceException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author diao 2022/3/22
*/
@SpringBootTest
public class UserServiceTests {
@Autowired
private IUserService userService;
@Test
public void reg(){
try {
User user = new User();
user.setUsername("test026");
user.setPassword("2002514wyh11");
userService.reg(user);
System.out.println("ok");
} catch (ServiceException e) {//因为业务层可能会抛出异常
//获取类的对象再获取类的名称
System.out.println(e.getClass().getSimpleName());
//获取异常类的信息
System.out.println(e.getMessage());
}
}
@Test
public void login(){
try {
User user = userService.login("test01", "123");
System.out.println(user);
} catch (ServiceException e) {
//获取异常信息
System.out.println(e.getClass().getSimpleName());
System.out.println(e.getMessage());
}
}
@Test
public void changePassword(){
userService.changePassword(8,"test02","2002514wyh11","123");
}
//通过uid获取个人资料用户数据
@Test
public void getByUid(){
User user = userService.getByUid(8);
System.out.println(user);
}
@Test
public void changeInfo(){
//1.创建一个User对象,里面封装个人资料数据
User user = new User();
user.setPhone("18175143063");
user.setEmail("[email protected]");
user.setGender(0);
userService.changeInfo(8,"test02",user);
}
@Test
public void changeAvatar(){
userService.changeAvatar(8,"/upload/test.png","小明");
}
}