环境配置准备
数据库准备
用户表:
配置文件准备
项目实施
与数据库对应就要有对应的实体类表,如下:
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
@NotNull
private Integer id;//主键ID
private String username;//用户名
@JsonIgnore//josn字符串中 不包含该字段
private String password;//密码
@NotEmpty
@Pattern(regexp = "^\\S{1,10}$")
private String nickname;//昵称
@NotEmpty
@Email
private String email;//邮箱
/*
* 下面三个开启的是驼峰明明*/
private String userPic;//用户头像地址
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}
无脑三层结构补全。从我的角度出发,我喜欢我需要什么功能,我再加对应的。
我需要一个注册功能,那么控制层就要做出反应,调用业务层的代码,继而通过持久层与数据库进行交互。
再确定一下请求路径和对应参数。注册的时候为了方便快一点,填写用户名和密码就行了,剩余的后面自己玩去。
请求路径设置为/user/register,对应参数根据上述分析为username&password。
接下来就是简单的三层编程了。
控制层:
@RestController
@RequestMapping("/user")
@Validated
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result register(@Pattern(regexp = "^\\S{5,16}$") String username,
@Pattern(regexp = "^\\S{5,16}$") String password) {
//查询用户,需要调用Service去查数据库
User u = userService.findByUserName(username);
if (u == null) {
//注册
userService.register(username, password);
return Result.success();
} else {
//占用
return Result.error("用户名已被占用");
}
}
@PostMapping("/login")
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username,
@Pattern(regexp = "^\\S{5,16}$") String password) {
User loginUser = userService.findByUserName(username);
if (loginUser == null)
return Result.error("用户名错误");
String md5Password = Md5Util.getMD5String(password);
if (loginUser.getPassword().equals(md5Password)) {
//密码比对成功
Map<String, Object> claims = new HashMap<>();
claims.put("id", loginUser.getId());
claims.put("username", loginUser.getUsername());
String token = JwtUtil.genToken(claims);
return Result.success(token);
} else
return Result.error("密码错误");
}
@GetMapping("/userInfo")
public Result<User> userInfo() {
//根据用户名去查询用户
Map<String, Object> map = ThreadLocalUtil.get();
String username = (String) map.get("username");
User user = userService.findByUserName(username);
return Result.success(user);
}
@PutMapping("/update")
public Result update(@RequestBody @Validated User user) {
userService.update(user);
return Result.success();
}
@PatchMapping("/updateaAvatar")
public Result updateAvatar(@PathVariable @URL String avatarUrl) {
userService.updateAvatar(avatarUrl);
return Result.success();
}
@PatchMapping("/updatePwd")
public Result updatePwd(@RequestBody Map<String, String> params) {
//校验参数,validation满足不了要求
String oldPwd = params.get("old_pwd");
String newPwd = params.get("new_pwd");
String rePwd = params.get("re_pwd");
if (!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd) || !StringUtils.hasLength(rePwd))
return Result.error("缺少必要的参数");
//校验原密码是否正确
Map<String, Object> map = ThreadLocalUtil.get();
String username = (String) map.get("username");
User loginUser = userService.findByUserName(username);
String md5Password = Md5Util.getMD5String(oldPwd);
if (!md5Password.equals(loginUser.getPassword()))
return Result.error("原密码错误");
if (!rePwd.equals(newPwd))
return Result.error("两次填写的新密码不一样");
userService.updatePwd(newPwd);
return Result.success();
}
}
业务层接口(为了规范一丢丢)
public interface UserService {
//根据用户名查询用户
User findByUserName(String username);
//注册
void register(String username, String password);
//更新
void update(User user);
//更新头像
void updateAvatar(String avatarUrl);
//重置密码
void updatePwd(String newPwd);
}
业务层实现类
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findByUserName(String username) {
User user = userMapper.findByUserName(username);
return user;
}
@Override
public void register(String username, String password) {
//加密处理
String md5String = Md5Util.getMD5String(password);
//添加
userMapper.add(username, md5String);
}
@Override
public void update(User user) {
user.setUpdateTime(LocalDateTime.now());
userMapper.update(user);
}
@Override
public void updateAvatar(String avatarUrl) {
Map<String, Object> map = ThreadLocalUtil.get();
Integer id = (Integer) map.get("id");
userMapper.updateAvatar(avatarUrl, id);
}
@Override
public void updatePwd(String newPwd) {
Map<String, Object> map = ThreadLocalUtil.get();
Integer id = (Integer) map.get("id");
userMapper.updatePwd(Md5Util.getMD5String(newPwd), id);
}
}
持久层实现:
@Mapper
public interface UserMapper {
//根据用户名查询用户
@Select("select * from user where username=#{usernmae}")
User findByUserName(String username);
//添加
@Insert("insert into user(username,password,create_time,update_time)" +
"values(#{username},#{password},now(),now())")
void add(String username, String password);
@Update("update user set nickname=#{nickname},email=#{email},update_time=#{updateTime} where id=#{id}")
void update(User user);
//更新头像,updatetime用的是now
@Update("update user set user_pic=#{avatarUrl},update_time=now() where id=#{id}")
void updateAvatar(String avatarUrl, Integer id);
//更新密码
@Update("update user set password=#{newPwd},update_time=now() where id=#{id}")
void updatePwd(String newPwd, Integer id);
}
好了,主要代码全部给出。接下来写一下我当时写的思路。
注册
注册用户,注册的时候我们会填啥,用户名+密码,其余的拜拜,一下子让我填那么多,我不注册了。用户提交表单后,前后端都需要进行检查(前端还没写,哈哈),后端首先检查数据库中是否有该用户,有的话咱就别注册了,别浪费资源;没有就注册,在数据库中插入该用户。那么对应的请求应该为post请求,请求路径上面写了。同时,为了保证数据的保密性,再插入数据库时采用md5加密方式,那么对其处理的业务就应该书写在业务层(上面写了md5),然后再传给持久层。
持久层接收到用户名和加密后的密码后,采用注解(别去写啥配置bean id了,累死了),直接插入即可。
登录
用户登录,输入用户名+密码。前端只给了我usename+password,我需要根据用户名(保证唯一)去数据库查询用户和密码。如果用户不存在,拜拜;存在,检查密码。但是此处的密码是加密后的,所以将用户输入的密码也加密,两者比对,比对成功将用户id和用户名放到jwt令牌中去,方便后序操作,不然每次都要我登录,有毛病;不要我登陆的话,那我的东西不都没了。
更新
我登陆进来了,我看到你有头像,笔名,邮件等全关联了,我羡慕了,我也要。我就填上去。
更新为Put请求,路径见上。也不可能说你每一个参数我都给你写在控制层的方法参数里,那我不得累死,直接调用@Requestbody将前端返回的josn数据转换为实体类对象,从该对象中去取值和更新。另一个validated注解先不管,那就是一个管理参数的。还有一个是数据库中有一个更新时间,前端是没给我的,自己在业务层调用一下LocalDateTime.now(),动动手指的事。最后就是简单的书写sql语句了。
更新头像同理,不写了,本孤独累了。
更新密码可以一写:三个密码有一个为空,不行,拜拜;两次新密码不对,拜拜;你只输入了密码,我哪知道你是谁,调用线程ThreadLocal获取当前对象,去比对密码,成功之后再更新。