目录
- 一、用户注册
-
- [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 | 必须 | |
| 用户邮箱地址 | 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>


