章节介绍
本章主要介绍了,entity实体类和请求model类的创建,撸一个用户登录注册功能,以及创建通用的统一结果返回类和自定义异常。
项目介绍
记录SpringBoot从零到实现一整个商城后端的过程。
项目组成及技术栈
- 接口 SpingBoot + JPA + mySql
- 后台 vue + vue-element-admin
- 移动端 uniapp + colorui + vuex + scss
登录注册展示
- 手机号注册
- 手机号登录
- 根据token获取用户信息
entity实体类创建
创建entity目录,新建User类, 里面用到的lombox工具和审计功能在上一章节已介绍
package com.smxy.mall.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.util.Date;
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "fat_user")
@Data
public class User {
@Id //主键Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
@Column(name = "createTime",updatable = false,nullable=false)
private Date createTime; //创建时间
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date updateTime; //更新时间
@CreatedBy
@Column(name = "createUser",updatable = false,nullable=false)
private String createUser; //创建者
@LastModifiedBy
private String updateUser; //更新者
private String userName; //账号
private String userPsw; //密码
private String phone; //手机号
private String openId; //微信标识
private String type; //用户类型
private String name; //昵称姓名
private String img; //头像
private String sex; //性别
private int defaultId; //默认地址
}
请求model类
model里面加入参数校验
package com.smxy.mall.model.request.user;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@ApiModel
@Data
public class PhoneReq {
@NotNull(message = "~手机号不能为空")
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1\\d{10}$",message = "手机号格式错误")
private String phone;
@NotNull(message = "~密码不能为空")
@NotBlank(message = "密码不能为空")
private String userPsw;
}
validation 使用
- pom依赖
<!-- validator -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
- 常用注解
手机号登录注册
因为是学习,这里就简单的用手机号和密码进行注册登录
- service 定义方法
package com.smxy.mall.service;
import com.smxy.mall.entity.User;
public interface UserService {
User saveFromPhone(String phone, String psw); //手机号密码注册
User findByPhone(String phone); //判断手机号是否存在
User findByPhoneAndUserPsw(String phone, String userPsw); //验证手机号密码
}
- dao
package com.smxy.mall.dao;
import com.smxy.mall.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface UserDao extends JpaRepository<User, Object>, JpaSpecificationExecutor<User> {
User findByPhoneAndUserPsw(String phone, String userPsw); //手机号密码登录
User findByPhone(String phone); //手机号是否存在
}
- service 实现类
package com.smxy.mall.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.smxy.mall.common.CustomException;
import com.smxy.mall.common.Response;
import com.smxy.mall.dao.UserDao;
import com.smxy.mall.entity.User;
import com.smxy.mall.model.Current;
import com.smxy.mall.service.UserService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
/**
* 手机号密码注册
* @param phone
* @param psw
* @return
*/
@Override
public User saveFromPhone(String phone, String psw) {
User obj = userDao.findByPhone(phone); //根据手机号查询用户
if(obj!=null){
throw new CustomException(Response.fail("手机号"+phone+"已存在")); //自定义异常
}
String md5Psw = DigestUtils.md5DigestAsHex(psw.getBytes());
User user = new User();
user.setPhone(phone);
user.setUserPsw(md5Psw);
user.setType("app");
return userDao.save(user);
}
/**
* 手机号密码登录
* @param phone
* @param userPsw
* @return
*/
@Override
public User findByPhoneAndUserPsw(String phone, String userPsw) {
String md5Psw = DigestUtils.md5DigestAsHex(userPsw.getBytes());
return userDao.findByPhoneAndUserPsw(phone,md5Psw);
}
@Override
public User findByPhone(String phone) {
return userDao.findByPhone(phone);
}
}
- controller 加上@Valid注解, 请求model中的校验才会生效 @CurrentUser注解 解析token,用户信息可从Current类中获取
package com.smxy.mall.controller;
import com.smxy.mall.annotation.CurrentUser;
import com.smxy.mall.annotation.JwtIgnore;
import com.smxy.mall.common.Response;
import com.smxy.mall.model.Audience;
import com.smxy.mall.model.Current;
import com.smxy.mall.model.request.user.AccountReq;
import com.smxy.mall.model.request.user.PageListReq;
import com.smxy.mall.model.request.user.PhoneReq;
import com.smxy.mall.utils.JwtTokenUtil;
import com.smxy.mall.entity.User;
import com.smxy.mall.service.UserService;
import com.smxy.mall.utils.Token;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@Api(tags = "用户模块")
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@Autowired
private Audience audience;
@ApiOperation("手机号注册")
@JwtIgnore //忽然jwt拦截器
@PostMapping("/register/phone")
public Object phoneReg(@Valid PhoneReq phoneReq){
return Response.success(userService.saveFromPhone(phoneReq.getPhone(),phoneReq.getUserPsw()));
}
@ApiOperation("手机号登录")
@JwtIgnore
@PostMapping("/login/phone")
public Object phoneLogin(@Valid PhoneReq phoneReq) {
User obj = userService.findByPhoneAndUserPsw(phoneReq.getPhone(),phoneReq.getUserPsw());
if(StringUtils.isEmpty(obj)){
return Response.fail("手机号或密码错误");
}else{
String token = JwtTokenUtil.createJWT(obj,audience); //创建token
Map<String,Object> map = new HashMap();
map.put("token",token);
map.put("userInfo",obj);
//返回token和用户信息
return Response.success(map);
}
}
@ApiOperation("用户个人信息")
@GetMapping("/userInfo")
public Object userInfo(@CurrentUser Current current) {
return Response.success(userService.findById(current.getUserId()));
}
}
Response 统一结果返回类
package com.smxy.mall.common;
import lombok.Data;
@Data
public class Response<T> {
private static final String successCode = "0";
private static final String errorCode = "1";
private static final String successMsg = "请求正常";
private static final String errorMsg = "请求失败";
private String code;
private String msg;
private T data;
public Response() {}
public Response(String code, String msg){
this.code = code;
this.msg = msg;
}
public Response(String code,T data,String msg) {
this.code = code;
this.data = data;
this.msg = msg;
}
/**
* 有返回数据
* 返回固定msg code
* @param data
* @return
*/
public static Response success(Object data) {
return new Response(successCode,data,successMsg);
}
/**
* 返回固定code 自定义msg
* @param info
* @return
*/
public static Response fail(String info) {
return new Response(errorCode,info);
}
/**
* 有返回数据
* 返回固定code 自定义msg
* @param data
* @param info
* @return
*/
public static Response success(Object data, String info) {
return new Response(successCode,data,info);
}
/**
* 无返回数据
* 返回固定msg code
* @return
*/
public static Response success() {
return new Response(successCode,successMsg);
}
/**
* 无返回数据
* 返回固定code msg自定义
* @param info
* @return
*/
public static Response success(String info) {
return new Response(successCode,info);
}
/**
* 返回固定code msg
* @return
*/
public static Response fail() {
return new Response(errorCode,errorMsg);
}
/**
* 返回自定义code msg
* @param code
* @param info
* @return
*/
public static Response fail(String code, String info) {
return new Response(code,info);
}
@Override
public String toString() {
return "Response{" +
"code=" + code +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
自定义异常
- 在common目录新建CustomException类
package com.smxy.mall.common;
import lombok.Getter;
@Getter
public class CustomException extends RuntimeException {
private String code;
private String message;
public CustomException(Response response) {
this.code = response.getCode();
this.message = response.getMsg();
}
}
- 在common目录新建全局异常类 GlobalException
package com.smxy.mall.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义异常
*/
@ExceptionHandler(CustomException.class)
public Object handleException(CustomException e) {
// 打印异常信息
log.error("### 异常信息:{} ###", e.getMessage());
return Response.fail(e.getCode(),e.getMessage());
}
/**
* 参数错误异常
*/
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
public Object handleException(Exception e) {
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException validException = (MethodArgumentNotValidException) e;
BindingResult result = validException.getBindingResult();
StringBuffer errorMsg = new StringBuffer();
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
errors.forEach(p ->{
FieldError fieldError = (FieldError) p;
errorMsg.append(fieldError.getDefaultMessage()).append(",");
log.error("### 1请求参数错误:{"+fieldError.getObjectName()+"},field{"+fieldError.getField()+ "},errorMessage{"+fieldError.getDefaultMessage()+"}"); });
}
return Response.fail("参数无效"+e.getMessage());
} else if (e instanceof BindException) {
BindException bindException = (BindException)e;
if (bindException.hasErrors()) {
log.error("### 2请求参数错误: {}", bindException.getAllErrors());
}
return Response.fail(bindException.getBindingResult().getFieldError().getDefaultMessage());
}else{
return Response.fail("参数无效");
}
}
// @ExceptionHandler(NullPointerException.class)
// public Object handleException(NullPointerException e){
// System.out.println(e);
// log.error("### 空指针异常:{} ###", e.getMessage());
// return Response.fail("空指针异常"+e.getMessage());
// }
/**
* 处理所有不可知的异常
*/
@ExceptionHandler(Exception.class)
public Object handleOtherException(Exception e){
//打印异常堆栈信息
e.printStackTrace();
// 打印异常信息
log.error("### 不可知的异常:{} ###", e.getMessage());
return Response.fail("系统繁忙,请稍后重试"+e.getMessage());
}
}
在service层中,直接throw new CustomException(Response.fail("错误信息")),就可以将统一格式的错误信息返回给前端
写在最后
有写的不对的地方,欢迎大家指出来,我好及时改正。一起成长~