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

目录

  • 一、用户注册
    • [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>
相关推荐
Scott9999HH35 分钟前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔41 分钟前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨3 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区5 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说6 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json