Node 后台学习笔记-毕设篇(旅游信息管理平台)
-
使用技术栈:Node + ts + nestjs + typeorm + MySQL
-
简介:
- 这是款具有上传下载、发布文章、评论、用户个人中心 的一款 旅游资源管理项目。
- 可对数据库进行操作
-
项目链接: github.com/donwenx
一、实现 token 封装
新建一个 token 模块
nest g resource token
新建一个文件放 token 实体
// token.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class Token {
@PrimaryGeneratedColumn()
id: number;
@Column("int")
userId: number;
@Column("varchar")
token: string;
@Column("int")
state: number;
@Column("int")
loginTime: number;
@Column("int")
expire: number;
}
二、优化注册功能
前面实现的注册功能要优化一下功能
- 解决用户名
重复注册
在 Service
层中设置一个查找用户name
的方法
通过repository.findOneBy()
查找具有该 name 的实体
// user.service.ts
/**
* 通过name获取用户信息
* @param name 用户名
* @returns 用户信息
*/
async getUserByName(name: string) {
const user = await this.repository.findOneBy({ name })
return user
}
调用 service 中的根据 name 查找用户的方法,用户存在抛出错误
// user.controller.ts
@Post('/create')
async createUser(@Body() createUserDto: CreateUserDto) {
const user = await this.userService.getUserByName(createUserDto.name)
if (user !== null) { // 用户存在抛出错误
return '用户名重复'
}
const data = await this.userService.createUser(
createUserDto.name,
createUserDto.password
)
return user
}
三、登录功能实现
- 新建模块详情请看 第二章 -
生成新模块
- Node 后台学习笔记-毕设篇-第二章 请求处理
新建 token
模块用于存放用户登录过期时间信息
注册实体
// token.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class Token {
@PrimaryGeneratedColumn()
id: number;
@Column("int")
userId: number;
@Column("varchar")
token: string;
@Column("int")
state: number;
@Column("int")
loginTime: number;
@Column("int")
expire: number;
}
实现 service 逻辑
// token.service.ts
import { Injectable } from '@nestjs/common';
import { Token } from './token.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { createNonceStr, getTimeStamp } from 'src/util/function';
@Injectable()
export class TokenService {
@InjectRepository(Token)
private repository: Repository<Token> // 注入一个token实体
/**
* 根据用户的userId创建token
* @param userId 用户的userId
* @param expire 过期时间
* @returns token信息
*/
async createToken(userId: number, expire: number) {
const data = new Token()
data.userId = userId
data.loginTime = getTimeStamp()
data.expire = data.loginTime + expire
data.token = createNonceStr(32)
data.state = 1
await this.repository.save(data)
return data.token
}
}
在导出 token
// token.module.ts
import { Module } from "@nestjs/common";
import { TokenService } from "./token.service";
import { TokenController } from "./token.controller";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Token } from "./token.entity";
@Module({
imports: [TypeOrmModule.forFeature([Token])],
controllers: [TokenController],
providers: [TokenService],
// 导出 user 给其他的模块使用
exports: [TypeOrmModule.forFeature([Token])],
// 方案1:
// exports:[TypeOrmModule.forFeature([Token])]
})
export class TokenModule {}
在 user 模块中导入 token
// user.module.ts
import { Module } from "@nestjs/common";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./user.entity";
import { TokenService } from "src/token/token.service";
import { TokenModule } from "src/token/token.module";
import { Token } from "src/token/token.entity";
@Module({
// 方案2
// imports: [TypeOrmModule.forFeature([User, Token]), TokenModule],
imports: [TypeOrmModule.forFeature([User]), TokenModule],
controllers: [UserController],
providers: [UserService, TokenService],
exports: [UserService, TokenService],
})
export class UserModule {}
在user.controller.ts
注入 TokenService
将在后面登录接口中使用创建 token
// user.controller.ts
@Inject()
private readonly tokenService: TokenService;
在 user.service.ts
中实现一个检测密码方法
// user.service.ts
/**
* 检查用户密码是否正确
* @param name 用户名
* @param password 密码
* @returns 返回true或false
*/
async checkPassword(name: string, password: string) {
// 通过从数据库中查询用户,判断数据库中的密码和当前输入的密码一致
const user = await this.repository.findOneBy({ name })
if (user.password === password) {
return true
}
return false
}
正式实现登录接口,返回 token 和用户信息
// user.controller.ts
@Post('/login')
async login(@Body() loginDto: LoginDto) {
// 1. 拿到 id
// 在 user controller 中实现一个新的接口,登录接口`/login`
const user = await this.userService.getUserByName(loginDto.name)
// 2. 判断用户是否存在
if (user === null) {
return '用户不存在'
}
// 3. 使用 id + password 检查密码
const ok = await this.userService.checkPassword(user.name, loginDto.password)
// 判断密码是否正确
if (!ok) {
// 密码错误抛出异常
return '密码错误'
}
// 设置token
const token = await this.tokenService.createToken(user.id, 24 * 60 * 60)
const { password, ...data } = user // 移除密码
return { token, ...data }
}
测试
登录成功测试
登录失败测试
用户不存在测试