store(商城项目)Springboot+springmvc+ajax+mybatis(三)

7. 用户-注册-前端页面

(开发笔记:无)

完成后,当用户在登录页面输入用户名、密码,并点击“立即注册”按钮时,就会触发所绑定的事件,执行了$.ajax()函数,该函数执行时,会将表单中的数据进行序列化(组织成服务器可以接收的格式),根据type方式向url指定的路径提交请求!

服务器端的Tomcat会根据DispatcherServlet的配置(在SpringBoot项目中,映射的路径是/*),接收到该请求,并转由UserControllerreg()方法进行处理,在该方法内部,是调用了Service对象的reg()方法进行处理的,当前项目中,Service对象其实就是UserServiceImpl类的对象(因为这个在组件扫描的包中,并且添加了@Service注解,所以,这个类的对象是由Spring框架创建并自动装配到UserController的属性中的)!

UserServiceImplreg()方法中就实现了注册过程,相关的数据操作(查询、插入)都是由基于MyBatis框架开发的持久层来实现的,整个reg()执行完毕后,没有返回结果,则在UserController中关于userService的调用也就结束,即userService.reg()执行完毕!

接下来,UserController中就返回了JSON数据(代码为return new JsonResult<>(OK);,是由Jackson框架将这行代码所创建的JsonResult对象转换为了JSON格式的字符串),则客户端的$.ajax()函数就会收到响应的结果,这个结果就是服务器端返回JSON字符串,由于响应是成功(HTTP响应码是200)的,则$.ajax()函数的success配置就会被触发,所以,就回调了success对应的函数,该函数的参数就是服务器端响应的JSON结果!

8. 用户-登录-持久层

在处理登录时,需要执行增删改查操作应该只有“查询”操作,客户端会提交用户名和密码,应该先只根据用户名查询用户数据,然后,取出其中的盐值,基于客户端提交的密码原文与盐值执行加密,得到相应密文,将密文与查询结果中的密码进行对比,就可以判断密码是否正确!

那么,需要执行的SQL语句大致是:

select * from t_user where username=?

在开发“注册”功能时,就已经实现了以上查询功能!所以,此次无需开发持久层!

9. 用户-登录-业务层

应该先创建处理“登录”过程中可能出现的错误对应的异常!例如,当用户名不存在时,可能抛出异常,则在cn.tedu.store.service.ex包中创建UserNotFoundException异常类,它应该继承自ServiceException;另外,当密码错误时,也会抛出异常,则在同样的包中创建PasswordNotMatchException异常类,也是继承自ServiceException的!

IUserService接口中添加抽象方法:

User login(String username, String password);

然后,在UserServiceImpl中重写以上抽象方法:

public User login(String username, String password) {
    // 根据参数username,调用userMapper.findByUsername()方法执行查询
    // 判断查询结果是否为null 
    // 是:抛出UserNotFoundException

    // 判断查询结果中的isDelete是否为1
    // 是:抛出UserNotFoundException

    // 从查询结果中取出盐值
    // 基于参数password与盐值执行加密,得到密文
    // 判断以上得到的密文与查询结果中的password是否不一致
    // 是:抛出PasswordNotMatchException

    // 创建新的User对象
    // 将查询结果中的uid、username、avatar的值封装到新创建的对象中
    // 返回新创建的对象
    return null;
}

具体代码为:

@Override
public User login(String username, String password) {
    // 输出日志
    System.err.println("UserServiceImpl.login()");
    System.err.println("\tusername=" + username);
    System.err.println("\tpassword=" + password);
    // 根据参数username,调用userMapper.findByUsername()方法执行查询
    User result = userMapper.findByUsername(username);
    // 判断查询结果是否为null 
    if (result == null) {
        // 是:抛出UserNotFoundException
        throw new UserNotFoundException(
            "登录失败!用户数据不存在!");
    }

    // 判断查询结果中的isDelete是否为1
    if (result.getIsDelete() == 1) {
        // 是:抛出UserNotFoundException
        throw new UserNotFoundException(
            "登录失败!用户数据不存在!");
    }

    // 从查询结果中取出盐值
    String salt = result.getSalt();
    // 基于参数password与盐值执行加密,得到密文
    String md5Password = getMd5Password(password, salt);
    // 判断以上得到的密文与查询结果中的password是否不一致
    System.err.println("\t数据库中的密码:" + result.getPassword());
    if (!md5Password.equals(result.getPassword())) {
        // 是:抛出PasswordNotMatchException
        throw new PasswordNotMatchException(
            "登录失败!密码错误!");
    }

    // 创建新的User对象
    User user = new User();
    // 将查询结果中的uid、username、avatar的值封装到新创建的对象中
    user.setUid(result.getUid());
    user.setUsername(result.getUsername());
    user.setAvatar(result.getAvatar());
    // 返回新创建的对象
    return user;
}

最后,在UserServiceTests中测试以上新开发的功能是否正确:

@Test
public void login() {
    try {
        String username = "rootx";
        String password = "1234x";
        User user = service.login(username, password);
        System.err.println("OK. " + user);
    } catch (ServiceException e) {
        System.err.println(e.getClass().getName());
        System.err.println(e.getMessage());
    }
}

10. 用户-登录-控制器层

首先,此次处理“登录”时,在业务层抛出了2种新的异常,则需要在GlobalExcpetionHandler中进行处理!

然后,在UserController中添加处理“登录”的方法:

// http://localhost:8080/users/login?username=root&password=1234
@RequestMapping("login")
public JsonResult<User> login(String username, String password, HttpSession session) {
    User data = userService.login(username, password);
    session.setAttribute("uid", data.getUid());
    session.setAttribute("username", data.getUsername());
    return new JsonResult<>(OK, data);
}

11. 用户-登录-前端页面

12. 用户-修改密码-持久层

(a) 规划所需要执行的SQL语句

需要修改密码时,执行的是“更新密码字段的值”的操作,需要执行的SQL语句大致是:

update t_user set password=?, modified_user=?, modified_time=? where uid=?

通常,在执行增、删、改类型的操作之前,都可能有一些相关的检查,也就是需要执行某些查询类型的操作,用于检查数据的访问是否合理!例如应该检查需要修改密码的这条用户数据是否存在,可以使用的SQL语句大致是:

select * from t_user where uid=?

(b) 接口与抽象方法

UserMapper接口中添加以上规则的2个功能的抽象方法:

/**
 * 修改某用户的密码
 * @param uid 用户的id
 * @param password 新密码
 * @param modifiedUser 修改执行人
 * @param modifiedTime 修改时间
 * @return 受影响的行数
 */
Integer updatePasswordByUid(
        @Param("uid") Integer uid, 
        @Param("password") String password, 
        @Param("modifiedUser") String modifiedUser, 
        @Param("modifiedTime") Date modifiedTime);

/**
 * 根据用户id查询用户数据详情
 * @param uid 用户id
 * @return 匹配的用户数据,如果没有匹配的数据,则返回null
 */
User findByUid(Integer uid);

(c) 配置映射

UserMapper.xml中配置以上2个抽象方法的映射:

<!-- 修改某用户的密码 -->
<!-- Integer updatePasswordByUid(
        @Param("uid") Integer uid, 
        @Param("password") String password, 
        @Param("modifiedUser") String modifiedUser, 
        @Param("modifiedTime") Date modifiedTime) -->
<update id="updatePasswordByUid">
    UPDATE
        t_user
    SET
        password=#{password},
        modified_user=#{modifiedUser},
        modified_time=#{modifiedTime}
    WHERE
        uid=#{uid}
</update>

<!-- 根据用户id查询用户数据详情 -->
<!-- User findByUid(Integer uid) -->
<select id="findByUid"
    resultMap="UserEntityMap">
    SELECT 
        *
    FROM
        t_user
    WHERE
        uid=#{uid}
</select>

UserMapperTests中添加2个单元测试:

@Test
public void updatePasswordByUid() {
    Integer uid = 16;
    String password = "1234";
    String modifiedUser = "密码管理员";
    Date modifiedTime = new Date();
    Integer rows = mapper.updatePasswordByUid(uid, password, modifiedUser, modifiedTime);
    System.err.println("rows=" + rows);
}

@Test
public void findByUid() {
    Integer uid = 16000;
    User result = mapper.findByUid(uid);
    System.err.println(result);
}

13. 用户-修改密码-业务层

(a) 规划可能出现的异常

最终需要执行的是更新密码操作,则可能出现更新失败的问题,则应该抛出UpdateException

在执行更新之前,应该检查用户数据是否存在,如果用户数据不存在,则抛出UserNotFoundException

并且,继续检查用户数据状态是否正确,例如isDelete是否为0,否则,将抛出UserNotFoundException

在执行更新密码之前,还应该检查原密码是否正确,如果不匹配,则抛出PasswordNotMatchException

所以,需要在cn.tedu.store.service.ex包中创建UpdateException

(b) 业务接口及抽象方法

IUserService接口中添加抽象方法:

void changePassword(Integer uid, String oldPassword, String newPassword, String username);

(c) 实现抽象方法

UserServiceImpl类中实现以上抽象方法:

public void changePassword(Integer uid, String oldPassword, String newPassword, String username) {
    // 日志:输出原密码,新密码
    // 基于参数uid调用userMapper的findByUid()查询用户数据
    // 判断查询结果是否为null
    // 是:UserNotFoundException

    // 判断查询结果中的isDelete是否为1
    // 是:UserNotFoundException

    // 从查询结果中取出盐值
    // 日志:“将原密码执行加密”
    // 将参数oldPassword结合盐值执行加密,得到oldMd5Password
    // 日志:输出查询结果中的password
    // 判断oldMd5Password与查询结果中的password是否不一致
    // 是:PasswordNotMatchException

    // 日志:“将新密码执行加密”
    // 将参数newPassword结合盐值执行加密,得到newMd5Password
    // 调用userMapper的updatePasswordByUid()执行更新密码,并获取返回的受影响的行数
    // 判断受影响的行数是否不为1
    // 是:UpdateException
}

完成后,在UserServiceTests中编写单元测试:

xxx

14. 用户-修改密码-控制器层

15. 用户-修改密码-前端页面

16. 用户-修改资料-持久层

17. 用户-修改资料-业务层

18. 用户-修改资料-控制器层

19. 用户-修改资料-前端页面

------------------------------------------------

附1:使用SpringMVC框架统一处理异常【补充】

添加了@ExceptionHandler注解的处理异常的方法,只能作用于当前控制器类,也就是当前控制器中处理请求时出现的异常才可以被处理,其它控制器处理请求时出现的异常并不会被处理!

可以将处理异常的方法写在控制器类的基类中,则每个控制器都相当于有这个方法,就可以统一处理异常了!这个基类的声明必须使用public权限修饰,而并不可以使用默认的访问权限!另外,这个基类可以声明为抽象的!

另外,也可以将处理异常的类添加@ControllerAdvice注解,并在处理异常的方法之前添加@ResponseBody注解,或者,直接在类之前添加@RestControllerAdvice并在方法之前不添加@ResponseBody注解,就可以使得当前类是SpringMVC框架的全局化的类,当前类中处理异常的做法也是全局化的,所以,无论是哪个控制器类的方法处理异常时出现异常,都会由这个全局化类中的处理方式进行处理!注意:@ControllerAdvice/@RestControllerAdvice这2个注解在SpringMVC框架中默认不可用,必须另外配置,在SpringBoot框架中是可以直接使用的!

关于统一处理异常时,使用的@ExceptionHandler注解,还可以配置注解参数,例如配置为:

@ExceptionHandler(ServiceException.class)

则表示只有ServiceException及其子孙类异常才会被接下来的方法进行处理,而其它类型的异常是不被接下的方法处理的!

该注解有参数是数组类型的,也可以使用大括号{}框住多个异常的类型!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饭九钦vlog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值