前言
首先登陆界面首先还是基于session实现(我当时也很惊讶,怎么用这个😯,结果看了目录,后面就用Redis和Jwt令牌)可能就是一个过渡吧,先用Session实现一遍,然后才能突出后面的很厉害。其实里面还是有很多小知识点的(Mybatis-plus)很值得学习,这个Session实现就当小配角,陪跑🤪我们学。
先来看看登陆界面的代码实现:
UserController类 :
java
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private IUserService userService;
/**
* 发送手机验证码
*/
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
// TODO 发送短信验证码并保存验证码
return userService.sendCode(phone, session);
}
/**
* 登录功能
* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
// TODO 实现登录功能
return userService.login(loginForm, session);
}
IUserService 接口:
java
package com.hmdp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;
import javax.servlet.http.HttpSession;
/**
* <p>
* 服务类
* </p>
*
* @author 虎哥
* @since 2021-12-22
*/
public interface IUserService extends IService<User> {
Result sendCode(String phone, HttpSession session);
Result login(LoginFormDTO loginForm, HttpSession session);
}
IUserService 接口实现类:
java
package com.hmdp.service.impl;
import...
/**
* <p>
* 服务实现类
* </p>
*
* @author 虎哥
* @since 2021-12-22
*/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result sendCode(String phone, HttpSession session) {
//1.校验手机号
if (RegexUtils.isPhoneInvalid(phone)) {
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误!");
}
//3.符合,生成验证码
String code = RandomUtil.randomNumbers(6);
//4.保存验证码到session
session.setAttribute("code", code);
//5.发送验证码
System.out.println("发送短信验证码成功,验证码:" + code);
//5.返回ok
return Result.ok();
}
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//1.校验手机号
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误!");
}
//2.校验验证码 不一致直接报错 从session中获取
Object cachecode = session.getAttribute("code");
String code = loginForm.getCode();
if (cachecode == null || !cachecode.toString().equals(code)) {
return Result.fail("验证码错误!");
}
//3.一致,根据手机号查询用户 select * from tb_user where phone = ?
User user = query().eq("phone", phone).one();
//4.判断用户是否存在
if (user == null) {
//5.不存在,创建新用户
user = createUserWithPhone(phone);
}
//6.存在,保存用户信息到session
session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
return Result.ok();
}
private User createUserWithPhone(String phone) {
//1.创建用户
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
//2.保存用户
save(user);
return user;
}
}
就是很常见的基于session存储来实现的登陆界面,以及Interceptor拦截器,很简单,之前写苍穹外卖的时候都写过了,所以只总结一些新的东西。
我们在保存用户信息和查找用户验证码的时候,并没有像之前一样新建一个Mapper类用SQL语句查找,只是用到了save方法和query方法,这就用到了Mybatis-plus
🌟 MyBatis-Plus 是什么?
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
官方文档: https://baomidou.com
🙌登陆界面这里主要用到了MyBatis Plus的Service查询
通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行、remove删除、list 查询集合、page查询分页
使用步骤:
搭建项目:
- 引入依赖:
xml
<!--mybatis plus场景-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
- 启动类
java
package com.hmdp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.hmdp.mapper")
@SpringBootApplication
public class HmDianPingApplication {
public static void main(String[] args) {
SpringApplication.run(HmDianPingApplication.class, args);
}
}
- application.yml
xml
server:
port: 8081
spring:
application:
name: hmdp
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/hmdp?useSSL=false&serverTimezone=UTC
username: root
password: 1234
redis:
host: localhost
port: 6379
# password: 123456
lettuce:
pool:
max-active: 10
max-idle: 10
min-idle: 1
time-between-eviction-runs: 10s
jackson:
default-property-inclusion: non_null # JSON处理时忽略非空字段
mybatis-plus:
type-aliases-package: com.hmdp.entity # 别名扫描包
logging:
level:
com.hmdp: debug
- 定义一个UserService接口继承与MyBatisPlus提供的IService接口:
java
在这里插入代码片package com.hmdp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;
import javax.servlet.http.HttpSession;
/**
* <p>
* 服务类
* </p>
*
* @author 虎哥
* @since 2021-12-22
*/
public interface IUserService extends IService<User> {
Result sendCode(String phone, HttpSession session);
Result login(LoginFormDTO loginForm, HttpSession session);
}
- 定义一个UserService的实现类,并且继承与MyBatisPlus提供的ServiceImpl:
java
package com.hmdp.service.impl;
import...
/**
* <p>
* 服务实现类
* </p>
*
* @author 虎哥
* @since 2021-12-22
*/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result sendCode(String phone, HttpSession session) {
//1.校验手机号
if (RegexUtils.isPhoneInvalid(phone)) {
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误!");
}
//3.符合,生成验证码
String code = RandomUtil.randomNumbers(6);
//4.保存验证码到session
// session.setAttribute("code", code);
//4.保存验证码到redis
stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
//5.发送验证码
System.out.println("发送短信验证码成功,验证码:" + code);
//5.返回ok
return Result.ok();
}
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//1.校验手机号
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误!");
}
//2.校验验证码 不一致直接报错 从session中获取
// Object cachecode = session.getAttribute("code");
// String code = loginForm.getCode();
// if (cachecode == null || !cachecode.toString().equals(code)) {
// return Result.fail("验证码错误!");
// }
//2.校验验证码 从redis中获取 不一致直接报错
String cachecode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
String code = loginForm.getCode();
if (cachecode == null || !cachecode.toString().equals(code)) {
return Result.fail("验证码错误!");
}
//3.一致,根据手机号查询用户 select * from tb_user where phone = ?
User user = query().eq("phone", phone).one();
//4.判断用户是否存在
if (user == null) {
//5.不存在,创建新用户
user = createUserWithPhone(phone);
}
//6.存在,保存用户信息到session
// session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
//6.存在,保存用户信息到redis
//6.1 随机生成token,作为登录令牌
String token = UUID.randomUUID().toString(true);
//6.2 将用户对象转为hash存储
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue( true)
.setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
//6.3 存储
String tokenkey = LOGIN_USER_KEY + token;
stringRedisTemplate.opsForHash().putAll( tokenkey, userMap);
//6.4设置token有效期
stringRedisTemplate.expire( tokenkey, LOGIN_USER_TTL, TimeUnit.MINUTES);
//6.4返回token
return Result.ok(token);
}
private User createUserWithPhone(String phone) {
//1.创建用户
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
//2.保存用户
save(user);
return user;
}
}
把数据库代码提取出来
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
public Result login(LoginFormDTO loginForm, HttpSession session) {
...
User user = query().eq("phone", phone).one(); // ← 这里调用了 query()
save(user); // ← 这里调用了 save()
}
private User createUserWithPhone(String phone) {
User user = new User();
user.setPhone(phone);
user.setNickName("user_" + RandomUtil.randomString(10));
save(user); // ← 保存用户
return user;
}
}
📚 一、save() 方法
来源:ServiceImpl 类
java
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
@Autowired
protected M baseMapper; // 自动注入你的 UserMapper
@Override
public boolean save(T entity) {
return baseMapper.insert(entity) == 1; // 调用 BaseMapper 的 insert 方法
}
}
所以:
java
save(user);
实际上就是
java
this.baseMapper.insert(user); // 通过 UserMapper 执行 SQL 插入
sql
save(user)
↓
ServiceImpl.save(user)
↓
baseMapper.insert(user) ←← 这才是真正的数据库操作
↓
UserMapper.insert(user) (由 MyBatis 执行 SQL)
🔍 三、query().eq("phone", phone).one() 又是怎么回事?
这是一套 链式编程 API,也是 MyBatis-Plus 的高级功能。
java
query().eq("phone", phone).one();
等价于
java
new LambdaQueryChainWrapper<User>(baseMapper)
.eq(User::getPhone, phone)
.one();
它做了什么?
- query() → 创建一个查询构造器(内部使用 baseMapper)
- .eq("phone", phone) → 添加条件:phone = ?
- .one() → 执行查询,返回一条记录(相当于 SELECT * FROM tb_user WHERE phone = ? LIMIT 1)
sql
query().eq("phone", phone).one()
↓
new LambdaQueryChainWrapper<>(baseMapper)
↓
wrapper.eq("phone", phone)
↓
wrapper.one() → baseMapper.selectOne(wrapper)
↓
执行 SQL:SELECT * FROM tb_user WHERE phone = ? LIMIT 1
小白啊!!!写的不好轻喷啊🤯如果觉得写的不好,点个赞吧🤪(批评是我写作的动力)
...。。。。。。。。。。。...
...。。。。。。。。。。。...