我们先来给一下整个业务流程

注册业务具体的包

controller层

@RestController
@RequestMapping("/user")
public class UserController {
    private static final Logger logger = LoggerFactory.getLogger(RegisterRequestDTO.class);//日志
    @Resource
    private UserService userService;

    @PostMapping("/register")
    public Response<Void> register(RegisterRequestDTO requestDTO){
     RegisterRequestDTO.checkDTO(requestDTO);


     userService.regisiter(requestDTO);
        return Response.ok();
    }

用户前端发来的信息JSON文件,在被传到controller层,经过反序列化之后变成RegisterRequestDTO这个类所创建的对象(requestdto),在类里调用checkDTO方法去检查requestDTO

这是RegsiterRequestDTO类,下面的方法包括(checkDTO是JSON文件序列化后传来的requestdto里的email,password,checkPassword,username,调用isEmpty看看是否为空,还有输入的email格式通过模式串 match类看看是否正确 (正则表达式),并且检测输入的userNumber身份证号长度是否为18)如果检测到的不正常就会抛出异常

@Data
@AllArgsConstructor
public class RegisterRequestDTO {
    private static final Logger logger = LoggerFactory.getLogger(RegisterRequestDTO.class);
    private String email;
    private String password;
    private String checkpassword;
    private String userName;
    private String userNumber;
    private Long createAt;
    private Long createBy;
    public static void checkDTO(@RequestBody  RegisterRequestDTO dto){
        if (StringTools.isEmpty(dto.getEmail())
            ||StringTools.isEmpty(dto.getPassword())
            ||StringTools.isEmpty(dto.getCheckpassword())
            ||StringTools.isEmpty(dto.getUserName())){
            logger.info("{} 错误:{}",CodeEnum.REGISTER_FAILED_03.getCode(),CodeEnum.REGISTER_FAILED_03.getMsg());
            throw new BusinessException(CodeEnum.REGISTER_FAILED_03);
        }
        String pattern="^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$";
        if (!ReUtil.isMatch(pattern, dto.getEmail())) {
            logger.info("{} 错误:{}",CodeEnum.REGISTER_FAILED_04.getCode(),CodeEnum.REGISTER_FAILED_04.getMsg());
            throw new BusinessException(CodeEnum.REGISTER_FAILED_04);
        }
        if(StringTools.isEmpty(dto.getUserNumber())){
            if (dto.getUserNumber().length()  !=18){
                throw  new BusinessException(CodeEnum.REGSITER_FAILED_05);
            }
        }

        if (!dto.getPassword().equals(dto.getCheckpassword())) {
            logger.info("{} 错误:{}",CodeEnum.REGISTER_FAILED_02.getCode(),CodeEnum.REGISTER_FAILED_02.getMsg());
            throw new BusinessException(CodeEnum.REGISTER_FAILED_02);
        }
        Date date=new Date();
        dto.setCreateAt(date.getTime());
    }


}


关于异常

这里是自己创建的异常类,为什么要自己创建一个异常类呢,如果不创建用默认的RuntimeException,因为RuntimeException只能传codeEnum.getmsg(),会使前端的code与msg不一致,而自己定义的BusinessException,可以直接传codeEnum这个引用,msg与code同时上传,不会导致msg与code对不上

注:子类想访问父类就得用super()

@Getter
public class BusinessException extends RuntimeException{
    private final CodeEnum codeEnum;
    public BusinessException(CodeEnum codeEnum){
        super(codeEnum.getMsg());
        this.codeEnum=codeEnum;
    }
}

我详细讲一下CodeEnum

@Getter
public enum CodeEnum {
    SUCCESS_CODE(200,"成功"),
    DEFAULT_CODE(900,"不许注册"),
    REGSITER_FAILED_00(800,"注册错误"),
    REGISTER_FAILED_01(901,"邮箱已经注册"),
    REGISTER_FAILED_02(902,"两次密码不一致"),
    REGISTER_FAILED_03(903,"格式不正确"),
    REGISTER_FAILED_04(904,"请输入正确邮箱"),
    REGSITER_FAILED_05(905,"身份证信息错误");

    private final Integer code;
    private final String msg;

    CodeEnum(Integer code,String msg){
    this.code=code;
    this.msg=msg;
    }
}

因为这是一个枚举类,枚举类不允许有setter方法,不是只需要getter方法。因为所有属性是final的,构造器也是默认修饰符,不允许在其他地方进行构建。因为你这一列粉色的都是已经构造好的对象了,外面的类可以直接使用这些对象,也就是定义好的code和msg枚举了所有的失败可能方法,与成功的情况

然后你抛出的这个异常(在RegsiterRequestdto这个类里抛出的)会被GlobalExceptionHandler这个类给捕获

@RestControllerAdvice
public class GlobalExceptionHandler {
    private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler(BusinessException.class)
   {
        return Response.error(ex.getCodeEnum());
    }
}

这个注解指定了我们要拦截异常的类型,只拦截BusinessException这个类,单独.class是表示这个类的意思

