这节继续实现下修改密码、修改个人信息的接口:
对应这两个页面:
不过在修改信息之前,需要先实现查询用户信息的接口,用来回显数据。
在 UserController 添加一个 /user/info 接口:
@Get('info')
@RequireLogin()
async info(@UserInfo('userId') userId: number) {
return this.userService.findUserDetailById(userId);
}
加上 @RequireLogin 装饰,这样 LoginGuard 就会对 /user/info 的请求做登录检查,并把 user 信息放到 request 上。
然后用前面封装的自定义装饰器 @UserInfo 从 reqeust.user 取 userId 注入 handler。
在 UserService 实现 findUserDetailById 方法:
async findUserDetailById(userId: number) {
const user = await this.prismaService.user.findUnique({
where: {
id: userId
},
select: {
id: true,
username: true,
nickName: true,
email: true,
headPic: true,
createTime: true
}
});
return user;
}
测试下:
登录,拿到 token。
然后带上 token 访问 /user/info
成功拿到了 user 的信息。
然后实现修改密码的接口:
@Post('update_password')
async updatePassword(@Body() passwordDto: UpdateUserPasswordDto) {
console.log(passwordDto);
return 'success';
}
创建 src/user/dto/update-user-password.dto.ts
import { IsEmail, IsNotEmpty, MinLength } from "class-validator";
export class UpdateUserPasswordDto {
@IsNotEmpty({
message: '密码不能为空'
})
@MinLength(6, {
message: '密码不能少于 6 位'
})
password: string;
@IsNotEmpty({
message: '邮箱不能为空'
})
@IsEmail({}, {
message: '不是合法的邮箱格式'
})
email: string;
@IsNotEmpty({
message: '用户名不能为空'
})
username: string;
@IsNotEmpty({
message: '验证码不能为空'
})
captcha: string;
}
需要传的是用户名、邮箱、密码、验证码。
确认密码在前端和密码对比就行,不需要传到后端。
测试下:
然后实现下具体的更新密码的逻辑:
在 UserController 里调用 UserService 的方法:
@Post('update_password')
async updatePassword(@Body() passwordDto: UpdateUserPasswordDto) {
return this.userService.updatePassword(passwordDto);
}
UserService 实现具体的逻辑:
async updatePassword(passwordDto: UpdateUserPasswordDto) {
const captcha = await this.redisService.get(`update_password_captcha_${passwordDto.email}`);
if(!captcha) {
throw new HttpException('验证码已失效', HttpStatus.BAD_REQUEST);
}
if(passwordDto.captcha !== captcha) {
throw new HttpException('验证码不正确', HttpStatus.BAD_REQUEST);
}
const foundUser = await this.prismaService.user.findUnique({
where: {
username: passwordDto.username
}
});
foundUser.password = passwordDto.password;
try {
await this.prismaService.user.update({
where: {
id: foundUser.id
},
data: foundUser
});
return '密码修改成功';
} catch(e) {
this.logger.error(e, UserService);
return '密码修改失败';
}
}
先查询 redis 中相对应的验证码,检查通过之后根据 email 查询用户信息,修改密码之后 save。
测试下:
在 redis 里手动添加 update_password_captcha_邮箱 的 key,值为 111111
半小时过期。
注意,我们现在连的是 db2
然后再试下:
修改成功。
我们为了开发方便没对密码做加密,可以直观看出来密码修改成功了:
然后再加上发送邮箱验证码的接口:
@Get('update_password/captcha')
async updatePasswordCaptcha(@Query('address') address: string) {
if(!address) {
throw new BadRequestException('邮箱地址不能为空');
}
const code = Math.random().toString().slice(2,8);
await this.redisService.set(`update_password_captcha_${address}`, code, 10 * 60);
await this.emailService.sendMail({
to: address,
subject: '更改密码验证码',
html: `<p>你的更改密码验证码是 ${code}</p>`
});
return '发送成功';
}
测试下:
然后再来写下修改个人信息的接口:
回显数据的接口就用 /user/info 这个。
然后加一个 /user/update 接口:
@Post('update')
@RequireLogin()
async update(@UserInfo('userId') userId: number, @Body() updateUserDto: UpdateUserDto) {
return await this.userService.update(userId, updateUserDto);
}
创建 src/user/dto/udpate-user.dto.ts
import { IsEmail, IsNotEmpty } from "class-validator";
export class UpdateUserDto {
headPic: string;
nickName: string;
@IsNotEmpty({
message: '邮箱不能为空'
})
@IsEmail({}, {
message: '不是合法的邮箱格式'
})
email: string;
@IsNotEmpty({
message: '验证码不能为空'
})
captcha: string;
}
headPic 和 nickName 不用加非空约束,可以不传。
然后在 UserService 实现 update 方法:
async update(userId: number, updateUserDto: UpdateUserDto) {
const captcha = await this.redisService.get(`update_user_captcha_${updateUserDto.email}`);
if(!captcha) {
throw new HttpException('验证码已失效', HttpStatus.BAD_REQUEST);
}
if(updateUserDto.captcha !== captcha) {
throw new HttpException('验证码不正确', HttpStatus.BAD_REQUEST);
}
const foundUser = await this.prismaService.user.findUnique({
where: {
id: userId
}
});
if(updateUserDto.nickName) {
foundUser.nickName = updateUserDto.nickName;
}
if(updateUserDto.headPic) {
foundUser.headPic = updateUserDto.headPic;
}
try {
await this.prismaService.user.update({
where: {
id: userId
},
data: foundUser
})
return '用户信息修改成功';
} catch(e) {
this.logger.error(e, UserService);
return '用户信息修改成功';
}
}
根据 userId 查询用户,修改信息后 update 到数据库。
这里需要用到验证码,我们加一个发送验证码的接口:
@Get('update/captcha')
async updateCaptcha(@Query('address') address: string) {
if(!address) {
throw new BadRequestException('邮箱地址不能为空');
}
const code = Math.random().toString().slice(2,8);
await this.redisService.set(`update_user_captcha_${address}`, code, 10 * 60);
await this.emailService.sendMail({
to: address,
subject: '更改用户信息验证码',
html: `<p>你的验证码是 ${code}</p>`
});
return '发送成功';
}
测试下:
首先拿到验证码:
登录账号拿到 token:
带上 token 访问 /user/update 接口:
更改成功。
查询下:
确实修改了。
代码在小册仓库。
总结
这节我们实现了修改密码、修改用户信息的接口。
分别实现了 /user/info 用于回显用户信息,/user/update_password 用于修改密码,/user/update 用于修改用户信息。
以及 /user/update/captcha 和 /user/update_password/captcha 用于发送验证码。
其中 /user/info 和 /user/update 接口需要登录,然后通过之前封装的 @UserInfo 装饰器从 request.user 取用户信息。
至此,用户模块的功能就完成了。