feat: 添加字典管理
parent
7252bc3573
commit
3a29108da1
Binary file not shown.
|
|
@ -1,4 +1,4 @@
|
||||||
import { PaginationDto } from '@/common/response';
|
import { PaginationDto } from '@/middlewares/response';
|
||||||
import { IntersectionType } from '@nestjs/swagger';
|
import { IntersectionType } from '@nestjs/swagger';
|
||||||
import { IsOptional, IsString } from 'class-validator';
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { BaseController } from '@/common/base';
|
import { BaseController } from '@/common/base';
|
||||||
import { Respond, RespondType } from '@/common/response';
|
import { Respond, RespondType } from '@/middlewares/response';
|
||||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, ParseIntPipe } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
|
||||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { Create{{upcaseName name}}Dto } from './dto/create-{{fileName name}}.dto';
|
import { Create{{upcaseName name}}Dto } from './dto/create-{{fileName name}}.dto';
|
||||||
import { Find{{upcaseName name}}Dto } from './dto/find-{{fileName name}}.dto';
|
import { Find{{upcaseName name}}Dto } from './dto/find-{{fileName name}}.dto';
|
||||||
import { Update{{upcaseName name}}Dto } from './dto/update-{{fileName name}}.dto';
|
import { Update{{upcaseName name}}Dto } from './dto/update-{{fileName name}}.dto';
|
||||||
|
|
@ -31,19 +31,19 @@ export class {{upcaseName name}}Controller extends BaseController {
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@ApiOperation({ description: '获取{{cnName}}', operationId: 'get{{upcaseName name}}' })
|
@ApiOperation({ description: '获取{{cnName}}', operationId: 'get{{upcaseName name}}' })
|
||||||
get{{upcaseName name}}(id: number): Promise<{{upcaseName name}}> {
|
get{{upcaseName name}}(@Param('id') id: number): Promise<{{upcaseName name}}> {
|
||||||
return this.{{lowcaseName name}}Service.findOne(id);
|
return this.{{lowcaseName name}}Service.findOne(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
@ApiOperation({ description: '更新{{cnName}}', operationId: 'set{{upcaseName name}}' })
|
@ApiOperation({ description: '更新{{cnName}}', operationId: 'set{{upcaseName name}}' })
|
||||||
update{{upcaseName name}}(id: number, @Body() update{{upcaseName name}}Dto: Update{{upcaseName name}}Dto) {
|
update{{upcaseName name}}(@Param('id') id: number, @Body() update{{upcaseName name}}Dto: Update{{upcaseName name}}Dto) {
|
||||||
return this.{{lowcaseName name}}Service.update(+id, update{{upcaseName name}}Dto);
|
return this.{{lowcaseName name}}Service.update(+id, update{{upcaseName name}}Dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@ApiOperation({ description: '删除{{cnName}}', operationId: 'del{{upcaseName name}}' })
|
@ApiOperation({ description: '删除{{cnName}}', operationId: 'del{{upcaseName name}}' })
|
||||||
del{{upcaseName name}}(id: number) {
|
del{{upcaseName name}}(@Param('id') id: number) {
|
||||||
return this.{{lowcaseName name}}Service.remove(+id);
|
return this.{{lowcaseName name}}Service.remove(+id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ import { LoggerModule } from '@/monitor/logger';
|
||||||
import { CacheModule } from '@/storage/cache';
|
import { CacheModule } from '@/storage/cache';
|
||||||
import { UploadModule } from '@/storage/file';
|
import { UploadModule } from '@/storage/file';
|
||||||
import { AuthModule } from '@/system/auth';
|
import { AuthModule } from '@/system/auth';
|
||||||
import { PermissionModule } from '@/system/permission';
|
|
||||||
import { RoleModule } from '@/system/role';
|
import { RoleModule } from '@/system/role';
|
||||||
import { UserModule } from '@/system/user';
|
import { UserModule } from '@/system/user';
|
||||||
import { ScanModule } from '@/utils/scan.module';
|
import { ScanModule } from '@/utils/scan.module';
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { MenuModule } from './system/menu';
|
import { MenuModule } from './system/menu';
|
||||||
|
import { DictModule, DictTypeModule } from './system/dict';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -76,10 +76,6 @@ import { MenuModule } from './system/menu';
|
||||||
* 角色模块
|
* 角色模块
|
||||||
*/
|
*/
|
||||||
RoleModule,
|
RoleModule,
|
||||||
/**
|
|
||||||
* 权限模块
|
|
||||||
*/
|
|
||||||
PermissionModule,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传模块
|
* 上传模块
|
||||||
|
|
@ -91,6 +87,8 @@ import { MenuModule } from './system/menu';
|
||||||
PostModule,
|
PostModule,
|
||||||
ContentModule,
|
ContentModule,
|
||||||
MenuModule,
|
MenuModule,
|
||||||
|
DictTypeModule,
|
||||||
|
DictModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
export * from './mail.controller';
|
|
||||||
export * from './mail.module';
|
export * from './mail.module';
|
||||||
export * from './mail.service';
|
export * from './mail.service';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import { Controller } from '@nestjs/common';
|
|
||||||
import { MailService } from './mail.service';
|
|
||||||
|
|
||||||
@Controller('mail')
|
|
||||||
export class MailController {
|
|
||||||
constructor(private readonly mailService: MailService) {}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { MailService } from './mail.service';
|
import { MailService } from './mail.service';
|
||||||
import { MailController } from './mail.controller';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [MailController],
|
controllers: [],
|
||||||
providers: [MailService],
|
providers: [MailService],
|
||||||
|
exports: [MailService],
|
||||||
})
|
})
|
||||||
export class MailModule {}
|
export class MailModule {}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
export * from './datasource';
|
export * from './datasource';
|
||||||
export * from './entities/base';
|
export * from './entities/base.entity';
|
||||||
export * from './database.module';
|
export * from './database.module';
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ export class LoggerInterceptor implements NestInterceptor {
|
||||||
tap({
|
tap({
|
||||||
next: () => {
|
next: () => {
|
||||||
const ms = Date.now() - now;
|
const ms = Date.now() - now;
|
||||||
this.logger.log(`[Suuccess] ${method} ${url}(${ms} ms) +1`, scope);
|
this.logger.log(`[成功] ${method} ${url}(${ms} ms) +1`, scope);
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
const ms = Date.now() - now;
|
const ms = Date.now() - now;
|
||||||
this.logger.log(`[Fail] ${method} ${url}(${ms} ms) +1`, scope);
|
this.logger.error(`[失败] ${method} ${url}(${ms} ms) +1`, scope);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,21 @@ import { BaseEntity } from '@/database';
|
||||||
import { Column, Entity } from 'typeorm';
|
import { Column, Entity } from 'typeorm';
|
||||||
|
|
||||||
@Entity({ orderBy: { id: 'DESC' } })
|
@Entity({ orderBy: { id: 'DESC' } })
|
||||||
export class Upload extends BaseEntity {
|
export class File extends BaseEntity {
|
||||||
/**
|
/**
|
||||||
* 文件名
|
* 文件名
|
||||||
* @example "xxx.jpg"
|
* @example "头像.jpg"
|
||||||
*/
|
*/
|
||||||
@Column({ comment: '文件名' })
|
@Column({ comment: '文件名' })
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
* @example '一段很长的描述'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '描述' })
|
||||||
|
description: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件大小
|
* 文件大小
|
||||||
* @example 1024
|
* @example 1024
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ export class UploadController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('hash/:hash')
|
@Get('hash/:hash')
|
||||||
@ApiOperation({ description: '查询文件是否已存在', operationId: 'getFileByHash' })
|
@ApiOperation({ description: '根据哈希查询', operationId: 'getFileByHash' })
|
||||||
isHashExists(@Param('hash') hash: string) {
|
getByHash(@Param('hash') hash: string) {
|
||||||
return this.uploadService.isHashExists(hash);
|
return this.uploadService.getByHash(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { existsSync, mkdirSync } from 'fs';
|
import { existsSync, mkdirSync } from 'fs';
|
||||||
import { diskStorage } from 'multer';
|
import { diskStorage } from 'multer';
|
||||||
import { extname, join } from 'path';
|
import { extname, join } from 'path';
|
||||||
import { Upload } from './entities/file.entity';
|
import { File } from './entities/file.entity';
|
||||||
import { UploadController } from './file.controller';
|
import { UploadController } from './file.controller';
|
||||||
import { UploadService } from './file.service';
|
import { UploadService } from './file.service';
|
||||||
import { dayjs } from '@/libraries';
|
import { dayjs } from '@/libraries';
|
||||||
|
|
@ -31,7 +31,7 @@ const MulteredModule = MulterModule.registerAsync({
|
||||||
});
|
});
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([Upload]), MulteredModule],
|
imports: [TypeOrmModule.forFeature([File]), MulteredModule],
|
||||||
controllers: [UploadController],
|
controllers: [UploadController],
|
||||||
providers: [UploadService],
|
providers: [UploadService],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,41 +3,42 @@ import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { extname, relative, sep } from 'path';
|
import { extname, relative, sep } from 'path';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { Upload } from './entities/file.entity';
|
import { File } from './entities/file.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UploadService extends BaseService {
|
export class UploadService extends BaseService {
|
||||||
constructor(@InjectRepository(Upload) private readonly uploadRepository: Repository<Upload>) {
|
constructor(@InjectRepository(File) private readonly repository: Repository<File>) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存文件信息
|
* 保存文件信息
|
||||||
* @param file 文件信息
|
* @param uploadFile 文件信息
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async create(file: Express.Multer.File) {
|
async create(uploadFile: Express.Multer.File) {
|
||||||
const path = relative(this.config.uploadDir, file.path).split(sep).join('/');
|
const { originalname: name, mimetype, size, path: hash } = uploadFile;
|
||||||
const uploadPrefix = this.config.uploadPrefix;
|
const relativePath = relative(this.config.uploadDir, uploadFile.path).split(sep).join('/');
|
||||||
const uploadUrl = `${uploadPrefix}/${path}`;
|
const path = `${this.config.uploadPrefix}/${relativePath}`;
|
||||||
const upload = this.uploadRepository.create({
|
const extension = extname(uploadFile.originalname);
|
||||||
name: file.originalname,
|
const file = this.repository.create({
|
||||||
mimetype: file.mimetype,
|
name,
|
||||||
size: file.size,
|
mimetype,
|
||||||
hash: file.filename,
|
size,
|
||||||
path: uploadUrl,
|
hash,
|
||||||
extension: extname(file.originalname),
|
path,
|
||||||
|
extension,
|
||||||
});
|
});
|
||||||
await this.uploadRepository.save(upload);
|
await this.repository.save(file);
|
||||||
return upload;
|
return file.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll() {
|
findAll() {
|
||||||
return this.uploadRepository.findAndCount();
|
return this.repository.findAndCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
findOne(id: number) {
|
findOne(id: number) {
|
||||||
return this.uploadRepository.findOne({ where: { id } });
|
return this.repository.findOne({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|
@ -49,12 +50,11 @@ export class UploadService extends BaseService {
|
||||||
* @param hash 哈希
|
* @param hash 哈希
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async isHashExists(hash: string) {
|
async getByHash(hash: string) {
|
||||||
const count = await this.uploadRepository.count({ where: { hash } });
|
return this.repository.findOneBy({ hash });
|
||||||
return count > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(id: number) {
|
remove(id: number) {
|
||||||
return this.uploadRepository.softDelete(id);
|
return this.repository.softDelete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { AuthUserDto } from './dto/auth-user.dto';
|
import { AuthUserDto } from './dto/auth-user.dto';
|
||||||
import { Public } from './jwt';
|
import { Public } from './jwt/jwt-decorator';
|
||||||
import { LoginLogInterceptor } from '@/monitor/log';
|
import { LoginLogInterceptor } from '@/monitor/log';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
|
||||||
import { UserModule } from '../user';
|
import { UserModule } from '../user';
|
||||||
import { AuthController } from './auth.controller';
|
import { AuthController } from './auth.controller';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { JwtGuard, JwtModule } from './jwt';
|
import { JwtModule } from './jwt/jwt-module';
|
||||||
import { APP_GUARD } from '@nestjs/core';
|
import { APP_GUARD } from '@nestjs/core';
|
||||||
import { LogModule } from '@/monitor/log';
|
import { LogModule } from '@/monitor/log';
|
||||||
|
import { JwtGuard } from './jwt/jwt-guard';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UserModule, JwtModule, LogModule],
|
imports: [UserModule, JwtModule, LogModule],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
export * from './auth.controller';
|
export * from './auth.controller';
|
||||||
export * from './auth.module';
|
export * from './auth.module';
|
||||||
export * from './auth.service';
|
export * from './auth.service';
|
||||||
export * from './jwt';
|
export * from './jwt/jwt-decorator';
|
||||||
|
export * from './jwt/jwt-guard';
|
||||||
|
export * from './jwt/jwt-module';
|
||||||
export * from './dto/auth-user.dto';
|
export * from './dto/auth-user.dto';
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export * from './jwt-decorator';
|
|
||||||
export * from './jwt-guard';
|
|
||||||
export * from './jwt-module';
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { User } from '@/system/user';
|
|
||||||
import { OmitType } from '@nestjs/swagger';
|
|
||||||
|
|
||||||
export class LoginedUserVo extends OmitType(User, ['password', 'id'] as const) {
|
|
||||||
/**
|
|
||||||
* 用户ID
|
|
||||||
*/
|
|
||||||
id: number;
|
|
||||||
/**
|
|
||||||
* 访问令牌
|
|
||||||
* @example 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MjIsInVzZXJuYW1lIjoianVldGFuIiwiaWF0IjoxNjkxMTM5MjI3LCJleHAiOjE2OTExOTkyMjd9.6z7f-xfsHABbsyg401o2boKeqNQ1epPDYfEdavIcfYc'
|
|
||||||
*/
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { BaseController } from '@/common/base';
|
||||||
|
import { Respond, RespondType } from '@/middlewares/response';
|
||||||
|
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
|
||||||
|
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { CreateDictDto } from './dto/create-dict.dto';
|
||||||
|
import { FindDictDto } from './dto/find-dict.dto';
|
||||||
|
import { UpdateDictDto } from './dto/update-dict.dto';
|
||||||
|
import { Dict } from './entities/dict.entity';
|
||||||
|
import { DictService } from './dict.service';
|
||||||
|
|
||||||
|
@ApiTags('dict')
|
||||||
|
@Controller('dicts')
|
||||||
|
export class DictController extends BaseController {
|
||||||
|
constructor(private dictService: DictService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ description: '新增字典', operationId: 'addDict' })
|
||||||
|
addDict(@Body() createDictDto: CreateDictDto) {
|
||||||
|
return this.dictService.create(createDictDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@Respond(RespondType.PAGINATION)
|
||||||
|
@ApiOkResponse({ isArray: true, type: Dict })
|
||||||
|
@ApiOperation({ description: '查询字典', operationId: 'getDicts' })
|
||||||
|
getDicts(@Query() query: FindDictDto) {
|
||||||
|
return this.dictService.findMany(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ description: '获取字典', operationId: 'getDict' })
|
||||||
|
getDict(@Param('id') id: number): Promise<Dict> {
|
||||||
|
return this.dictService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ description: '更新字典', operationId: 'setDict' })
|
||||||
|
updateDict(@Param('id') id: number, @Body() updateDictDto: UpdateDictDto) {
|
||||||
|
return this.dictService.update(+id, updateDictDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ description: '删除字典', operationId: 'delDict' })
|
||||||
|
delDict(@Param('id') id: number) {
|
||||||
|
return this.dictService.remove(+id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Module, forwardRef } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { Dict } from './entities/dict.entity';
|
||||||
|
import { DictController } from './dict.controller';
|
||||||
|
import { DictService } from './dict.service';
|
||||||
|
import { DictTypeModule } from '../dictType';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Dict]), forwardRef(() => DictTypeModule)],
|
||||||
|
controllers: [DictController],
|
||||||
|
providers: [DictService],
|
||||||
|
exports: [DictService],
|
||||||
|
})
|
||||||
|
export class DictModule {}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { BaseService } from '@/common/base';
|
||||||
|
import { Inject, Injectable, NotFoundException, forwardRef } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Like, Repository } from 'typeorm';
|
||||||
|
import { CreateDictDto } from './dto/create-dict.dto';
|
||||||
|
import { FindDictDto } from './dto/find-dict.dto';
|
||||||
|
import { UpdateDictDto } from './dto/update-dict.dto';
|
||||||
|
import { Dict } from './entities/dict.entity';
|
||||||
|
import { DictTypeService } from '../dictType';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DictService extends BaseService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Dict) private dictRepository: Repository<Dict>,
|
||||||
|
@Inject(forwardRef(() => DictTypeService)) private dictTypeService: DictTypeService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增字典
|
||||||
|
*/
|
||||||
|
async create(createDictDto: CreateDictDto) {
|
||||||
|
const dict = this.dictRepository.create(createDictDto);
|
||||||
|
const { typeId } = createDictDto;
|
||||||
|
const type = await this.dictTypeService.findOne(typeId);
|
||||||
|
if (!type) {
|
||||||
|
throw new NotFoundException('字典类型不存在');
|
||||||
|
}
|
||||||
|
dict.type = type;
|
||||||
|
await this.dictRepository.save(dict);
|
||||||
|
return dict.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件/分页查询
|
||||||
|
*/
|
||||||
|
async findMany(findDictdto: FindDictDto) {
|
||||||
|
const { page, size, typeId } = findDictdto;
|
||||||
|
const { skip, take } = this.formatPagination(page, size, true);
|
||||||
|
return this.dictRepository.findAndCount({ skip, take, where: { typeId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询
|
||||||
|
*/
|
||||||
|
findOne(idOrOptions: number | Partial<Dict>) {
|
||||||
|
const where = typeof idOrOptions === 'number' ? { id: idOrOptions } : (idOrOptions as any);
|
||||||
|
return this.dictRepository.findOne({ where });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID更新
|
||||||
|
*/
|
||||||
|
update(id: number, updateDictDto: UpdateDictDto) {
|
||||||
|
return this.dictRepository.update(id, updateDictDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID删除(软删除)
|
||||||
|
*/
|
||||||
|
remove(id: number) {
|
||||||
|
return this.dictRepository.softDelete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateDictDto {
|
||||||
|
/**
|
||||||
|
* 字典类型
|
||||||
|
* @example 1
|
||||||
|
*/
|
||||||
|
@IsNumber()
|
||||||
|
typeId: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型名称
|
||||||
|
* @example '性别'
|
||||||
|
*/
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识
|
||||||
|
* @example 'gender'
|
||||||
|
*/
|
||||||
|
@IsString()
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
* @examle '一段很长的描述'
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { PaginationDto } from '@/middlewares/response';
|
||||||
|
import { IntersectionType } from '@nestjs/swagger';
|
||||||
|
import { Transform } from 'class-transformer';
|
||||||
|
import { IsInt, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class FindDictDto extends IntersectionType(PaginationDto) {
|
||||||
|
/**
|
||||||
|
* 类型ID
|
||||||
|
* @example 1
|
||||||
|
*/
|
||||||
|
@IsInt()
|
||||||
|
@Transform(({ value }) => Number(value))
|
||||||
|
typeId: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { IsNumber } from 'class-validator';
|
||||||
|
import { CreateDictDto } from './create-dict.dto';
|
||||||
|
|
||||||
|
export class UpdateDictDto extends PartialType(CreateDictDto) {
|
||||||
|
@IsNumber()
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { BaseEntity } from '@/database';
|
||||||
|
import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
|
||||||
|
import { DictType } from '../../dictType';
|
||||||
|
|
||||||
|
@Entity({ orderBy: { id: 'DESC' } })
|
||||||
|
export class Dict extends BaseEntity {
|
||||||
|
/**
|
||||||
|
* 类型名称
|
||||||
|
* @example '性别'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '名称' })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识
|
||||||
|
* @example 'gender'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '标识' })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
* @example true
|
||||||
|
*/
|
||||||
|
@Column({ comment: '状态', default: true })
|
||||||
|
status: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
* @examle '一段很长的描述'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '描述' })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段类型
|
||||||
|
*/
|
||||||
|
@ManyToOne(() => DictType, (dictType) => dictType.dicts)
|
||||||
|
@JoinColumn()
|
||||||
|
type: DictType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型ID
|
||||||
|
* @example 1
|
||||||
|
*/
|
||||||
|
@Column()
|
||||||
|
typeId: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './entities/dict.entity';
|
||||||
|
export * from './dict.controller';
|
||||||
|
export * from './dict.module';
|
||||||
|
export * from './dict.service';
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { BaseController } from '@/common/base';
|
||||||
|
import { Respond, RespondType } from '@/middlewares/response';
|
||||||
|
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
|
||||||
|
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { CreateDictTypeDto } from './dto/create-dictType.dto';
|
||||||
|
import { FindDictTypeDto } from './dto/find-dictType.dto';
|
||||||
|
import { UpdateDictTypeDto } from './dto/update-dictType.dto';
|
||||||
|
import { DictType } from './entities/dictType.entity';
|
||||||
|
import { DictTypeService } from './dictType.service';
|
||||||
|
|
||||||
|
@ApiTags('dictType')
|
||||||
|
@Controller('dictTypes')
|
||||||
|
export class DictTypeController extends BaseController {
|
||||||
|
constructor(private dictTypeService: DictTypeService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ description: '新增字典类型', operationId: 'addDictType' })
|
||||||
|
addDictType(@Body() createDictTypeDto: CreateDictTypeDto) {
|
||||||
|
return this.dictTypeService.create(createDictTypeDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@Respond(RespondType.PAGINATION)
|
||||||
|
@ApiOkResponse({ isArray: true, type: DictType })
|
||||||
|
@ApiOperation({ description: '查询字典类型', operationId: 'getDictTypes' })
|
||||||
|
getDictTypes(@Query() query: FindDictTypeDto) {
|
||||||
|
return this.dictTypeService.findMany(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ description: '获取字典类型', operationId: 'getDictType' })
|
||||||
|
getDictType(@Param('id') id: number): Promise<DictType> {
|
||||||
|
return this.dictTypeService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ description: '更新字典类型', operationId: 'setDictType' })
|
||||||
|
updateDictType(@Param('id') id: number, @Body() updateDictTypeDto: UpdateDictTypeDto) {
|
||||||
|
return this.dictTypeService.update(+id, updateDictTypeDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ description: '删除字典类型', operationId: 'delDictType' })
|
||||||
|
delDictType(@Param('id') id: number) {
|
||||||
|
return this.dictTypeService.remove(+id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { DictType } from './entities/dictType.entity';
|
||||||
|
import { DictTypeController } from './dictType.controller';
|
||||||
|
import { DictTypeService } from './dictType.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([DictType])],
|
||||||
|
controllers: [DictTypeController],
|
||||||
|
providers: [DictTypeService],
|
||||||
|
exports: [DictTypeService],
|
||||||
|
})
|
||||||
|
export class DictTypeModule {}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { BaseService } from '@/common/base';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Like, Repository } from 'typeorm';
|
||||||
|
import { CreateDictTypeDto } from './dto/create-dictType.dto';
|
||||||
|
import { FindDictTypeDto } from './dto/find-dictType.dto';
|
||||||
|
import { UpdateDictTypeDto } from './dto/update-dictType.dto';
|
||||||
|
import { DictType } from './entities/dictType.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DictTypeService extends BaseService {
|
||||||
|
constructor(@InjectRepository(DictType) private dictTypeRepository: Repository<DictType>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增字典类型
|
||||||
|
*/
|
||||||
|
async create(createDictTypeDto: CreateDictTypeDto) {
|
||||||
|
const dictType = this.dictTypeRepository.create(createDictTypeDto);
|
||||||
|
await this.dictTypeRepository.save(dictType);
|
||||||
|
return dictType.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件/分页查询
|
||||||
|
*/
|
||||||
|
async findMany(findDictTypedto: FindDictTypeDto) {
|
||||||
|
const { page, size } = findDictTypedto;
|
||||||
|
const { skip, take } = this.formatPagination(page, size, true);
|
||||||
|
return this.dictTypeRepository.findAndCount({ skip, take });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询
|
||||||
|
*/
|
||||||
|
findOne(idOrOptions: number | Partial<DictType>) {
|
||||||
|
const where = typeof idOrOptions === 'number' ? { id: idOrOptions } : (idOrOptions as any);
|
||||||
|
return this.dictTypeRepository.findOne({ where });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID更新
|
||||||
|
*/
|
||||||
|
update(id: number, updateDictTypeDto: UpdateDictTypeDto) {
|
||||||
|
return this.dictTypeRepository.update(id, updateDictTypeDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID删除(软删除)
|
||||||
|
*/
|
||||||
|
remove(id: number) {
|
||||||
|
return this.dictTypeRepository.softDelete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateDictTypeDto {
|
||||||
|
/**
|
||||||
|
* 类型名称
|
||||||
|
* @example '性别'
|
||||||
|
*/
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识
|
||||||
|
* @example 'gender'
|
||||||
|
*/
|
||||||
|
@IsString()
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
* @examle '一段很长的描述'
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { PaginationDto } from '@/middlewares/response';
|
||||||
|
import { IntersectionType } from '@nestjs/swagger';
|
||||||
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class FindDictTypeDto extends IntersectionType(PaginationDto) {
|
||||||
|
/**
|
||||||
|
* 字段描述(Swagger用途)
|
||||||
|
* @example '示例值'
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
demo?: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { IsNumber } from 'class-validator';
|
||||||
|
import { CreateDictTypeDto } from './create-dictType.dto';
|
||||||
|
|
||||||
|
export class UpdateDictTypeDto extends PartialType(CreateDictTypeDto) {
|
||||||
|
@IsNumber()
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { BaseEntity } from '@/database';
|
||||||
|
import { Column, Entity, OneToMany } from 'typeorm';
|
||||||
|
import { Dict } from '../../data';
|
||||||
|
|
||||||
|
@Entity({ orderBy: { id: 'DESC' } })
|
||||||
|
export class DictType extends BaseEntity {
|
||||||
|
/**
|
||||||
|
* 类型名称
|
||||||
|
* @example '性别'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '名称' })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识
|
||||||
|
* @example 'gender'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '标识' })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
* @example true
|
||||||
|
*/
|
||||||
|
@Column({ comment: '状态', default: true })
|
||||||
|
status: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
* @examle '一段很长的描述'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '描述' })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典数组
|
||||||
|
*/
|
||||||
|
@OneToMany(() => Dict, (dict) => dict.type)
|
||||||
|
dicts: Dict[];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './entities/dictType.entity';
|
||||||
|
export * from './dictType.controller';
|
||||||
|
export * from './dictType.module';
|
||||||
|
export * from './dictType.service';
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './dictType';
|
||||||
|
export * from './data';
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { PartialType } from '@nestjs/swagger';
|
import { PartialType } from '@nestjs/swagger';
|
||||||
import { IsNumber } from 'class-validator';
|
|
||||||
import { CreateMenuDto } from './create-menu.dto';
|
import { CreateMenuDto } from './create-menu.dto';
|
||||||
|
|
||||||
export class UpdateMenuDto extends PartialType(CreateMenuDto) {}
|
export class UpdateMenuDto extends PartialType(CreateMenuDto) {}
|
||||||
|
|
|
||||||
|
|
@ -42,17 +42,28 @@ export class Menu extends BaseEntity {
|
||||||
@Column({ comment: '类型(1: 目录, 2: 页面, 3: 按钮)' })
|
@Column({ comment: '类型(1: 目录, 2: 页面, 3: 按钮)' })
|
||||||
type: number;
|
type: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父级菜单
|
||||||
|
*/
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
@TreeParent()
|
@TreeParent()
|
||||||
parent: Menu;
|
parent: Menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父级ID
|
||||||
|
*/
|
||||||
@Column({ comment: '父级ID', nullable: true })
|
@Column({ comment: '父级ID', nullable: true })
|
||||||
parentId: number;
|
parentId: number;
|
||||||
|
|
||||||
@ApiHideProperty()
|
/**
|
||||||
|
* 子项数组
|
||||||
|
*/
|
||||||
@TreeChildren()
|
@TreeChildren()
|
||||||
children: Menu[];
|
children: Menu[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联角色
|
||||||
|
*/
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
@ManyToMany(() => Role, (role) => role.menus)
|
@ManyToMany(() => Role, (role) => role.menus)
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import { IsOptional, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class CreatePermissionDto {
|
|
||||||
/**
|
|
||||||
* 权限名称
|
|
||||||
* @example 权限名称
|
|
||||||
*/
|
|
||||||
@IsString()
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* 权限标识
|
|
||||||
* @example permission:permission
|
|
||||||
*/
|
|
||||||
@IsString()
|
|
||||||
slug: string;
|
|
||||||
/**
|
|
||||||
* 权限描述
|
|
||||||
*/
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
description?: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import { PartialType } from '@nestjs/swagger';
|
|
||||||
import { IsNumber } from 'class-validator';
|
|
||||||
import { CreatePermissionDto } from './create-permission.dto';
|
|
||||||
|
|
||||||
export class UpdatePermissionDto extends PartialType(CreatePermissionDto) {
|
|
||||||
@IsNumber()
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import { BaseEntity } from '@/database';
|
|
||||||
import { Role } from '@/system/role/entities/role.entity';
|
|
||||||
import { Column, Entity, ManyToMany } from 'typeorm';
|
|
||||||
|
|
||||||
enum PermissionType {
|
|
||||||
Menu = 'menu',
|
|
||||||
Api = 'api',
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity({ orderBy: { id: 'DESC' } })
|
|
||||||
export class Permission extends BaseEntity {
|
|
||||||
/**
|
|
||||||
* 权限名称
|
|
||||||
* @example '文章列表'
|
|
||||||
*/
|
|
||||||
@Column({ comment: '权限名称' })
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限标识
|
|
||||||
* @example 'post:list'
|
|
||||||
*/
|
|
||||||
@Column({ comment: '权限标识' })
|
|
||||||
slug: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限类型
|
|
||||||
* @example 'menu'
|
|
||||||
*/
|
|
||||||
@Column({ nullable: true })
|
|
||||||
type: PermissionType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限描述
|
|
||||||
* @example '文章列表'
|
|
||||||
*/
|
|
||||||
@Column({ comment: '权限描述', nullable: true })
|
|
||||||
description: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限角色
|
|
||||||
* @example {}
|
|
||||||
*/
|
|
||||||
@ManyToMany(() => Role, (role) => role.permissions)
|
|
||||||
roles: Role[];
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
export * from './dto/create-permission.dto';
|
|
||||||
export * from './dto/update-permission.dto';
|
|
||||||
export * from './entities/permission.entity';
|
|
||||||
export * from './permission.controller';
|
|
||||||
export * from './permission.module';
|
|
||||||
export * from './permission.service';
|
|
||||||
export * from './permission.decorator';
|
|
||||||
export * from './permission.guard';
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import { Respond, RespondType } from '@/middlewares/response';
|
|
||||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';
|
|
||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
|
||||||
import { CreatePermissionDto } from './dto/create-permission.dto';
|
|
||||||
import { UpdatePermissionDto } from './dto/update-permission.dto';
|
|
||||||
import { PermissionService } from './permission.service';
|
|
||||||
|
|
||||||
@ApiTags('permission')
|
|
||||||
@Controller('permissions')
|
|
||||||
export class PermissionController {
|
|
||||||
constructor(private readonly permissionService: PermissionService) {}
|
|
||||||
|
|
||||||
@Post()
|
|
||||||
@ApiOperation({ description: '创建权限', operationId: 'addPermission' })
|
|
||||||
create(@Body() createPermissionDto: CreatePermissionDto) {
|
|
||||||
return this.permissionService.create(createPermissionDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
@Respond(RespondType.PAGINATION)
|
|
||||||
@ApiOperation({ description: '批量查询权限', operationId: 'getPermissions' })
|
|
||||||
findAll() {
|
|
||||||
return this.permissionService.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(':id')
|
|
||||||
@ApiOperation({ description: '查询权限', operationId: 'getPermission' })
|
|
||||||
findOne(@Param('id') id: string) {
|
|
||||||
return this.permissionService.findOne(+id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Patch(':id')
|
|
||||||
@ApiOperation({ description: '更新权限', operationId: 'setPermission' })
|
|
||||||
update(@Param('id') id: string, @Body() updatePermissionDto: UpdatePermissionDto) {
|
|
||||||
return this.permissionService.update(+id, updatePermissionDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete(':id')
|
|
||||||
@ApiOperation({ description: '删除权限', operationId: 'delPermission' })
|
|
||||||
remove(@Param('id') id: string) {
|
|
||||||
return this.permissionService.remove(+id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import { SetMetadata } from '@nestjs/common';
|
|
||||||
|
|
||||||
export const PERMISSION_KEY = 'APP:PERMISSION';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限枚举
|
|
||||||
*/
|
|
||||||
export const enum PermissionEnum {
|
|
||||||
/**
|
|
||||||
* 新增
|
|
||||||
*/
|
|
||||||
CREATE = 'create',
|
|
||||||
/**
|
|
||||||
* 查询
|
|
||||||
*/
|
|
||||||
READ = 'read',
|
|
||||||
/**
|
|
||||||
* 更新
|
|
||||||
*/
|
|
||||||
UPDATE = 'update',
|
|
||||||
/**
|
|
||||||
* 删除
|
|
||||||
*/
|
|
||||||
DELETE = 'delete',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 指定所需的权限
|
|
||||||
* @param permissions
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function PermissionWith(...permissions: string[]) {
|
|
||||||
return SetMetadata(PERMISSION_KEY, permissions);
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { CanActivate, ExecutionContext, Inject, Injectable, UnauthorizedException, forwardRef } from '@nestjs/common';
|
|
||||||
import { Reflector } from '@nestjs/core';
|
|
||||||
import { PERMISSION_KEY } from './permission.decorator';
|
|
||||||
import { UserService } from '@/system/user';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class PermissionGuard implements CanActivate {
|
|
||||||
constructor(private reflector: Reflector, @Inject(forwardRef(() => UserService)) private userService: UserService) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const controller = context.getClass();
|
|
||||||
const handler = context.getHandler();
|
|
||||||
const permissions = this.reflector.getAllAndMerge(PERMISSION_KEY, [controller, handler]);
|
|
||||||
if (!permissions || !permissions.length) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const user = context.switchToHttp().getRequest().user;
|
|
||||||
if (!user) {
|
|
||||||
throw new UnauthorizedException('用户未登录');
|
|
||||||
}
|
|
||||||
const userPermissions = await this.userService.findUserPermissions(user.id);
|
|
||||||
const hasPermission = permissions.every((permission) => userPermissions.includes(permission));
|
|
||||||
if (!hasPermission) {
|
|
||||||
throw new UnauthorizedException('权限不足');
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { Module, forwardRef } from '@nestjs/common';
|
|
||||||
import { PermissionService } from './permission.service';
|
|
||||||
import { PermissionController } from './permission.controller';
|
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
||||||
import { Permission } from './entities/permission.entity';
|
|
||||||
import { APP_GUARD } from '@nestjs/core';
|
|
||||||
import { PermissionGuard } from './permission.guard';
|
|
||||||
import { UserModule } from '../user';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [TypeOrmModule.forFeature([Permission]), forwardRef(() => UserModule)],
|
|
||||||
controllers: [PermissionController],
|
|
||||||
providers: [
|
|
||||||
PermissionService,
|
|
||||||
{
|
|
||||||
provide: APP_GUARD,
|
|
||||||
useClass: PermissionGuard,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class PermissionModule {}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { CreatePermissionDto } from './dto/create-permission.dto';
|
|
||||||
import { UpdatePermissionDto } from './dto/update-permission.dto';
|
|
||||||
import { Permission } from './entities/permission.entity';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class PermissionService {
|
|
||||||
constructor(@InjectRepository(Permission) private readonly permissionRepository: Repository<Permission>) {}
|
|
||||||
|
|
||||||
async create(createPermissionDto: CreatePermissionDto) {
|
|
||||||
const permission = this.permissionRepository.create(createPermissionDto);
|
|
||||||
await this.permissionRepository.save(permission);
|
|
||||||
return permission.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
findAll() {
|
|
||||||
return this.permissionRepository.findAndCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
findOne(id: number) {
|
|
||||||
return `This action returns a #${id} permission`;
|
|
||||||
}
|
|
||||||
|
|
||||||
update(id: number, updatePermissionDto: UpdatePermissionDto) {
|
|
||||||
return this.permissionRepository.update(id, updatePermissionDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(id: number) {
|
|
||||||
return `This action removes a #${id} permission`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +1,33 @@
|
||||||
import { Permission } from '@/system/permission/entities/permission.entity';
|
|
||||||
import { IsInt, IsOptional, IsString } from 'class-validator';
|
import { IsInt, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class CreateRoleDto {
|
export class CreateRoleDto {
|
||||||
|
/**
|
||||||
|
* 角色名称
|
||||||
|
* @example '管理员'
|
||||||
|
*/
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色标识
|
||||||
|
* @example 'admin'
|
||||||
|
*/
|
||||||
@IsString()
|
@IsString()
|
||||||
slug: string;
|
slug: string;
|
||||||
|
|
||||||
@IsString()
|
/**
|
||||||
|
* 角色描述
|
||||||
|
* @example '一段很长的描述'
|
||||||
|
*/
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色ID数组
|
||||||
|
* @example [1]
|
||||||
|
*/
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt({ each: true })
|
@IsInt({ each: true })
|
||||||
permissions?: Permission[];
|
menuIds: number[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
import { PartialType } from '@nestjs/swagger';
|
import { PartialType } from '@nestjs/swagger';
|
||||||
import { CreateRoleDto } from './create-role.dto';
|
import { CreateRoleDto } from './create-role.dto';
|
||||||
import { IsInt, IsOptional } from 'class-validator';
|
|
||||||
|
|
||||||
export class UpdateRoleDto extends PartialType(CreateRoleDto) {
|
export class UpdateRoleDto extends PartialType(CreateRoleDto) {}
|
||||||
@IsOptional()
|
|
||||||
@IsInt({ each: true })
|
|
||||||
permissionIds?: number[];
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { BaseEntity } from '@/database';
|
import { BaseEntity } from '@/database';
|
||||||
import { Menu } from '@/system/menu';
|
import { Menu } from '@/system/menu';
|
||||||
import { Permission } from '@/system/permission/entities/permission.entity';
|
|
||||||
import { User } from '@/system/user';
|
import { User } from '@/system/user';
|
||||||
import { ApiHideProperty } from '@nestjs/swagger';
|
import { ApiHideProperty } from '@nestjs/swagger';
|
||||||
import { Column, Entity, JoinTable, ManyToMany, RelationId } from 'typeorm';
|
import { Column, Entity, JoinTable, ManyToMany, RelationId } from 'typeorm';
|
||||||
|
|
@ -28,20 +27,23 @@ export class Role extends BaseEntity {
|
||||||
@Column({ comment: '角色描述', nullable: true })
|
@Column({ comment: '角色描述', nullable: true })
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联用户
|
||||||
|
*/
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
@ManyToMany(() => User, (user) => user.roles)
|
@ManyToMany(() => User, (user) => user.roles)
|
||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
@ApiHideProperty()
|
/**
|
||||||
@JoinTable()
|
* 关联菜单
|
||||||
@ManyToMany(() => Permission, (permission) => permission.roles)
|
*/
|
||||||
permissions: Permission[];
|
|
||||||
|
|
||||||
@ApiHideProperty()
|
|
||||||
@RelationId('permissions')
|
|
||||||
permissionIds: number[];
|
|
||||||
|
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
@ManyToMany(() => Menu, (menu) => menu.roles)
|
@ManyToMany(() => Menu, (menu) => menu.roles)
|
||||||
menus: Menu[];
|
menus: Menu[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单ID数组
|
||||||
|
*/
|
||||||
|
@RelationId('menus')
|
||||||
|
menuIds: number[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,6 @@ export class RoleService {
|
||||||
|
|
||||||
async create(createRoleDto: CreateRoleDto) {
|
async create(createRoleDto: CreateRoleDto) {
|
||||||
const role = this.roleRepository.create(createRoleDto);
|
const role = this.roleRepository.create(createRoleDto);
|
||||||
if (createRoleDto.permissions) {
|
|
||||||
role.permissions = createRoleDto.permissions.map((id) => ({ id })) as any;
|
|
||||||
}
|
|
||||||
await this.roleRepository.save(role);
|
await this.roleRepository.save(role);
|
||||||
return role.id;
|
return role.id;
|
||||||
}
|
}
|
||||||
|
|
@ -31,11 +28,6 @@ export class RoleService {
|
||||||
if (!role) {
|
if (!role) {
|
||||||
throw new NotFoundException('角色不存在');
|
throw new NotFoundException('角色不存在');
|
||||||
}
|
}
|
||||||
if (updateRoleDto.permissionIds) {
|
|
||||||
const permissions = updateRoleDto.permissionIds.map((id) => ({ id }));
|
|
||||||
await this.roleRepository.save({ id, permissions });
|
|
||||||
delete updateRoleDto.permissionIds;
|
|
||||||
}
|
|
||||||
return this.roleRepository.update(id, updateRoleDto);
|
return this.roleRepository.update(id, updateRoleDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export class User extends BaseEntity {
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户文章
|
* 关联文章
|
||||||
*/
|
*/
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
@ManyToMany(() => Post, (post) => post.author)
|
@ManyToMany(() => Post, (post) => post.author)
|
||||||
|
|
@ -68,7 +68,7 @@ export class User extends BaseEntity {
|
||||||
posts: Post[];
|
posts: Post[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户角色
|
* 关联角色
|
||||||
*/
|
*/
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
@ManyToMany(() => Role, (role) => role.user, { cascade: true })
|
@ManyToMany(() => Role, (role) => role.user, { cascade: true })
|
||||||
|
|
@ -76,7 +76,7 @@ export class User extends BaseEntity {
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户角色ID
|
* 角色ID数组
|
||||||
*/
|
*/
|
||||||
@RelationId('roles')
|
@RelationId('roles')
|
||||||
roleIds: number[];
|
roleIds: number[];
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,11 @@ export class UserService extends BaseService {
|
||||||
async findUserPermissions(id: number) {
|
async findUserPermissions(id: number) {
|
||||||
const user = await this.userRepository.findOne({
|
const user = await this.userRepository.findOne({
|
||||||
where: { id },
|
where: { id },
|
||||||
relations: ['roles', 'roles.permissions'],
|
relations: ['roles'],
|
||||||
});
|
});
|
||||||
if (user) {
|
if (user) {
|
||||||
const permissions = user.roles.flatMap((role) => role.permissions);
|
const permissions = user.roles.flatMap((role) => role.menuIds);
|
||||||
return [...new Set(permissions.map((i) => i.slug))];
|
return [...new Set(permissions.map((i) => i))];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue