项目初衷
统一管理不同应用之间 用户、角色、权限管理。减少后续多应用开发权限管理问题。
创建项目并初始化
npm i -g @nestjs/cli
nest new user-app
连接 mysql 数据库
使用 @nestjs/config 包管理数据库相关配置信息
npm i --save @nestjs/config
根目录下创建 .env 文件 写入相关配置
安装连接数据库 相关依赖
npm install --save @nestjs/typeorm typeorm mysql2
相关代码
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigService, ConfigModule } from '@nestjs/config';
import envConfig from '../config/env';
@Module({
imports: [
// 项目配置文件
ConfigModule.forRoot({
isGlobal: true,
envFilePath: [envConfig.path],
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
return {
type: 'mysql', // 数据库类型
autoLoadEntities: true, //
host: configService.get('DB_HOST', localhost), // 主机,默认为localhost
port: configService.get<number>('DB_PORT', 3306), // 端口号
username: configService.get('DB_USER', 'root'), // 用户名
password: configService.get('DB_PASSWORD', '123456'), // 密码
database: configService.get('DB_DATABASE', 'user_system'), //数据库名
timezone: '+08:00', //服务器上配置的时区
synchronize: true, //根据实体自动创建数据库表,修改字段类型时会清除原有数据, 生产环境建议关闭
};
},
}),
],
})
export class AppModule {}
注意: synchronize: true, 设置为 true时 改变 修改实体时对应的数据库表也会被修改 。
# 会根据 实体结构以及关系自动创建表
autoLoadEntities: true,
synchronize: true,
业务开发
数据库表 ER 图
应用权限相关接口开发示例
一般情况下 创建一个资源包含 如下几个文件。然后编写CRUD 代码
permissions.controller.ts
import { Controller, Get, Post, Body, Query, UseGuards } from '@nestjs/common';
import { PermissionsService } from './permissions.service';
import {
CreatePermissionDto,
UpdatePermissionDto,
SearchPermissionDto,
} from './dto/permission';
import { IsNotEmpty} from 'class-validator';
@Controller('permission')
export class PermissionsController {
constructor(private readonly permissionsService: PermissionsService) {}
@Post('create')
create(@Body() createPermissionDto: CreatePermissionDto) {
return this.permissionsService.create(createPermissionDto);
}
@Get('list')
findAll() {
return this.permissionsService.list();
}
@Post('page')
getPage(@Body() body: SearchPermissionDto) {
return this.permissionsService.getPage(body);
}
@Post('update')
update(@Body() updatePermissionDto: UpdatePermissionDto) {
return this.permissionsService.update(updatePermissionDto);
}
@Get('detail')
@IsNotEmpty()
findOne(@Query('id') id: string) {
return this.permissionsService.findOne(+id);
}
@Get('remove')
@IsNotEmpty()
remove(@Query('id') id: string) {
return this.permissionsService.remove(+id);
}
}
permissions.service.ts
import {
Injectable,
HttpException,
HttpStatus,
Inject,
forwardRef,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import {
CreatePermissionDto,
UpdatePermissionDto,
SearchPermissionDto,
} from './dto/permission';
import Permission from './entities/permission.entity';
import { RolesService } from 'src/roles/roles.service';
import Role from 'src/roles/entities/role.entity';
@Injectable()
export class PermissionsService {
constructor(
@InjectRepository(Permission)
private readonly permissionRepository: Repository<Permission>,
// Inject forwardRef 解决循环依赖问题
@Inject(forwardRef(() => RolesService))
private rolesService: RolesService,
) {}
async getSubmitDto(data: CreatePermissionDto | UpdatePermissionDto) {
const { roleIds = [], ...reset } = data;
let roles: Role[] = [];
if (roleIds?.length > 0) {
roles = await this.rolesService.findByIds(roleIds);
}
const param = {
...reset,
roles,
};
return param;
}
/** 创建权限 */
async create(createPermissionDto: CreatePermissionDto) {
const { name } = createPermissionDto;
const existPermission = await this.permissionRepository.findOne({
where: { name },
});
if (existPermission) {
throw new HttpException('角色已存在', HttpStatus.BAD_REQUEST);
}
const param = await this.getSubmitDto(createPermissionDto);
const newPermission = this.permissionRepository.create(param);
return await this.permissionRepository.save(newPermission);
}
/** 查询 */
async list() {
const qb = this.permissionRepository.createQueryBuilder('permissions');
const result = await qb.getMany();
return result;
}
/** 分页查询 */
async getPage(body: SearchPermissionDto) {
const { pageSize = 10, pageIndex = 1 } = body ?? {};
const qb = this.permissionRepository.createQueryBuilder('permissions');
qb.skip(pageSize * (pageIndex - 1)).take(pageSize);
const total = await qb.getCount();
const result = await qb.getMany();
return { total, list: result };
}
async findOne(id: number) {
const qb = this.permissionRepository.createQueryBuilder('permissions');
qb.where('permissions.permission_id=:id').setParameter('id', id);
const result = await qb.getOne();
if (!result) {
throw new HttpException('权限字符不存在', HttpStatus.BAD_REQUEST);
}
return result;
}
async update(updatePermissionDto: UpdatePermissionDto) {
const exitsPermission = await this.permissionRepository.findOne({
where: { permission_id: updatePermissionDto?.permission_id },
});
if (!exitsPermission) {
throw new HttpException('权限字符不存在', HttpStatus.BAD_REQUEST);
}
const param = await this.getSubmitDto(updatePermissionDto);
const newPermission = this.permissionRepository.merge(
exitsPermission,
param,
);
return await this.permissionRepository.save(newPermission);
}
async remove(id: number) {
const exitsPermission = await this.permissionRepository.findOne({
where: { permission_id: id },
});
if (!exitsPermission) {
throw new HttpException('权限字符不存在', HttpStatus.BAD_REQUEST);
}
return await this.permissionRepository.remove(exitsPermission);
}
async findByIds(ids: number[]) {
return this.permissionRepository.findBy({ permission_id: In(ids) });
}
}
permissions.module.ts
import { Module, forwardRef } from '@nestjs/common';
import { PermissionsService } from './permissions.service';
import { PermissionsController } from './permissions.controller';
import Permission from './entities/permission.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RolesModule } from 'src/roles/roles.module';
@Module({
imports: [
TypeOrmModule.forFeature([Permission]),
forwardRef(() => RolesModule),
],
controllers: [PermissionsController],
providers: [PermissionsService],
// 暴露 service 用处:假如role 中查询权限信息需要使用 PermissionsService查询数据
exports: [PermissionsService],
})
export class PermissionsModule {}
entities/permission.entity.ts
权限 实体
import {} from '@nestjs/typeorm';
import { Column, Entity, PrimaryGeneratedColumn, ManyToMany } from 'typeorm';
import Role from 'src/roles/entities/role.entity';
@Entity('permissions')
export default class Permission {
@PrimaryGeneratedColumn()
permission_id: number;
@Column()
name: string;
@Column()
description: string;
// 多对多关系处理
@ManyToMany(() => Role, (role) => role?.permissions)
roles: Role[];
}
dto/permission.ts 接口说明
import { ApiProperty } from '@nestjs/swagger';
import { PartialType } from '@nestjs/mapped-types';
import { IsNotEmpty, IsNumber, IsOptional ,IsArray, MaxLength} from 'class-validator';
export class CreatePermissionDto {
@ApiProperty()
@IsNotEmpty()
name: string;
@ApiProperty()
@IsOptional()
@MaxLength(100)
description: string;
@ApiProperty()
@IsOptional()
@IsArray({each: true})
roleIds: number[];
}
export class SearchPermissionDto extends PartialType(CreatePermissionDto) {
@ApiProperty()
@IsNotEmpty()
@IsNumber()
pageIndex: number;
@ApiProperty()
@IsNotEmpty()
@IsNumber()
pageSize: number;
}
export class UpdatePermissionDto extends PartialType(CreatePermissionDto) {
@ApiProperty()
@IsNotEmpty()
@IsNumber()
permission_id: number;
}
出现的问题
1.循环引入问题
在表关联关系中处理 多对多关系 会出现循环引入问题。官方给出处理方法
2. 表关联关系 多对多关系注意点
下一篇 将会介绍 接入swagger ,以及全局响应拦截器、验证管道。。。