NestJS用户模块CRUD和分页实现
之前我们已经搭建好项目并且链接上了数据库,接下来我们就将user用户模块的CRUD操作给完善一下
模块搭建
👉搭建下面这个文件目录,准备放置用户表相关的操作规则
javascript
src\modules\user\dto
👉生成用户相关模块
javascript
// 完整命令
nest generate module modules/user --no-spec
nest generate controller modules/user --no-spec
nest generate service modules/user --no-spec
👉搭建用户表对应的实体user.entity.ts
这个时候我们数据库里面还没有任何的用户数据,搭建用户数据表以后增加用户对应的实体表信息
这部分主要就从typeorm引入对应的Entity(实体)PrimaryGeneratedColumn(主键)和Column(列表)即可,这样子稍后就可以跟我们实体表一一对应
javascript
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('sys_user')
export class User {
@PrimaryGeneratedColumn({ name: 'user_id' }) // 设置主键列
userId: number;
@Column({ nullable: true, name: 'username', comment: '用户的登录账号' })
username: string;
@Column({ length: 255, charset: 'utf8mb3', default: '123456', comment: '用户的登录密码' })
password: string;
@Column({ nullable: true, length: 255, charset: 'utf8mb3', comment: '姓名' })
name: string;
@Column({ nullable: true, length: 255, charset: 'utf8mb3', comment: '年龄' })
age: string;
@Column({ nullable: true, type: 'int', comment: '用户性别 1男 2女' })
sex: number;
@Column({ nullable: true, type: 'datetime', comment: '创建时间' })
createTime: Date;
@Column({ nullable: true, length: 255, charset: 'utf8mb3', comment: '用户的地址' })
address: string;
@Column({ nullable: true, type: 'tinyint', comment: '1 正常 0 2 禁用' })
state: number;
@Column({ nullable: true, length: 255, charset: 'utf8mb3', comment: '手机号' })
phone: string;
@Column({ nullable: true, length: 255, charset: 'utf8mb3', comment: '头像地址' })
avatar: string;
disease: string;
}
👉app.module.ts引入
在根目录之中引入了用户模块,上面的命令会帮我们自动生成
javascript
import { UserModule } from './modules/user/user.module';
@Module({
imports: [
TypeOrmModule.forRoot(typeOrmConfig),
UserModule, //用户模块
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
👉搭建user.module.ts
这部分主要帮助我们合拢整个user模块
javascript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
imports: [TypeOrmModule.forFeature([User])], // 导入实体
controllers: [UserController], // 注册控制器
providers: [UserService], // 注册服务
})
export class UserModule {}
👉搭建user.controller.ts
user.controller.ts主要控制用户的接口,并且返回用户操作方法,这部分写入userService.ts之中
javascript
import { Controller, Get, Post, Param, Body, Put, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
// 定义接口,支持泛型
interface AppServiceResponse<T> {
code: number;
data?: T;
message: string;
}
// 返回数据格式
interface UserResponse<T> {
code: number;
data?: T;
message: string;
}
// 创建用户
class CreateUserDTO {
readonly _id: string;
readonly user_name: string;
readonly password: string;
}
@Controller()
export class UserController {
constructor(private readonly userService: UserService) {}
}
👉搭建userService.ts
javascript
import { Injectable ,ConflictException,HttpException, HttpStatus} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import * as bcrypt from 'bcryptjs';
interface ResponseData<T> {
data: T;// data 是泛型 T 类型,这里 T 会是 User
message: string; // message 是字符串类型
code: number; // code 是数字类型
}
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
}
新增用户
基础的结构搭建好了以后,接下来我们先从新增开始做起,写用户的新增模块
👉user.controller.ts
增加对应的用户新增接口时调用的方法addOne
typescript
// 新增用户
@Post('/system/user')
async addOne(
@Body('username') username: string,
@Body('password') password: string,
) {
return this.userService.addOne(username, password);
}
👉user.service.ts
写好新增用户时候的方法addOne
javascript
// 增加用户服务(User Service)
async addOne(username: string,password:string){
const user = this.userRepository.create({ username,password}); // 创建一个新用户
return this.userRepository.save(user);
}
👉测试新增接口
这个时候我们测试已经有数据了,调用[http://localhost:8888/api/system/user](http://localhost:8888/api/system/user)
的post方式
javascript
{
"userId": 8,
"username": "111",
"password": "111",
"name": null,
"age": null,
"sex": null,
"createTime": null,
"address": null,
"state": null,
"phone": null,
"avatar": null,
"updateTime": null,
"userHeight": null,
"userWeight": null,
"disease": null
}
上面数据还比较简单,所以我们要根据自己的需求规范一下返回的数据格式,完善一下,新增ok
javascript
return {
message: '添加成功!',
code: 200,
};
查询用户
👉user.controller.ts
增加对应的用户查询接口时调用findAll
typescript
// 获取所有用户
@Get('/system/user')
async getAll(){
return this.userService.getAll();
}
👉user.service.ts
javascript
async getAll(){
// 调用userRepository的find方法,返回一个User数组
const user = this.userRepository.find();
return {
message: '查询成功!',
code: 200,
data: user, // 返回查询到的用户数据
};
}
👉测试接口
这个时候我们测试返回如下
javascript
{
"message": "查询成功!",
"code": 200,
"data": {}
}
这里面的data是空的,这是因为我们没有规范好对应的数据,这里我们规范一下想要返回的数据格式
javascript
export interface ResponseDto<T> {
code: number;
message?: string;
data: T;
total?: number;
}
//重新定义一下服务返回的数据在user.service.ts中
async getAll(): Promise<ResponseDto<User[]>>{
// 调用userRepository的find方法,返回一个User数组
const user = await this.userRepository.find();
return {
message: '查询成功!',
code: 200,
data: user, // 返回查询到的用户数据
total: user.length,
};
}
再次查询数据正常
javascript
{
"message": "查询成功!",
"code": 200,
"data": [
{
"userId": 1,
"username": "XXX",
"password": "XXX",
"name": "11111",
"age": "18",
"sex": 1,
"createTime": null,
"address": "11111",
"state": null,
"phone": "123456",
"avatar": null,
"updateTime": null,
"userHeight": null,
"userWeight": null,
"disease": null
},
.....
],
"total": 12
}
查询用户信息
👉user.controller.ts
javascript
// 查询单个用户
@Get('/system/user/:id')
async getOne(@Param('id') id: string){
return this.userService.getOne(id);
}
👉user.service.ts
javascript
async getOne(id){
try {
// 查询用户,确保 id 与数据库字段类型一致
const user = await this.userRepository.findOne({ where: { userId: id } });
// 如果找不到用户,返回 404 错误
if (!user) {
return {
code: 404,
message: '用户不存在',
data: null, // 当用户不存在时,data 返回 null
};
}
// 如果找到用户,返回 200 和用户数据
return {
code: 200,
message: '查询成功',
data: user, // 返回找到的用户数据
};
} catch (error) {
// 捕获并返回数据库查询中的异常
return {
code: 500,
message: '服务器内部错误',
data: null,
};
}
}
👉测试接口
javascript
{
"code": 200,
"message": "查询成功",
"data": {
"userId": 1,
"username": "12",
"password": "XXX",
"name": "XXX",
"age": "18",
}
}
修改提交用户信息
👉user.controller.ts
javascript
// 更新用户
@Put('/system/user')
async updateUser(
// @Param('id') id: number,
@Body('id') id: number,
@Body('username') username: string,
@Body('password') password: string,
@Body('name') name: string,
@Body('age') age: number,
@Body('address') address: number,
) {
return this.userService.updateUser(id, username, password, name,age,address);
}
👉user.service.ts
javascript
// 修改
async updateUser(id,username, password,name,age,address){
const user = await this.userRepository.findOne({ where: { userId:id } });
if (!user) {
return {
code: 404,
message: '用户不存在!',
data: null,
};
}
user.username = username;
user.name = name;
user.password = password;
user.age = age;
user.address = address;
const updatedUser = await this.userRepository.save(user);
return {
code: 200,
message: '更新成功',
// data: updatedUser,
};
}
👉测试接口
javascript
{
"code": 200,
"message": "更新成功",
}
删除用户信息
👉user.controller.ts
javascript
// 删除用户
@Delete('/system/user/:id')
async deleteUser(@Param('id') id: number){
return this.userService.deleteUser(id);
}
👉user.service.ts
javascript
// 删除
async deleteUser(id: number){
const delUser = await this.userRepository.delete(id);
if(delUser){
return {
code: 200,
message: '删除成功',
// data: updatedUser,
};
}
return {
code: 401,
message: '删除失败',
// data: updatedUser,
};
}
👉测试接口
javascript
{
"code": 200,
"message": "删除成功",
}
分页信息
👉user.controller.ts
javascript
import { Query} from '@nestjs/common';
// 获取所有用户分页
@Get('/system/user')
async getAll(
@Query('pageNum') pageNum = 1, // 默认第1页
@Query('pageSize') pageSize= 10, // 默认每页10条
) {
return this.userService.getAll(pageNum, pageSize);
}
👉user.service.ts
javascript
// 查询所有用户-User Service
async getAll(pageNum: number = 1, pageSize: number = 10): Promise<ResponseDto<User[]>>{
// 设置分页的偏移量和限制
// const skip = (pageNum - 1) * pageSize // 偏移量 (页码从 1 开始)
// 使用 find 方法查询分页数据,skip 和 take 分别控制数据的偏移和返回数量
const [users, total] = await this.userRepository.findAndCount({
skip:(pageNum - 1) * pageSize, // 偏移量
take: pageSize, // 每页返回的数据数量
});
const totalPages = Math.ceil(total / pageSize);
// 调用userRepository的find方法,返回一个User数组
const user = await this.userRepository.find();
return {
message: '查询成功!',
code: 200,
data: users, // 返回查询到的用户数据
total: total,
};
}
👉测试接口
这个时候我们的分页参数和返回的数据格式也已经正确了
javascript
{
"message": "查询成功!",
"code": 200,
"data": [
{
"userId": 1,
"username": "123456",
"password": "11111111",
"name": "11111",
"age": "18",
"sex": 1,
"createTime": null,
"address": "11111",
"state": null,
"phone": "123456",
"avatar": null,
"updateTime": null,
"userHeight": null,
"userWeight": null,
"disease": null
},
],
"total": 11
}