【项目】【抽奖系统】注册功能实现

目录

  • 一、用户注册
    • [1.1 参数要求](#1.1 参数要求)
    • [1.2 接口规范](#1.2 接口规范)
    • [1.3 实现Controller](#1.3 实现Controller)
      • [1.3.1 Validation](#1.3.1 Validation)
      • [1.3.2 Controller实现](#1.3.2 Controller实现)
      • [1.3.3 controller 的返回数据 UserRegisterResult](#1.3.3 controller 的返回数据 UserRegisterResult)
      • [1.3.4 controller 的参数](#1.3.4 controller 的参数)
      • [1.3.5 添加的错误码](#1.3.5 添加的错误码)
      • [1.3.6 controller 层 全局异常捕获](#1.3.6 controller 层 全局异常捕获)
      • [1.3.7 测试](#1.3.7 测试)
    • [1.4 实现 service层](#1.4 实现 service层)
      • [1.4.1 IUserService 接口](#1.4.1 IUserService 接口)
      • [1.4.2 service层的返回结果 UserRegisterDTO](#1.4.2 service层的返回结果 UserRegisterDTO)
      • [1.4.3 实现 service 接口](#1.4.3 实现 service 接口)
        • [1.4.3.1 校验参数 checkRegisterInfo 方法](#1.4.3.1 校验参数 checkRegisterInfo 方法)
        • [1.4.3.2 正则校验工具类 RegexUtil](#1.4.3.2 正则校验工具类 RegexUtil)
        • [1.4.3.3 身份枚举类 UserIdentityEnum](#1.4.3.3 身份枚举类 UserIdentityEnum)
        • [1.4.3.4 邮箱是否被使用 checkMailUsed 方法](#1.4.3.4 邮箱是否被使用 checkMailUsed 方法)
        • [1.4.3.5 手机号是否被使用 checkPhoneNumUsed 方法](#1.4.3.5 手机号是否被使用 checkPhoneNumUsed 方法)
      • [1.4.4 添加的错误码](#1.4.4 添加的错误码)
    • [1.5 实现 dao](#1.5 实现 dao)
      • [1.5.1 环境配置](#1.5.1 环境配置)
      • [1.5.2 UserMapperr 接口类](#1.5.2 UserMapperr 接口类)
      • [1.5.3 TypeHandler 自动运⾏处理功能](#1.5.3 TypeHandler 自动运⾏处理功能)
        • [1.5.3.1 Encrypt 类 代表 phoneNumber](#1.5.3.1 Encrypt 类 代表 phoneNumber)
        • [1.5.3.2 EncryptTypeHandler类:自己实现 TypeHandler](#1.5.3.2 EncryptTypeHandler类:自己实现 TypeHandler)
        • [1.5.3.3 修改配置,使用自定义TypeHandle](#1.5.3.3 修改配置,使用自定义TypeHandle)
      • [1.5.4 测试](#1.5.4 测试)
      • [1.5.5 do层数据(相当于model)](#1.5.5 do层数据(相当于model))
  • [1.6 前端](#1.6 前端)

一、用户注册

时序图:

1.1 参数要求

参数名 描述 类型 默认值 条件
name 用户名 String 必须
mail 用户邮箱地址 String 必须
phoneNumber 用户电话号码 String 必须
password 密码 String
identity 管理员身份 String 必须

1.2 接口规范

java 复制代码
[请求] /register POST
{
 "name":"张三",
 "mail":"451@qq.com",
 "phoneNumber":"13188888888",
 "password":"123456789",
 "identity":"ADMIN"
}
[响应] 
{
 "code": 200,
 "data": {
 "userId": 22
 },
 "msg": ""
}

1.3 实现Controller

1.3.1 Validation

对于 controller 接⼝⼊参字段的验证,可以使⽤ Spring Boot 中集成的 Validation 来完成。例如可以看到我们在接⼝⼊参上加⼊了 @Validated 注解,并且 param 对象中的每个成员都使⽤@NotBlank 注解来检查参数不能为空。使⽤需引⼊依赖:

xml 复制代码
<!-- spring-boot 2.3及以上的版本只需要引⼊下⾯的依赖 -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

1.3.2 Controller实现

com.yj.lottery_system.controller 包下实现

逻辑:

  • @Validated 表示对参数进行校验 @RequestBody 接收JSON数据
  • UserRegisterResult: Controller层的返回结果
  • UserRegisterParam : Controller层的参数
  • 调用service层服务
  • convertToUserRegisterResult方法实现对service返回数据到Controller返回数据的转化
java 复制代码
package com.yj.lottery_system.controller;

import com.yj.lottery_system.common.errorcode.ControllerErrorCodeConstants;
import com.yj.lottery_system.common.exception.ControllerException;
import com.yj.lottery_system.common.pojo.CommonResult;
import com.yj.lottery_system.common.utils.JacksonUtil;
import com.yj.lottery_system.controller.param.UserRegisterParam;
import com.yj.lottery_system.controller.result.UserRegisterResult;
import com.yj.lottery_system.service.IUserService;
import com.yj.lottery_system.service.dto.UserRegisterDTO;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class UserController {
    private static final Logger log = LoggerFactory.getLogger(UserController.class);
    @Resource
    private IUserService userService;
    /**
     * 注册
     */
    @RequestMapping("/register")
    public CommonResult<UserRegisterResult> userRegister(@Validated @RequestBody UserRegisterParam param) {
        //日志打印
        log.info("userRegister UserRegisterParam: {}", JacksonUtil.writeValueAsString(param));
        //调用service服务
        UserRegisterDTO userRegisterDTO = userService.register(param);
        return CommonResult.success(convertToUserRegisterResult(userRegisterDTO));

    }

    private UserRegisterResult convertToUserRegisterResult(UserRegisterDTO userRegisterDTO) {
        UserRegisterResult userRegisterResult = new UserRegisterResult();
        if(null == userRegisterDTO) {
            throw  new ControllerException((ControllerErrorCodeConstants.REGISTER_ERROR));
        }
        userRegisterResult.setUserId(userRegisterDTO.getUserId());
        return userRegisterResult;
    }
}

1.3.3 controller 的返回数据 UserRegisterResult

com.yj.lottery_system.controller.result 包下实现

根据接口规范,成功后的数据除码和信息外,只需要返回用户id。

java 复制代码
package com.yj.lottery_system.controller.result;

import lombok.Data;

import java.io.Serializable;
@Data
public class UserRegisterResult implements Serializable {
    private Long userId;
}

1.3.4 controller 的参数

com.yj.lottery_system.controller.param 包下实现:

根据接口规范,参数包含 用户名,用户邮箱地址,用户电话,密码,身份标识。出 密码只有管理员有,不是必传参数外,其余都是必传参数, @NotBlank 注解校验String参数,禁止 null、空串、纯空格。

java 复制代码
package com.yj.lottery_system.controller.param;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

import java.io.Serializable;

@Data
public class UserRegisterParam implements Serializable {
    @NotBlank(message = "用户名不能为空")
    private String name;//用户名

    @NotBlank(message = "邮箱不能为空")
    private String mail;//用户邮箱地址

    @NotBlank(message = "电话不能为空")
    private String phoneNumber;//用户电话

    private String password;//密码,管理员才有

    @NotBlank(message = "身份标识不能为空")
    private String identity;//身份标识

}

1.3.5 添加的错误码

com.yj.lottery_system.common.errorcode 包下ControllerErrorCodeConstants类中:

java 复制代码
ErrorCode REGISTER_ERROR = new ErrorCode(100,"注册失败");

1.3.6 controller 层 全局异常捕获

控制层通⽤异常处理 @RestControllerAdvice+@ExceptionHandler

com.yj.lottery_system.controller.handler 包下实现:

  • 捕获自定义的service层异常和controller层异常,还有没有捕获抛出的系统异常。
  • 都打印日志,和返回错误码与堆栈信息。
java 复制代码
package com.yj.lottery_system.controller.handler;

import com.yj.lottery_system.common.errorcode.GlobalErrorCodeConstants;
import com.yj.lottery_system.common.exception.ControllerException;
import com.yj.lottery_system.common.exception.ServiceException;
import com.yj.lottery_system.common.pojo.CommonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice //捕获全局抛的异常

public class GlobalExceptionHandler {
    private final static Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler(value = ServiceException.class)
    public CommonResult<?> serviceException(ServiceException e) {
        //错误日志
        LOGGER.error("ServiceException: ", e);
        //构造返回结果
        return CommonResult.error(GlobalErrorCodeConstants.INTERNAL_SERVICE_ERROR.getCode(), e.getMessage());
    }
    @ExceptionHandler(value = ControllerException.class)
    public CommonResult<?> controllerException(ControllerException e) {
        //错误日志
        LOGGER.error("ControllerException: ", e);
        //构造返回结果
        return CommonResult.error(GlobalErrorCodeConstants.INTERNAL_SERVICE_ERROR.getCode(), e.getMessage());
    }
    @ExceptionHandler(value = Exception.class)
    public CommonResult<?> exception(Exception e) {
        //错误日志
        LOGGER.error("ControllerException: ", e);
        //构造返回结果
        return CommonResult.error(GlobalErrorCodeConstants.INTERNAL_SERVICE_ERROR.getCode(), e.getMessage());
    }

}

1.3.7 测试

我的service是实现了,只不过写在了后面。

正确传参:

  • 传密码:
  • 不传密码:

错误传参:

将后端写完后的正确测试:

1.4 实现 service层

1.4.1 IUserService 接口

com.yj.lottery_system.service 包下创建注册接口方法:

  • UserRegisterParam :controller层传的参数。
  • UserRegisterDTO :service层的返回结果。
java 复制代码
package com.yj.lottery_system.service;


import com.yj.lottery_system.controller.param.UserRegisterParam;
import com.yj.lottery_system.service.dto.UserRegisterDTO;

public interface IUserService {
    /**
     * 注册
     */
    UserRegisterDTO register(UserRegisterParam param);
}

1.4.2 service层的返回结果 UserRegisterDTO

com.yj.lottery_system.service.dto 下实现:

  • controller层只需要service返回用户id。
java 复制代码
package com.yj.lottery_system.service.dto;

import lombok.Data;

@Data
public class UserRegisterDTO {
    //用户id
    private Long userId;
}

1.4.3 实现 service 接口

com.yj.lottery_system.service.impl 包下:

java 复制代码
package com.yj.lottery_system.service.impl;

import ch.qos.logback.core.util.StringUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.yj.lottery_system.common.errorcode.ServiceErrorCodeConstants;
import com.yj.lottery_system.common.exception.ServiceException;
import com.yj.lottery_system.common.utils.RegexUtil;
import com.yj.lottery_system.controller.param.UserRegisterParam;
import com.yj.lottery_system.dao.dataObject.Encrypt;
import com.yj.lottery_system.dao.dataObject.UserDO;
import com.yj.lottery_system.dao.mapper.UserMapper;
import com.yj.lottery_system.service.IUserService;
import com.yj.lottery_system.service.dto.UserRegisterDTO;
import com.yj.lottery_system.service.enums.UserIdentityEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {
        //校验参数
        checkRegisterInfo(param);
        //加密私密数据(构造dao层数据)
        UserDO userDO = new UserDO();
        userDO.setUserName(param.getName());
        userDO.setIdentity(param.getIdentity());
        userDO.setEmail(param.getMail());
        userDO.setPhoneNumber(new Encrypt(param.getPhoneNumber()));
        //有密码加密存储
        if(StringUtils.hasText(param.getPassword())) {
            userDO.setPassword(DigestUtil.sha256Hex(param.getPassword()));
        }
        //保存数据
        userMapper.insert(userDO);
        //构造返回
        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();
        userRegisterDTO.setUserId(userDO.getId());
        return userRegisterDTO;
    }

    private void checkRegisterInfo(UserRegisterParam param) {
        //非空校验
        if(null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }
        //校验邮箱格式 xxx@xxx.xxx 正则表达式
        if(!RegexUtil.checkMail(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }
        //校验手机号格式
        if(!RegexUtil.checkMobile(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
        //校验身份信息
        if(null == UserIdentityEnum.forName(param.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        //校验管理员密码必填
        if(param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                && !StringUtils.hasText(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_NULL);
        }
        //密码校验大于等于6位
        if(StringUtils.hasText(param.getPassword())
                && !RegexUtil.checkPassword(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }
        //校验邮箱是否被使用
        if(checkMailUsed(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }
        //校验手机号是否被使用
        if(checkPhoneNumUsed(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }
    //校验手机号是否被使用
    private boolean checkPhoneNumUsed(String phoneNumber) {
        int count = userMapper.countByPhone(new Encrypt(phoneNumber));
        return count > 0;
    }

    //校验邮箱是否被使用
    private boolean checkMailUsed(String mail) {
        //调用dao
        int count = userMapper.countByMail(mail);
        return count > 0;
    }
}
1.4.3.1 校验参数 checkRegisterInfo 方法

com.yj.lottery_system.service.impl 包下,UserServiceImpl 类中:

  • 非空校验
  • 调用正则校验工具类 RegexUtil 校验邮箱格式
  • 调用正则校验工具类 RegexUtil 校验手机号格式
  • 调用身份枚举类中的校验方法 UserIdentityEnum.forName 校验身份信息
  • 校验管理员密码必填
  • 调用正则校验工具类 RegexUtil 校验密码至少6位
  • 调用checkMailUsed 校验邮箱是否被使用
  • 调用 checkPhoneNumUsed 手机号是否被使用
java 复制代码
    private void checkRegisterInfo(UserRegisterParam param) {
        //非空校验
        if(null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }
        //校验邮箱格式 xxx@xxx.xxx 正则表达式
        if(!RegexUtil.checkMail(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }
        //校验手机号格式
        if(!RegexUtil.checkMobile(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
        //校验身份信息
        if(null == UserIdentityEnum.forName(param.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        //校验管理员密码必填
        if(param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                && !StringUtils.hasText(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_NULL);
        }
        //密码校验大于等于6位
        if(StringUtils.hasText(param.getPassword())
                && !RegexUtil.checkPassword(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }
       
        //校验邮箱是否被使用
        if(checkMailUsed(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }
        //校验手机号是否被使用
        if(checkPhoneNumUsed(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }
1.4.3.2 正则校验工具类 RegexUtil

com.yj.lottery_system.common.utils 包下:

java 复制代码
package com.yj.lottery_system.common.utils;

import org.springframework.util.StringUtils;

import java.util.regex.Pattern;

public class RegexUtil {
    /**
     * 邮箱:xxx@xx.xxx(形如:abc@qq.com)
     *
     * @param content 邮箱
     * @return
     */
    public static boolean checkMail(String content) {
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表⽰匹配字符串的开始。
         * [a-z0-9]+ 表⽰匹配⼀个或多个⼩写字⺟或数字。
         * ([._\\-]*[a-z0-9])* 表⽰匹配零次或多次下述模式:⼀个点、下划线、反斜杠或短
         横线,后⾯跟着⼀个或多个⼩写字⺟或数字。这部分是可选的,并且可以重复出现。
         * @ 字符字⾯量,表⽰电⼦邮件地址中必须包含的"@"符号。
         * ([a-z0-9]+[-a-z0-9]*[a-z0-9]+.) 表⽰匹配⼀个或多个⼩写字⺟或数字,后⾯可以
         跟着零个或多个短横线或⼩写字⺟和数字,然后是⼀个⼩写字⺟或数字,最后是⼀个点。这是匹配域名
         的⼀部分。
         * {1,63} 表⽰前⾯的模式重复1到63次,这是对顶级域名⻓度的限制。
         * [a-z0-9]+ 表⽰匹配⼀个或多个⼩写字⺟或数字,这是顶级域名的开始部分。
         * $ 表⽰匹配字符串的结束。
         */
        String regex = "^[a-z0-9]+([._\\\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$";


        return Pattern.matches(regex, content);
    }
    /**
     * ⼿机号码以1开头的11位数字
     *
     * @param content
     * @return
     */
    public static boolean checkMobile(String content) {
         if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表⽰匹配字符串的开始。
         * 1 表⽰⼿机号码以数字1开头。
         * [3|4|5|6|7|8|9] 表⽰接下来的数字是3到9之间的任意⼀个数字。这是中国⼤陆⼿机号
         码的第⼆位数字,通常⽤来区分不同的运营商。
         * [0-9]{9} 表⽰后⾯跟着9个0到9之间的任意数字,这代表⼿机号码的剩余部分。
         * $ 表⽰匹配字符串的结束。
         */
        String regex = "^1[3|4|5|6|7|8|9][0-9]{9}$";
        return Pattern.matches(regex, content);
    }
    /**
     * 密码强度正则,6到12位
     *
     * @param content
     * @return
     */
    public static boolean checkPassword(String content){
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表⽰匹配字符串的开始。
         * [0-9A-Za-z] 表⽰匹配的字符可以是:
         * 0-9:任意⼀个数字(0到9)。
         * A-Z:任意⼀个⼤写字⺟(从A到Z)。
         * a-z:任意⼀个⼩写字⺟(从a到z)。
         * {6,12} 表⽰前⾯的字符集合(数字、⼤写字⺟和⼩写字⺟)可以重复出现6到12次。
         * $ 表⽰匹配字符串的结束。
         */
        String regex= "^[0-9A-Za-z]{6,12}$";
        return Pattern.matches(regex, content);
    }

}
1.4.3.3 身份枚举类 UserIdentityEnum

com.yj.lottery_system.service.enums 包下:

  • 只有管理员和普通用户两种身份
  • 提供一个校验身份的静态方法
java 复制代码
package com.yj.lottery_system.service.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum UserIdentityEnum {
    ADMIN("管理员"),
    NORMAL("普通用户");
    private final String msg;
    public static UserIdentityEnum forName(String identity) {
        for(UserIdentityEnum userIdentityEnum : UserIdentityEnum.values()) {
            if(identity.equalsIgnoreCase( userIdentityEnum.name() )) {
                return userIdentityEnum;
            }
        }
        return null;
    }
}
1.4.3.4 邮箱是否被使用 checkMailUsed 方法

com.yj.lottery_system.service.impl 包,UserServiceImpl类:

调用dao层的方法即可

java 复制代码
private boolean checkMailUsed(String mail) {
    //调用dao
    int count = userMapper.countByMail(mail);
    return count > 0;
}
1.4.3.5 手机号是否被使用 checkPhoneNumUsed 方法

com.yj.lottery_system.service.impl 包,UserServiceImpl类:

调用dao层的方法即可

java 复制代码
    //校验手机号是否被使用
    private boolean checkPhoneNumUsed(String phoneNumber) {
        int count = userMapper.countByPhone(new Encrypt(phoneNumber));
        return count > 0;
    }

1.4.4 添加的错误码

com.yj.lottery_system.common.errorcode 包下:

java 复制代码
package com.yj.lottery_system.common.errorcode;

public interface ServiceErrorCodeConstants {
    //人员模块错误码
    ErrorCode REGISTER_INFO_IS_EMPTY = new ErrorCode(100,"注册信息为空");
    ErrorCode MAIL_ERROR = new ErrorCode(101,"邮箱错误");
    ErrorCode PHONE_NUMBER_ERROR = new ErrorCode(102,"手机号错误");
    ErrorCode IDENTITY_ERROR = new ErrorCode(103,"身份错误");
    ErrorCode PASSWORD_IS_NULL = new ErrorCode(104,"管理员密码为空");

    ErrorCode PASSWORD_ERROR = new ErrorCode(105,"密码错误");
    ErrorCode MAIL_USED = new ErrorCode(106,"邮箱已被使用");
    ErrorCode PHONE_NUMBER_USED = new ErrorCode(107,"电话号已被使用");



    //活动模块错误码
    //奖品模块错误码
    //抽奖错误码
}

1.5 实现 dao

1.5.1 环境配置

pom.xml中添加Mybatis依赖和mysql驱动

xml 复制代码
        <!-- Mybatis 依赖包 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <!-- mysql驱动包 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

application.yml 添加配置项:

yml 复制代码
spring:
  application:
    name: lottery_system
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver #MySql驱动类名称
    url: jdbc:mysql://127.0.0.1:3306/lottery_system?characterEncoding=utf8&useSSL=false #数据库连接的url
    username: root
    password: 1234
  profiles:
    active: dev     # 本地环境 部署后需要变成 spring.profiles.active=test


## logback xml ##
logging:
  config: classpath:logback-spring.xml


#服务器配置
server:
  port: 58081 #端口号


  ## MyBatis ##
  #配置mybatis xml的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件
  #这个项目使用的注解
  #mybatis.mapper-locations=classpath:mapper/*Mapper.xml
  #配置驼峰⾃动转换
mybatis:
  configuration:
    map-underscore-to-camel-case: true

1.5.2 UserMapperr 接口类

com.yj.lottery_system.dao.mapper 包下,UserMapper接口类:

  • 我们使用注解的方式来调用数据库。
  • 提供查询邮箱绑定人数的方法
  • 提供新增 用户的方法,并且加上选项,将数据库自增 id 赋值给对象
java 复制代码
package com.yj.lottery_system.dao.mapper;

import com.yj.lottery_system.dao.dataObject.Encrypt;
import com.yj.lottery_system.dao.dataObject.UserDO;
import org.apache.ibatis.annotations.*;

@Mapper
public interface  UserMapper {
    /**
     * 查询邮箱绑定人数
     * @param email 邮箱
     * @return 绑定人数
     */
    @Select("select count(*) from user where email = #{email}")
     int countByMail(@Param("email") String email);
    /**
     * 查询电话号绑定人数
     * @param phoneNumber 电话号码
     * @return 绑定人数
     */
    @Select("select count(*) from user where phone_number = #{phoneNumber}")
     int countByPhone(@Param("phoneNumber") Encrypt phoneNumber);

    @Insert("insert into user (user_name, email, phone_number, password, identity)"+
            " values (#{userName},#{email},#{phoneNumber},#{password},#{identity})")
    @Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id")
    void insert(UserDO userDO);

}

1.5.3 TypeHandler 自动运⾏处理功能

TypeHandler : 简单理解就是当处理某些特定字段时,我们可以实现⼀些⽅法,让 Mybatis 遇到这些特定字段可以⾃动运⾏处理。

1.5.3.1 Encrypt 类 代表 phoneNumber

因为 phoneNumber 是String类型,不可能使用 TypeHandler 时是将String类型全部处理,所以将 phoneNumber 单独使用一个类封装,处理这个类。

com.yj.lottery_system.dao.dataObject 包下:

java 复制代码
package com.yj.lottery_system.dao.dataObject;

import lombok.Data;

@Data
public class Encrypt {
    private String value;

    public Encrypt() {}
    public Encrypt(String value) {
        this.value = value;
    }
}
1.5.3.2 EncryptTypeHandler类:自己实现 TypeHandler

com.yj.lottery_system.dao.handler 包下,EncryptTypeHandler类:

  • 继承 BaseTypeHandler<T> 类型,T 传需要处理的类型
  • 重写四个方法
  • 在设置参数的时候进行加密
  • 其余三个获取值的时候进行解密
java 复制代码
package com.yj.lottery_system.dao.handler;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.yj.lottery_system.dao.dataObject.Encrypt;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@MappedTypes(Encrypt.class) //被处理类型
@MappedJdbcTypes(JdbcType.VARCHAR) //转化后的jdbc类型
public class EncryptTypeHandler extends BaseTypeHandler<Encrypt> {
    //密钥
    private final byte[] KEY = "123456789abcdefg".getBytes(StandardCharsets.UTF_8);

    /**设置参数
     *
     * @param ps SQL 预编译对象
     * @param i 需要赋值的索引位置
     * @param parameter 原本位置 i 需要赋的值
     * @param jdbcType jdbc类型
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Encrypt parameter, JdbcType jdbcType) throws SQLException {
        if(null == parameter || null == parameter.getValue()) {
            ps.setString(i,null);
            return;
        }
        //加密
        AES aes = SecureUtil.aes(KEY);
        String s = aes.encryptHex(parameter.getValue());
        ps.setString(i,s);

    }

    /**
     * 获取值
     * @param rs 结果集
     * @param columnName 索引名
     * @return
     * @throws SQLException
     */
    @Override
    public Encrypt getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return decrypt(rs.getString(columnName));
    }


    /**
     * 获取值
     * @param rs 结果集
     * @param columnIndex 索引位置
     * @return
     * @throws SQLException
     */
    @Override
    public Encrypt getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return decrypt(rs.getString(columnIndex));
    }


    /**
     * 获取值
     * @param cs 结果集
     * @param columnIndex 索引位置
     * @return
     * @throws SQLException
     */
    @Override
    public Encrypt getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return decrypt(cs.getString(columnIndex));
    }

    //解密 方法
    private Encrypt decrypt(String s) {
        if(!StringUtils.hasText(s)) {
            return null;
        }
        return new Encrypt(SecureUtil.aes(KEY).decryptStr(s));

    }
}
1.5.3.3 修改配置,使用自定义TypeHandle

application.yml中修改处理路径为自己的路径:

yml 复制代码
mybatis:
  type-handlers-package: com.yj.lottery_system.dao.handler #处理路径,替换成⾃⼰的

1.5.4 测试

1.5.5 do层数据(相当于model)

与数据库表中对应的类

每张表都有的id创建时间和更新时间,提取为公共类:

com.yj.lottery_system.dao.dataObject 包下 BaseDO 类:

java 复制代码
package com.yj.lottery_system.dao.dataObject;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data
public class BaseDO implements Serializable {
    /**
     * 主键
     */
    private Long id;
    /**
     * 创建时间
     */
    private Date gmtCreate;
    /**
     * 修改时间
     */
    private Date gmtModified;

}

user表对应其他数据:

java 复制代码
package com.yj.lottery_system.dao.dataObject;

import lombok.Data;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)
@Data
public class UserDO extends BaseDO{
    /**
     * 邮箱
     */
    private String email;
    /**
     * 身份信息
     */
    private String identity;
    /**
     * 密码
     */
    private String password;
    /**
     * 电话号码
     */
    private Encrypt phoneNumber;
    /**
     * 用户名
     */
    private String userName;
}

1.6 前端

register.html :

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet"
          href="https://cdn.staticfile.org/twitter-bootstrap/4.5.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="./css/base.css">
    <script
            src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script
            src="https://cdn.bootcdn.net/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script>
    <link rel="stylesheet"
          href="https://cdn.staticfile.org/twitter-bootstrap/4.5.2/js/bootstrap.min.js">
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #fff;
            margin: 0;
            padding: 0;
        }
        .register-container {
            max-width: 600px;
            margin: 50px auto;
            padding: 20px;
            background-color: #fff;
            border-radius: 5px;
        }
        .register-container h2{
            font-weight: 600;
            font-size: 32px;
            letter-spacing: 1px;
            color: #000000;
            line-height: 50px;
            text-align: center;
            margin-bottom: 60px;
        }
        .show-password {
            float: right;
            cursor: pointer;
            color: #008CBA;
        }

    </style>
</head>
<body>
<div class="register-container">
    <h2>填写注册信息</h2>
    <form id="registerForm">
        <div class="form-group">
            <label for="name">姓名</label>
            <input type="text" class="form-control" id="name"
                   name="name" placeholder="请输入姓名" required>
        </div>
        <div class="form-group">
            <label for="mail">邮箱</label>
            <input type="email" class="form-control" id="mail"
                   name="mail" placeholder="请输入邮箱" required>
        </div>
        <div class="form-group">
            <label for="phoneNumber">手机号</label>
            <input type="text" class="form-control" id="phoneNumber"
                   name="phoneNumber" placeholder="请输入手机号" required>
        </div>
        <div class="form-group">
            <label for="password">密码</label>
            <input type="password" class="form-control" id="password"
                   name="password" placeholder="请输入密码" required>
        </div>
        <button type="submit"
                class="btn btn-primary btn-block">注册</button>
    </form>
</div>

<script>
    // 获取链接参数
    var params = new URLSearchParams(window.location.search);
    var jumpList = params.get('jumpList');
    var admin = params.get('admin');

    // 判断是否隐藏密码输入框
    if(admin === 'false') {
        // 隐藏密码输入框
        $('#password').closest('div.form-group').css('display', 'none');
    }

    // 使用jQuery Validate插件来验证表单
    $("#registerForm").validate({
        rules: {
            name: "required",
            mail: {
                required: true,
                email: true
            },
            phoneNumber: "required",
            password: {
                required: true,
                minlength: 6
            }
        },
        messages: {
            name: "请输入您的姓名",
            mail: "请输入有效的邮箱地址",
            phoneNumber: "请输入您的手机号",
            password: {
                required: "请输入密码",
                minlength: "密码长度至少为6个字符"
            }
        },
        submitHandler: function(form) {
            var formData = {
                name: $('#name').val(),
                mail: $('#mail').val(),
                phoneNumber: $('#phoneNumber').val(),
                password: $('#password').val(),
                identity: (admin == null || admin === 'true') ? "ADMIN" : "NORMAL"
            };

            // 表单验证通过后,发送AJAX请求
            $.ajax({
                url: '/register',
                type: 'POST',
                contentType: 'application/json',
                data: JSON.stringify(formData),
                success: function(result) {
                    if (result.code != 200) {
                        alert("注册失败!" + result.msg);
                    } else {
                        if (jumpList === 'true') {
                            // 给父页面发消息,表示要跳转到人员列表页
                            window.parent.postMessage(
                                {
                                    from: 'user-list.html',
                                    id: '#userList'
                                }, '*'
                            );
                        } else {
                            alert("注册成功,去登录!");
                            location.assign("blogin.html");
                        }
                    }

                }
            });
            return false; // 阻止表单的默认提交行为
        }
    });
</script>
</body>
</html>
相关推荐
CryptoRzz1 分钟前
日本股票 API 对接实战指南(实时行情与 IPO 专题)
java·开发语言·python·区块链·maven
yugi9878382 分钟前
基于M序列的直扩信号扩频码生成方法及周期长码直扩信号的MATLAB实现方案
开发语言·matlab
程序员水自流3 分钟前
MySQL数据库自带系统数据库功能介绍
java·数据库·mysql·oracle
谷哥的小弟8 分钟前
Spring Framework源码解析——RequestContext
java·后端·spring·框架·源码
乾元9 分钟前
基于时序数据的异常预测——短期容量与拥塞的提前感知
运维·开发语言·网络·人工智能·python·自动化·运维开发
江上清风山间明月10 分钟前
使用python将markdown文件生成pdf文件
开发语言·python·pdf
j_xxx404_13 分钟前
C++算法入门:二分查找合集(二分查找|在排序数组中查找元素的第一个和最后一个位置)
开发语言·c++
天远Date Lab13 分钟前
Java微服务实战:聚合型“全能小微企业报告”接口的调用与数据清洗
java·大数据·python·微服务
ss27316 分钟前
阻塞队列:ArrayBlockingQueue如何用Lock与Condition实现高效并发控制
开发语言·python
lizz3118 分钟前
C++操作符重载深度解析
java·c++·算法