 @ExceptionHandler(BusinessException.class)
 public Response<Void>handleException(BusinessException ex,WebRequest request)

BusinessException ex,是被捕获的异常,也就是在RegisterRequestDTO中抛出的异常(由BusinessException创建的,也就是ex即是CodeEnum.REGISTER_FAILED_0X),然后返回Response.error调用.getMessage最后得到这个信息

以下是Response的对象以及可调用的方法(.error .ok)

通用返回类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Response<T> {
    private Integer code;
    public String msg;
    private String data;
    public static <T> Response<T> ok() {
        return new Response<>(CodeEnum.SUCCESS_CODE.getCode(), CodeEnum.SUCCESS_CODE.getMsg(),null );
    }
    public static <T> Response<T> ok(T data){
        String dataJson = JSONUtil.toJsonStr(data);
        return new Response<>(CodeEnum.SUCCESS_CODE.getCode(), CodeEnum.SUCCESS_CODE.getMsg(), dataJson);
    }
    public static <T> Response<T> ok(T data,String msg){
        String dataJson = JSONUtil.toJsonStr(data);
        return new Response<>(CodeEnum.SUCCESS_CODE.getCode(), msg, dataJson);
    }
    public static <T> Response<T> ok(String msg){
        return new Response<>(CodeEnum.SUCCESS_CODE.getCode(), msg,null);
    }
    public static <T> Response<T> error(){
        return new Response<>(CodeEnum.REGISTER_FAILED_01.getCode(), CodeEnum.DEFAULT_CODE.getMsg(), null);
    }
    public static <T> Response<T> error(CodeEnum codeEnum,String msg){
        return new Response<>(codeEnum.getCode(),msg,null);
    }
    public static <T> Response<T> error(CodeEnum codeEnum){
        return new Response<>(codeEnum.getCode(),codeEnum.getMsg(),null);
    }
    public static <T> Response<T> error(String msg){
        return new Response<>(CodeEnum.DEFAULT_CODE.getCode(),msg,null);
    }
}
以上只是说了当你输入了空信息,两次密码不一致,身份证号长度不等于18,邮箱格式不正确的时候,在controller层会发生的流程,即当出错时,会走完controller层之后给用户界面返回msg,统一返回给前端的数据格式

dao层

service 层注入 dao 层的类,也就是 RedisComment 和 MybatisPlus 等操作数据库的类,为了实现具体的业务需求

Redis其实也是一个数据库

也就是如果在RegsiterExceptionDTO中没有被拦截下,通过了四个if的检测,用户端发来的(DTO)那就要,经过RedisComment类,把DTO存入Redis缓存中

这个就是RedisComment类的内容

@Component("redisComent")
public class RedisComment {

  @Resource
  private RedisUtils redisUtils;
//判断邮箱是否存在于redis
  public  boolean emailIsRegister(RegisterRequestDTO requestDTO){
    String o =(String) redisUtils.get(RedisComponent.REDIS_CACHE_REGISTER_EMAIL + requestDTO.getEmail());
    return !StringTools.isEmpty(o);
  }
  //将当前邮箱存入 redis
  public void emailSaveRedis(String email){
    redisUtils.set(RedisComponent.REDIS_CACHE_REGISTER_EMAIL + email,"yes");
  }
}

你可以看到,它主要是判断缓存中是否有相同的邮箱,redisUtils调用get方法,得到RedisComponent.REDIS_CACHE_REGISTER_EMAIL + requestDTO.getEmail()的key,存在一个o的字符串里,然后调用StringTools的isEmpty方法判断缓存中是否有这个邮箱

如果没有就用redisUtil的set方法把他存入缓存

以下是StringTools的isEmpty的方法

    public static boolean isEmpty(String str) {
        if (null == str || "".equals(str) || "null".equals(str) || "\u0000".equals(str)) {
            return true;
        } else if ("".equals(str.trim())) {
            return true;
        }
        return false;
    }

返回的是ture or false 用来判断是否存在相同的邮箱

RedisComponent类

一个简单的字符串用来表明缓存中的邮箱,起识别作用

 public static final String REDIS_CACHE_REGISTER_EMAIL="axaxedx:redis:register:email";

Service层

主要就是UserServiceimpl这个类用来实现Service层的业务,通过调用redsicomment的方法看缓存中是否有相同的邮箱,有就抛异常,与上面介绍的方法相同 boolean isRegister = redisComment.emailIsRegister(requestDTO);

@Service("userService")
public class UserServiceimpl implements UserService {
    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private RedisComment redisComment;

    @Override
    public void regisiter(RegisterRequestDTO requestDTO) {
        boolean isRegister = redisComment.emailIsRegister(requestDTO);
        if(isRegister){
            throw new BusinessException(CodeEnum.REGISTER_FAILED_01);
//            throw new RuntimeException(CodeEnum.REGISTER_FAILED_01.getMsg());
        }
        UserInfo userInfo=UserInfo.DTO2po(requestDTO);

        if(StringTools.isEmpty(requestDTO.getUserNumber())){
            userInfo.setStatus(UserStatus.NEED_NUMBER.getStatus());
        }else {
            userInfo.setStatus(UserStatus.NORMAL.getStatus());
        }
        userInfoMapper.insert(userInfo);
        redisComment.emailSaveRedis(userInfo.getEmail());
    }


}

UserInfo userInfo=UserInfo.DTO2po(requestDTO);这句的意思是把dto通过userInfo类的方法dto2po把dto转为po这样就可以通过

 userInfoMapper.insert(userInfo);
 redisComment.emailSaveRedis(userInfo.getEmail());

这两句语句,第一句是插入缓存,如果第一句没问题也就是缓存中没有相同的邮箱,就会执行第二句,第二句是送入数据库

这里的意思是如果dto里的这个身份证号码是空的,就会返回UserStatus中的NEED_NUMBER,否则就返回正常

if(StringTools.isEmpty(requestDTO.getUserNumber())){
    userInfo.setStatus(UserStatus.NEED_NUMBER.getStatus());
}else {
    userInfo.setStatus(UserStatus.NORMAL.getStatus());
}