二. 注册模块
- 创建Service包,用来存放Service层的对象。
- UserService接口
import com.forum.dataobject.UserDao;
import com.forum.dataobject.UserPasswordDao;
/**
* @author xiao xu
* user service interface
*/
public interface UserService {
/**
* insert user
* @param userDao user
* @param userPasswordDao user password
* @exception Exception 用于事务
*/
void insertUser(UserDao userDao, UserPasswordDao userPasswordDao) throws Exception;
/**
* 检查手机号是否已经存在
* @param telephone 手机号
* @return 返回影响行数
*/
int isExist(String telephone);
}
- UserServiceImpl类,有关User的主要业务逻辑都会在这个类中进行处理。一般的Service层的接口实现类都会在service包下新建一个impl包用来存放接口的实现类,UserServiceImpl也是如此。
import com.forum.dao.UserDaoMapper;
import com.forum.dao.UserPasswordDaoMapper;
import com.forum.dataobject.UserDao;
import com.forum.dataobject.UserPasswordDao;
import com.forum.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* @author xiao xu
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
UserDaoMapper userDaoMapper;
@Resource
UserPasswordDaoMapper userPasswordDaoMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void insertUser(UserDao userDao, UserPasswordDao userPasswordDao) throws Exception {
if(userDaoMapper.insertSelective(userDao) == 0) {
throw new Exception();
}
userPasswordDao.setUserId(userDao.getId());
if(userPasswordDaoMapper.insertSelective(userPasswordDao) == 0) {
throw new Exception();
}
}
@Override
public int isExist(String telephone) {
if (userDaoMapper.selectByTelephone(telephone) == null) {
return 0;
} else {
return 1;
}
}
}
说明:
- @Service注解表示将UserServiceImpl这个类交由Spring容器进行管理,这样就可以在类中使用Spring的依赖注入获取其他被Spring容器管理的类实例,而不是使用new来创建类实例从而降低耦合度。
- @Resource注解表示将UserDaoMapper注入到本类中。
- @Transactional(rollbackFor = Exception.class) 用来开启数据库事务,rollbackFor表示触发回滚的目标类,为了简单这里使用Exception这个异常类。
- insertUser方法用来进行用户注册,由于在创建数据库表的时候为了安全将用户数据和用户密码分开存储,因此在用户注册时候需要操作两张表,所以这两个操作要么全都成功要么全都失败,这里就会需要使用事务来管理这两个操作;进入到方法中userDaoMapper.insertSelective(userDao)表达式的含义是将userDao中包含的数据插入到数据库中返回的结果是对数据库表产生影响的行数,所以这里和0进行比较,如果等于0说明数据库插入失败,那么抛出异常进行回滚;这个地方需要注意的一点是,user_pwd在进行存储时需要user_info这张表的id值用以将用户数据和密码进行关联,但是这里user_info使用的是自增主键,因此需要user_info插入数据之后将id的值返回,最后将用户密码插入到user_pwd表中;想要获取这个id的值,需要在mybaits相应的方法中添加这两个参数useGeneratedKeys="true" keyProperty="id",完整的mybatis方法是/resources/mapping/UserDaoMapper.xml:
<insert id="insertSelective" parameterType="com.forum.dataobject.UserDao" useGeneratedKeys="true" keyProperty="id">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Tue Oct 13 10:41:42 CST 2020.
-->
insert into user_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
name,
</if>
<if test="account != null">
account,
</if>
<if test="age != null">
age,
</if>
<if test="gender != null">
gender,
</if>
<if test="userImg != null">
user_img,
</if>
<if test="telephone != null">
telephone,
</if>
<if test="status != null">
status,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="account != null">
#{account,jdbcType=VARCHAR},
</if>
<if test="age != null">
#{age,jdbcType=INTEGER},
</if>
<if test="gender != null">
#{gender,jdbcType=BIT},
</if>
<if test="userImg != null">
#{userImg,jdbcType=VARCHAR},
</if>
<if test="telephone != null">
#{telephone,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=BIT},
</if>
</trim>
</insert>
- isExist方法用来校验手机号是否已经存在,为后面的校验手机号是否已经被注册提供实现。这个方法所使用的数据库查询语句在mybatis逆向工程中并没有自动生成,因此需要我们手动编写。在com/forum/dao/UserDaoMapper中添加如下接口:
UserDao selectByTelephone(String telephone);
- 然后在/resources/mapping/UserDaoMapper.xml中添加如下mybatis方法,注意接口名要和方法的id值相同
<select id="selectByTelephone" parameterType="java.lang.String" resultMap="BaseResultMap">
select <include refid="Base_Column_List"/>
from user_info
where telephone = #{telephone,jdbcType=VARCHAR}
</select>
说明:parameterTyper="java.lang.String"表示这个接口的参数类型是String类型;resultMap表示返回值类型为BaseResultMap也就是文件顶部所定义的对象
- 创建controller包,包下创建用于处理用户有关的请求的控制器UserController
import com.forum.dataobject.UserDao;
import com.forum.dataobject.UserPasswordDao;
import com.forum.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Administrator
* @create 2020 -10 -13 11:30
* 由于采用前后端分离的方式,返回的数据都是restFul风格所以直接使用@RestController
*/
@RestController
@CrossOrigin(origins = "*", allowCredentials = "true")
@RequestMapping("/user")
public class UserController {
@Resource
HttpServletRequest httpServletRequest;
@Resource
UserService userService;
@RequestMapping("/getValidateCode")
public String getValidateCode(@RequestParam("telephone") String telephone) {
// 校验手机号格式,如果格式错误直接返回错误
String regex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(17[013678])|(18[0,5-9]))\\d{8}$";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(telephone);
boolean isMatch = m.matches();
if (!isMatch) {
return "error";
}
// 检查手机号是否以被注册
if (userService.isExist(telephone) > 0) {
return "error";
}
//生成随机验证码
Random random = new Random();
String validateCode = String.valueOf(random.nextInt(999999));
// 使用session进行存储
HttpSession session = httpServletRequest.getSession();
// 设置session过期时间
session.setMaxInactiveInterval(5 * 60);
session.setAttribute(telephone, validateCode);
return validateCode;
}
@RequestMapping("/register")
public String register(UserDao userDao, @RequestParam("password") String password, @RequestParam("validateCode") String validateCode) {
// 获取session中的验证码,进行校验
if(!httpServletRequest.getSession().getAttribute(userDao.getTelephone()).equals(validateCode)) {
return "failed";
} else {
// 校验通过,删除此手机号对应的验证码
httpServletRequest.getSession().removeAttribute(userDao.getTelephone());
}
userDao.setAccount(userDao.getTelephone());
userDao.setStatus(true);
UserPasswordDao userPasswordDao = new UserPasswordDao();
userPasswordDao.setUserPassword(password);
try {
userService.insertUser(userDao, userPasswordDao);
} catch (Exception e) {
return "failed";
}
return "ok";
}
}
private String encodeByMd5(String str) throws NoSuchAlgorithmException {
// 使用MessageDigest获取Md5实例
MessageDigest md5 = MessageDigest.getInstance("MD5");
// 使用Base64Encoder进行加迷
BASE64Encoder base64 = new BASE64Encoder();
return base64.encode(md5.digest(str.getBytes()));
}
说明:
- 在类上添加@RestController注解标明这个类处理完http请求后返回的数据都是RestFul风格。
- @CrossOrigin(origins = "*", allowCredentials = "true") 这个注解用来解决跨域问题,由于项目采用前后端分类的方式,所以在进行前后端交互的时候会产生跨域问题,springboot可以使用这个注解来解决跨域问题。
- @RequestMapping("/user")这个注解作用在类上面表示,这个控制器可以处理/user/*的一类请求
- @Resource注解作用在UserService上,表示将UserService这个Bean注入到此类中,在使用的时候直接用即可,不用再使用new进行实例化
- getValidateCode方法用来处理/user/getValidateCode请求(用户获取手机验证码),@RequestParam("telephone")表示这个请求中必须包含一个名为telephone的数据,spring会将这个数据反序列到String telephone这个变量中,然后方法体内先是使用正则表达式对手机号的格式进行校验,通过之后再校验此手机号是否已经被注册(向数据库查询该手机号,已存在就是已被注册),接着生成随机的验证码并存入到Session中(Session可以先理解成服务器缓存),最后将验证码返回。
- register方法用来处理/user/register(用户注册)请求,这个方法的几个参数意义为:UserDao用来接收用户数据,SpringBoot会根据请求中的数据自动将相应的数据封装到UserDao中,后面的两个参数跟getValidateCode方法中的参数类似。进入方法之后会先校验手机号验证码,通过之后调用Service层的insertUser方法完成注册。
到目前为止的项目结构:
写在最后:整个注册服务返回的数据类型都是String类型,只能返回成功或者失败。更多的时候是需要返回复杂数据类型的,所以这里非常的不合理,后面会单独的进行改造并用来作为整个项目的返回结果。将返回结果统一处理的好处有很多,比如说方便阅读,方便前端直接对返回数据进行处理等等,后面用到的时候就知道了。
网友评论