store商城项目二:用户登录功能
这里模拟实际项目开发中需求开发流程足迹,力求贴近实战。
1 需求调研与归档
需求属主:系统业务员(用户)提出需求,BA(产品线下调研,总结整理出需求文档)
需求背景:
由于系统业务功能的优化,使得系统的访问和使用量日益剧增,在有限账号的使用下已经不能满足客户的使用需要,同时为了避免个人用户记录以及数据的安全性,系统在原注册功能的基础上,实现用户的正常登入功能。
2 开发需求评审移交,确定开发功能。
2.1需求评审
需求评审参与者:BA(产品)、开发、测试、业务人员(不一定会有)。
需求评审会组织者:BA(产品)
需求会议内容:
- 确定当前版本的周期。
- 移交版本内需要实现的功能。
2.2 开发设计文档
根据需求,提取功能点。出具开发设计文档
设计文档内容:
功能设计的可行性分析报告
项目功能开发计划
设计说明书
数据表设计:新增表?新增字段?…
代码设计:实体类开发?业务方法设计、单元测试等…
技术运用
接口设计
验证方式(单元测试等)
2.3 测试相关 省略
3 登录功能设计
通过上一篇 注册功能的开发过程,我们大致可以了解流程如下:从后端–>前端的一个开发思路。(当然我们也可以采用 从前端–>后端的开发思路。)
设计一个功能的实现,首先不能盲目的去写代码。往往设计分析是最重要。有了设计思路,设计代码的过程是很简单的。
在现实开发中,我们应该花更多的时间去思考设计一个功能的实现需要一个怎样的过程。将复杂的需求逐步细化成一个个简单的功能。
这里只是提供一个思路,当然本身登录功能比较简单,这里只是提供一个过程,让大家熟悉开发的思路。实际项目的功能会更复杂。逻辑设计更多,表之间的关联性更多。
3-1 登录实现-持久层
1、规划sql
- 根据登录功能,我们了解到。前端输入用户名和密码后,我们需要查询该用户是否存在 查询语句
- 存在,在判断输入的密码是否与注册保持一致。(业务层判断)
select * from sys_user where username = '';
2、编写映射文件 UserMapper.xml
之前注册的时候已经存在查询的sql,直接复用。(但是分析的过程不能省略)
<?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 id="userColumn">
username,
password,
salt,
phone,
email,
gender,
avatar,
is_delete,
created_user,
created_time,
modified_user,
modified_time
</sql>
<select id="selectByUserName" parameterType="string" resultMap="userMap">
select
<include refid="userColumn"></include>
from t_user where username= #{username} and is_delete = 0
</select>
</mapper>
3、设计mapper接口 UserMapper
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-2 登录实现-业务层
1、异常规划
- 用户不存在异常:
UserNotFoundException
- 用户匹配的密码错误:
PasswordNotMatchException
package com.cy.store.service.ex;
import com.cy.store.common.ServiceException;
/**
* 用户的密码错误异常
*/
public class PasswordNotMatchException extends ServiceException {
public PasswordNotMatchException() {
super();
}
public PasswordNotMatchException(String message) {
super(message);
}
public PasswordNotMatchException(String message, Throwable cause) {
super(message, cause);
}
public PasswordNotMatchException(Throwable cause) {
super(cause);
}
protected PasswordNotMatchException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
2、业务层接口和实现类
在UserService
中定义接口的方法
package com.cy.store.service;
import com.cy.store.entity.UserEntity;
public interface UserService {
// 用户注册功能
void register(UserEntity userEntity);
// 用户查询功能
UserEntity login(String username,String password);
}
在UserServiceImpl
实现类中登录方法。
/**
* 登录功能
* @param username 用户名
* @param password 密码
* @return 当前登录的用户对象
*/
@Override
public UserEntity login(String username ,String password) {
// 对入参做非空判断
if (username == "" || username == null || password == "" || password == null ) {
throw new IllegalArgumentException("用户名或者密码不能为空!");
}
// 通过用户名查询用户,判断是否存在该用户
UserEntity result = userMapper.selectByUserName(username);
if (result == null) {
throw new UserNotFoundException("用户不存在");
}
// 通过数据库中的对象 获取当前对象的 加密密码 与 盐值
String queryPassword = result.getPassword();
String salt = result.getSalt();
// 通过加密方法对输入的密码+盐值。
String mdPassword = EncryptMD5.MD5PASSWORD(password, salt);
// 将加密的密码与数据库中的加密密码做判断
if (!mdPassword.equals(queryPassword)) {
throw new PasswordNotMatchException("用户密码错误。");
}
// 登入成功后,返回当前对象信息
UserEntity user = new UserEntity();
user.setUsername(username);
user.setModifiedUser(result.getModifiedUser());
user.setCreatedUser(result.getCreatedUser());
user.setModifiedTime(result.getModifiedTime());
user.setCreatedTime(result.getCreatedTime());
return user;
}
3-3 登录实现-控制层
1、异常统一处理类中,添加新的业务异常
package com.cy.store.common;
import com.cy.store.service.ex.*;
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 if (e instanceof PasswordNotMatchException) {
return JsonResult.fail(e.getMessage());
} else {
return JsonResult.fail(e.getMessage());
}
}
}
2、UserController
中编写登录的请求处理方法。
@PostMapping("/login")
public JsonResult<UserEntity> login(@RequestBody UserEntity userEntity){
String username = userEntity.getUsername();
String password = userEntity.getPassword();
UserEntity user = userService.login(username, password);
return JsonResult.ok(user,"请求处理成功!");
}
4 结果展示
展示1:
展示2: