项目功能- 用户注册功能
1 创建数据表
-- 创建用户信息表
CREATE TABLE `store`.`t_user`(
`uid` int NOT NULL AUTO_INCREMENT COMMENT '用户id',
`username` varchar(20) NOT NULL UNIQUE COMMENT '用户名',
`password` varchar(32) NOT NULL COMMENT '密码',
`salt` varchar(36) NULL COMMENT '盐值',
`phone` varchar(20) NULL COMMENT '电话号码',
`email` varchar(30) NULL COMMENT '电子邮箱',
`gender` int NULL COMMENT '性别:0-女;1-男',
`avatar` varchar(50) NULL COMMENT '头像',
`is_delete` int NULL COMMENT '是否删除:0-未删除;1-已删除',
`created_user` varchar(20) NULL COMMENT '创建人',
`created_time` datetime NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
`modified_user` varchar(20) NULL COMMENT '更新人',
`modified_time` datetime NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`uid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
2 创建用户实体类
2-1 创建实体类的基类
将类中公共的属性抽取出来形成一个基类,被所有实体类继承
package com.cy.store.common;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/*
实体类的基类
*/
@Data
public class BaseEntity implements Serializable {
// 创建人
private String createdUser;
//创建时间
private Date createdTime;
//更新人
private String modifiedUser;
//更新时间
private Date modifiedTime;
//是否删除:0-未删除;1-已删除
private Integer isDelete;
}
2-2 创建用户实体类 继承基类
package com.cy.store.entity;
import com.cy.store.common.BaseEntity;
import lombok.Data;
import java.io.Serializable;
/*
用户对象实体类
*/
@Data
public class UserEntity extends BaseEntity implements Serializable {
// 用户id
private Integer uid;
// 用户名
private String username;
// 密码
private String password;
// 盐值
private String salt;
// 电话号码
private String phone;
// 电子邮箱
private String email;
// 性别:0-女;1-男
private Integer gender;
// 头像
private String avatar;
}
3 注册-持久层
通过Mybatis来操作数据库,在做mybatis的开发流程
分析:
1.先查询用户是否存在,存在则不能进行注册,并给出提示。(对应查询语句)
2.用户不存在,在进行注册。
3-1 规划需要执行的sql语句
用户注册功能,相当于在做数据库的插入操作。
insert into t_user (username,password,...) values (....);
3-2 设计接口和抽象方法
定义mapper接口。
package com.cy.store.mapper;
import com.cy.store.entity.UserEntity;
/*
用户持久层mapper接口
*/
public interface UserMapper {
/**
* 插入用户
* @param userEntity 用户对象
* @return 影响的行数
*/
Integer insertUser(UserEntity userEntity);
/**
* 通过用户名查询用户信息
* @param username 用户名称
* @return UserEntity 用户对象
*/
UserEntity selectByUserName(String username);
}
3-3 编写映射
定义xml映射文件,与对应的接口进行关联。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper.UserMapper">
<!-- 当数据表中的字段与实体类中不一致时,做映射关系处理 -->
<resultMap id="userMap" type="com.cy.store.entity.UserEntity">
<result property="isDelete" column="is_delete"/>
<result property="modifiedTime" column="modified_time"/>
<result property="modifiedUser" column="modified_user"/>
<result property="createdTime" column="created_time"/>
<result property="createdUser" column="created_user"/>
</resultMap>
<!-- 重复使用的sql片段 -->
<sql id="userColumn">
uid,
username,
password,
salt,
phone,
email,
gender,
avatar,
is_delete,
created_time,
created_user,
modified_time,
modified_user
</sql>
<!-- 根据用户名称查询用户信息 -->
<select id="selectByUserName" parameterType="string" resultMap="userMap">
select
<include refid="userColumn"></include>
from Blog where username= #{username}
</select>
<!-- 插入用户 -->
<insert id="insertUser" parameterType="com.cy.store.entity.UserEntity" useGeneratedKeys="true" keyProperty="uid">
insert into t_user (<include refid="userColumn"></include>) values (
#{username},#{password},#{salt},#{phone},#{email},#{gender},#{avatar},#{isDelete},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime}
)
</insert>
</mapper>
3-4 单元测试
每一个层都应要写单元测试,对已开发好的功能做自测,确保没有问题后才可以发布到测试环境,移交测试人员进行测试。
4 注册-业务层(****)
业务功能层是整个系统实现过程中最重要的一层。因为这层不仅需要考虑具体功能的实现过程,还要考虑问题的出现。
- 功能具体实现的逻辑设计
- 实现该功能时有可能出现的问题,也就是异常的规划。
4-1 异常的规划
1 设计业务层的异常机制
分析 业务层的异常首先是属于运行时抛出的异常,所有我们设计的异常都是 RunTimeException的子类,因为异常是有很多的,所以我们应该设计一个异常基类,用于被其他不同业务异常继承。
具体实现如下:
package com.cy.store.common;
/*
设计整个系统的异常基类,这个类继承RunTimeException
并重写父类中的构造方法
*/
public class ServiceException extends RuntimeException{
public ServiceException() {
super();
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(Throwable cause) {
super(cause);
}
protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
可能出现的异常
- 注册时用户已经存在,被占用异常。
- 数据插入时异常。
package com.cy.store.service.ex;
import com.cy.store.common.ServiceException;
/**
* 数据插入异常
*/
public class InsertException extends ServiceException {
public InsertException() {
super();
}
public InsertException(String message) {
super(message);
}
public InsertException(String message, Throwable cause) {
super(message, cause);
}
public InsertException(Throwable cause) {
super(cause);
}
protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
4-2 业务层接口以及实现类设计
- 接口设计
package com.cy.store.service;
import com.cy.store.entity.UserEntity;
public interface UserService {
// 用户注册功能,这里不需要返回。业务层通过mapper的影响行数做逻辑判断。
void register(UserEntity userEntity);
// 用户查询功能
UserEntity queryByName(String username);
}
- 实现类设计
package com.cy.store.service.impl;
import com.cy.store.entity.UserEntity;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.UserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UserDuplicatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Date;
import java.util.InvalidPropertiesFormatException;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void register(UserEntity userEntity) {
// 判断参数是否为空
String username = userEntity.getUsername();
if (username == null || username == "") {
throw new NullPointerException("用户名不能为空");
}
// 查询用户是否存在
UserEntity entity = userMapper.selectByUserName(username);
if (entity != null) {
throw new UserDuplicatedException("用户已存在");
}
// 插入用户信息
userEntity.setIsDelete(0);
userEntity.setCreatedUser("system");
userEntity.setCreatedTime(new Date());
userEntity.setModifiedUser("system");
userEntity.setModifiedTime(new Date());
Integer integer = userMapper.insertUser(userEntity);
if (integer != 1) {
throw new InsertException("用户插入时出现异常");
}
}
@Override
public UserEntity queryByName(String username) {
if (username == "" || username == null) {
throw new NullPointerException("用户姓名不能为空");
}
UserEntity userEntity = userMapper.selectByUserName(username);
return userEntity;
}
}
4-3 单元测试 略
4-4 业务层逻辑补充-密码处理
分析:
- 1、确定密码加密的算法逻辑。
- 2、使用UUID类生成一个随机的盐值,并将盐值保留存入数据库表中,这个字段在后面用户登录的时候 需要解密使用。
- 3、将 用户输入的M密码+盐值,利用加密算法加密,等到新的加密密码保存在数据库表中。这样就做到了密码的加密。
- 4、用户登入的时候,可以利用原始密码+盐值 进行加码 得到的密码与 数据库中注册时存入的你密码做比对。一致则登入成功,否则失败。
代码实现:
package com.cy.store.utils;
import org.springframework.util.DigestUtils;
public class EncryptMD5 {
/**
*
* @param oldPassword 原始密码
* @param salt 盐值
* @return 新的密码
*/
public static String MD5PASSWORD(String oldPassword,String salt){
String newPassword = oldPassword + salt;
for (int i = 0; i < 3; i++) {
newPassword = DigestUtils.md5DigestAsHex(newPassword.getBytes());
}
return newPassword;
}
}
package com.cy.store.service.impl;
import com.cy.store.entity.UserEntity;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.UserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UserDuplicatedException;
import com.cy.store.utils.EncryptMD5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Date;
import java.util.InvalidPropertiesFormatException;
import java.util.UUID;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void register(UserEntity userEntity) {
// 判断参数是否为空
String username = userEntity.getUsername();
String oldPassword = userEntity.getPassword();
if (username== null || username == "") {
throw new NullPointerException("用户名不能为空");
}
// 查询用户是否存在
UserEntity entity = userMapper.selectByUserName(username);
if (entity != null) {
throw new UserDuplicatedException("用户已存在");
}
// 插入用户信息
userEntity.setIsDelete(0);
userEntity.setCreatedUser("system");
userEntity.setCreatedTime(new Date());
userEntity.setModifiedUser("system");
userEntity.setModifiedTime(new Date());
// 密码加密处理
String salt = UUID.randomUUID().toString().replace("-", "");
userEntity.setSalt(salt);
String newPassword = EncryptMD5.MD5PASSWORD(oldPassword, salt);
userEntity.setPassword(newPassword);
Integer integer = userMapper.insertUser(userEntity);
if (integer != 1) {
throw new InsertException("用户插入时出现异常");
}
}
@Override
public UserEntity queryByName(String username) {
if (username == "" || username == null) {
throw new NullPointerException("用户姓名不能为空");
}
UserEntity userEntity = userMapper.selectByUserName(username);
return userEntity;
}
}
5 注册-控制层
5-1 统一结果数据封装
分析: 前端请求后得到的结果,可以看做是一个对象。这个对象包含的属性有:状态码、数据、描述信息。
封装响应对象
package com.cy.store.common;
import java.io.Serializable;
/**
* Json格式的数据对象
*/
public class JsonResult<E> implements Serializable {
// 业务状态码
private Integer code;
// 描述信息
private String msg;
// 数据对象
private E data;
public JsonResult() {
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public E getData() {
return data;
}
public void setData(E data) {
this.data = data;
}
/**
* 逻辑处理正常,返回成功场景
* @param data 返回数据
* @param msg 返回消息
* @param <E> 数据类型
* @return JsonResult 对象
*/
public static <E> JsonResult<E> ok(E data ,String msg){
JsonResult<E> result = new JsonResult<>();
result.setCode(1001);
result.setMsg(msg);
result.setData(data);
return result;
}
/**
* 逻辑处理异常,返回失败场景
* @param msg
* @param <E>
* @return
*/
public static JsonResult<Void> fail(String msg){
JsonResult<Void> result = new JsonResult<>();
result.setCode(9999);
result.setMsg(msg);
return result;
}
}
5-2 设计请求
分析: 对于注册功能来说
- 请求路径: /user/reg
- 请求参数: User user
- 请求方式: post
- 响应结果: JsonResult
创建一个Controller类用来接受处理请求,UserController
。
package com.cy.store.controller;
import com.cy.store.common.JsonResult;
import com.cy.store.entity.UserEntity;
import com.cy.store.service.UserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UserDuplicatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 用户注册
* @param user java实体类对象的形式传参
* @return
*/
//@PostMapping("/reg")
//public JsonResult<Void> reg(@RequestBody UserEntity user){
//
// try {
// userService.register(user);
// return JsonResult.ok(null, "用户注册成功");
// } catch (UserDuplicatedException e) {
// return JsonResult.fail(e.getMessage());
// }catch (InsertException e){
// return JsonResult.fail(e.getMessage());
// }
//}
/**
* 封装了 异常统一处理类后的写法
* 如果请求发生了异常,会去调用异常处理类,处理异常并返回给前端,没有异常就直接返回处理成功的结果。
* @param user
* @return
*/
@PostMapping("/reg")
public JsonResult<Void> reg(@RequestBody UserEntity user) {
userService.register(user);
return JsonResult.ok(null, "用户注册成功");
}
}
创建系统业务异常的统一处理全局类 StoreExceptionHandler
package com.cy.store.common;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UserDuplicatedException;
import com.cy.store.service.ex.UserNameNotNullException;
import com.cy.store.service.ex.UserNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* StoreExceptionHandler 统一对请求中的异常做处理,
* 系统·抛出异常后才会调用这个类。
* 通过捕获异常的类型,判断属于哪个异常,然后返回给前端。
*/
@RestControllerAdvice
@Slf4j
public class StoreExceptionHandler {
@ExceptionHandler(ServiceException.class)
public JsonResult<Void> handlerException(Throwable e) {
log.info("Exception:==>" + e.getMessage());
if (e instanceof UserDuplicatedException) {
return JsonResult.fail(e.getMessage());
} else if (e instanceof InsertException) {
return JsonResult.fail(e.getMessage());
} else if (e instanceof UserNotFoundException) {
return JsonResult.fail(e.getMessage());
} else if (e instanceof UserNameNotNullException) {
return JsonResult.fail(e.getMessage());
} else {
return JsonResult.fail(e.getMessage());
}
}
}