feat: 添加文件分类模块
自动部署 / build (push) Failing after 2s
Details
自动部署 / build (push) Failing after 2s
Details
parent
e6d2adef44
commit
26013a9f92
|
|
@ -2,7 +2,7 @@ version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
server:
|
server:
|
||||||
image: git.dev.juetan.cn/juetan/server:latest
|
image: git.app.juetan.cn/appnify/server:latest
|
||||||
networks:
|
networks:
|
||||||
- public
|
- public
|
||||||
deploy:
|
deploy:
|
||||||
|
|
@ -12,14 +12,15 @@ services:
|
||||||
constraints: [node.role == manager]
|
constraints: [node.role == manager]
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.nest.rule=Host(`nest.dev.juetan.cn`) && PathPrefix(`/api`, `/upload`)
|
- traefik.http.routers.aserver.rule=Host(`appnify.app.juetan.cn`) && PathPrefix(`/api`, `/upload`)
|
||||||
- traefik.http.routers.nest.entrypoints=websecure
|
- traefik.http.routers.aserver.entrypoints=websecure
|
||||||
- traefik.http.routers.nest.tls=true
|
- traefik.http.routers.aserver.tls=true
|
||||||
- traefik.http.routers.nest.tls.certresolver=acmer
|
- traefik.http.routers.aserver.tls.certresolver=acmer
|
||||||
- traefik.http.services.nest1.loadbalancer.server.port=3030
|
- traefik.http.routers.aserver.middlewares=tohttps@docker
|
||||||
|
- traefik.http.services.aserver1.loadbalancer.server.port=3030
|
||||||
|
|
||||||
web:
|
web:
|
||||||
image: git.dev.juetan.cn/juetan/web:latest
|
image: git.app.juetan.cn/appnify/web:latest
|
||||||
networks:
|
networks:
|
||||||
- public
|
- public
|
||||||
deploy:
|
deploy:
|
||||||
|
|
@ -29,11 +30,12 @@ services:
|
||||||
constraints: [node.role == manager]
|
constraints: [node.role == manager]
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.vue.rule=Host(`nest.dev.juetan.cn`)
|
- traefik.http.routers.aweb.rule=Host(`appnify.app.juetan.cn`)
|
||||||
- traefik.http.routers.vue.entrypoints=websecure
|
- traefik.http.routers.aweb.entrypoints=websecure
|
||||||
- traefik.http.routers.vue.tls=true
|
- traefik.http.routers.aweb.tls=true
|
||||||
- traefik.http.routers.vue.tls.certresolver=acmer
|
- traefik.http.routers.aweb.tls.certresolver=acmer
|
||||||
- traefik.http.services.vue1.loadbalancer.server.port=80
|
- traefik.http.routers.aweb.middlewares=tohttps@docker
|
||||||
|
- traefik.http.services.aweb1.loadbalancer.server.port=80
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -8,7 +8,7 @@ import { SerializationModule } from '@/middlewares/serialization';
|
||||||
import { ValidationModule } from '@/middlewares/validation';
|
import { ValidationModule } from '@/middlewares/validation';
|
||||||
import { LoggerModule } from '@/monitor/logger';
|
import { LoggerModule } from '@/monitor/logger';
|
||||||
import { CacheModule } from '@/storage/cache';
|
import { CacheModule } from '@/storage/cache';
|
||||||
import { UploadModule } from '@/storage/file';
|
import { FileModule } from '@/storage/file';
|
||||||
import { AuthModule } from '@/system/auth';
|
import { AuthModule } from '@/system/auth';
|
||||||
import { RoleModule } from '@/system/role';
|
import { RoleModule } from '@/system/role';
|
||||||
import { UserModule } from '@/system/user';
|
import { UserModule } from '@/system/user';
|
||||||
|
|
@ -16,6 +16,7 @@ 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';
|
import { DictModule, DictTypeModule } from './system/dict';
|
||||||
|
import { FileCategoryModule } from './storage/fileCategory';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -80,7 +81,11 @@ import { DictModule, DictTypeModule } from './system/dict';
|
||||||
/**
|
/**
|
||||||
* 上传模块
|
* 上传模块
|
||||||
*/
|
*/
|
||||||
UploadModule,
|
FileModule,
|
||||||
|
/**
|
||||||
|
* 文件分类
|
||||||
|
*/
|
||||||
|
FileCategoryModule,
|
||||||
/**
|
/**
|
||||||
* 文章模块
|
* 文章模块
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
import { ConfigService } from '@/config';
|
import { ConfigService } from '@/config';
|
||||||
|
import { BaseEntity } from '@/database';
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
|
import { Between, FindManyOptions } from 'typeorm';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
startDateTime: string;
|
||||||
|
endDateTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务基类
|
* 服务基类
|
||||||
|
|
@ -42,4 +51,19 @@ export class BaseService {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mergeCommonParams(params: Params): FindManyOptions<BaseEntity> {
|
||||||
|
const { page, size, startDateTime, endDateTime } = params;
|
||||||
|
const skip = (page - 1) * size;
|
||||||
|
const take = size === 0 ? this.config.defaultPageSize : size;
|
||||||
|
const fromDate = new Date(startDateTime);
|
||||||
|
const endDate = new Date(endDateTime);
|
||||||
|
return {
|
||||||
|
skip,
|
||||||
|
take,
|
||||||
|
where: {
|
||||||
|
createdAt: Between(fromDate, endDate),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
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 FindFileDto extends IntersectionType(PaginationDto) {
|
||||||
|
/**
|
||||||
|
* 文件名称
|
||||||
|
* @example '风景'
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类ID
|
||||||
|
* @example 1
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
@Transform(({ value }) => Number(value))
|
||||||
|
categoryId?: number;
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { IsOptional, IsString } from 'class-validator';
|
import { IsNumber, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class UpdateFileDto {
|
export class UpdateFileDto {
|
||||||
/**
|
/**
|
||||||
* 文件名
|
* 文件名
|
||||||
* @example "头像.jpg"
|
* @example "头像.jpg"
|
||||||
*/
|
*/
|
||||||
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 描述
|
* 描述
|
||||||
|
|
@ -15,4 +16,12 @@ export class UpdateFileDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类ID
|
||||||
|
* @example 1
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
categoryId?: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { BaseEntity } from '@/database';
|
import { BaseEntity } from '@/database';
|
||||||
import { Column, Entity } from 'typeorm';
|
import { FileCategory } from '@/storage/fileCategory';
|
||||||
|
import { ApiHideProperty } from '@nestjs/swagger';
|
||||||
|
import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
|
||||||
|
|
||||||
@Entity({ orderBy: { id: 'DESC' } })
|
@Entity({ orderBy: { id: 'DESC' } })
|
||||||
export class File extends BaseEntity {
|
export class File extends BaseEntity {
|
||||||
|
|
@ -33,7 +35,7 @@ export class File extends BaseEntity {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件路径
|
* 文件路径
|
||||||
* @example "/upload/2021/10/01/xxx.jpg"
|
* @example "/upload/2021-10-01/xxx.jpg"
|
||||||
*/
|
*/
|
||||||
@Column({ comment: '文件路径' })
|
@Column({ comment: '文件路径' })
|
||||||
path: string;
|
path: string;
|
||||||
|
|
@ -51,4 +53,19 @@ export class File extends BaseEntity {
|
||||||
*/
|
*/
|
||||||
@Column({ comment: '文件后缀' })
|
@Column({ comment: '文件后缀' })
|
||||||
extension: string;
|
extension: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类
|
||||||
|
*/
|
||||||
|
@ApiHideProperty()
|
||||||
|
@ManyToOne(() => FileCategory, (category) => category.files)
|
||||||
|
@JoinColumn()
|
||||||
|
category: FileCategory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类ID
|
||||||
|
* @example 0
|
||||||
|
*/
|
||||||
|
@Column({ comment: '分类ID', nullable: true })
|
||||||
|
categoryId: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
Param,
|
Param,
|
||||||
Patch,
|
Patch,
|
||||||
Post,
|
Post,
|
||||||
|
Query,
|
||||||
Req,
|
Req,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
|
|
@ -17,12 +18,13 @@ import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { CreateFileDto } from './dto/create-file.dto';
|
import { CreateFileDto } from './dto/create-file.dto';
|
||||||
import { UpdateFileDto } from './dto/update-file.dto';
|
import { UpdateFileDto } from './dto/update-file.dto';
|
||||||
import { UploadService } from './file.service';
|
import { FileService } from './file.service';
|
||||||
|
import { FindFileDto } from './dto/find-file.dto';
|
||||||
|
|
||||||
@ApiTags('file')
|
@ApiTags('file')
|
||||||
@Controller('file')
|
@Controller('file')
|
||||||
export class UploadController {
|
export class FileController {
|
||||||
constructor(private readonly uploadService: UploadService) {}
|
constructor(private readonly fileService: FileService) {}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@UseInterceptors(FileInterceptor('file'))
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
|
|
@ -30,37 +32,43 @@ export class UploadController {
|
||||||
@ApiBody({ description: '要上传的文件', type: CreateFileDto })
|
@ApiBody({ description: '要上传的文件', type: CreateFileDto })
|
||||||
@ApiOperation({ description: '上传文件', operationId: 'addFile' })
|
@ApiOperation({ description: '上传文件', operationId: 'addFile' })
|
||||||
create(@UploadedFile() file: Express.Multer.File, @Req() req: Request, @Ip() ip: string) {
|
create(@UploadedFile() file: Express.Multer.File, @Req() req: Request, @Ip() ip: string) {
|
||||||
return this.uploadService.create(file);
|
return this.fileService.create(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Respond(RespondType.PAGINATION)
|
@Respond(RespondType.PAGINATION)
|
||||||
@ApiOperation({ description: '批量查询', operationId: 'getFiles' })
|
@ApiOperation({ description: '批量查询', operationId: 'getFiles' })
|
||||||
findAll() {
|
findMany(@Query() findFileDto: FindFileDto) {
|
||||||
return this.uploadService.findAll();
|
return this.fileService.findMany(findFileDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@ApiOperation({ description: '查询', operationId: 'getFile' })
|
@ApiOperation({ description: '查询', operationId: 'getFile' })
|
||||||
findOne(@Param('id') id: number) {
|
findOne(@Param('id') id: number) {
|
||||||
return this.uploadService.findOne(+id);
|
return this.fileService.findOne(+id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('hash/:hash')
|
@Get('hash/:hash')
|
||||||
@ApiOperation({ description: '根据哈希查询', operationId: 'getFileByHash' })
|
@ApiOperation({ description: '根据哈希查询', operationId: 'getFileByHash' })
|
||||||
getByHash(@Param('hash') hash: string) {
|
getByHash(@Param('hash') hash: string) {
|
||||||
return this.uploadService.getByHash(hash);
|
return this.fileService.getByHash(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
@ApiOperation({ description: '更新', operationId: 'setFile' })
|
@ApiOperation({ description: '更新', operationId: 'setFile' })
|
||||||
update(@Param('id') id: number, @Body() updateFileDto: UpdateFileDto) {
|
update(@Param('id') id: number, @Body() updateFileDto: UpdateFileDto) {
|
||||||
return this.uploadService.update(id, updateFileDto);
|
return this.fileService.update(id, updateFileDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@ApiOperation({ description: '删除', operationId: 'delFile' })
|
@ApiOperation({ description: '删除', operationId: 'delFile' })
|
||||||
remove(@Param('id') id: number) {
|
remove(@Param('id') id: number) {
|
||||||
return this.uploadService.remove(+id);
|
return this.fileService.remove(+id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete()
|
||||||
|
@ApiOperation({ description: '批量删除文件', operationId: 'delFiles' })
|
||||||
|
removeMany(@Body() ids: number[]) {
|
||||||
|
return this.fileService.removeMany(ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import { ConfigService } from '@/config';
|
import { ConfigService } from '@/config';
|
||||||
import { Module } from '@nestjs/common';
|
import { Module, forwardRef } from '@nestjs/common';
|
||||||
import { MulterModule } from '@nestjs/platform-express';
|
import { MulterModule } from '@nestjs/platform-express';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
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 { File } from './entities/file.entity';
|
import { File } from './entities/file.entity';
|
||||||
import { UploadController } from './file.controller';
|
import { FileController } from './file.controller';
|
||||||
import { UploadService } from './file.service';
|
import { FileService } from './file.service';
|
||||||
import { dayjs } from '@/libraries';
|
import { dayjs } from '@/libraries';
|
||||||
|
import { FileCategoryModule } from '../fileCategory';
|
||||||
|
|
||||||
const MulteredModule = MulterModule.registerAsync({
|
const MulteredModule = MulterModule.registerAsync({
|
||||||
useFactory: (config: ConfigService) => {
|
useFactory: (config: ConfigService) => {
|
||||||
|
|
@ -31,8 +32,8 @@ const MulteredModule = MulterModule.registerAsync({
|
||||||
});
|
});
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([File]), MulteredModule],
|
imports: [TypeOrmModule.forFeature([File]), MulteredModule, forwardRef(() => FileCategoryModule)],
|
||||||
controllers: [UploadController],
|
controllers: [FileController],
|
||||||
providers: [UploadService],
|
providers: [FileService],
|
||||||
})
|
})
|
||||||
export class UploadModule {}
|
export class FileModule {}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,18 @@ import { BaseService } from '@/common/base';
|
||||||
import { Injectable } from '@nestjs/common';
|
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 { FindOptionsWhere, Like, Repository } from 'typeorm';
|
||||||
import { UpdateFileDto } from './dto/update-file.dto';
|
import { UpdateFileDto } from './dto/update-file.dto';
|
||||||
import { File } from './entities/file.entity';
|
import { File } from './entities/file.entity';
|
||||||
|
import { FindFileDto } from './dto/find-file.dto';
|
||||||
|
import { FileCategoryService } from '../fileCategory';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UploadService extends BaseService {
|
export class FileService extends BaseService {
|
||||||
constructor(@InjectRepository(File) private readonly repository: Repository<File>) {
|
constructor(
|
||||||
|
@InjectRepository(File) private readonly repository: Repository<File>,
|
||||||
|
private fileCategoryService: FileCategoryService,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,8 +41,17 @@ export class UploadService extends BaseService {
|
||||||
return file.id;
|
return file.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll() {
|
findMany(findFileDto: FindFileDto) {
|
||||||
return this.repository.findAndCount();
|
const { page, size, name, categoryId } = findFileDto;
|
||||||
|
const { skip, take } = this.formatPagination(page, size, true);
|
||||||
|
const where: FindOptionsWhere<File> = {};
|
||||||
|
if (name) {
|
||||||
|
where.name = Like(`%${name}%`);
|
||||||
|
}
|
||||||
|
if (categoryId) {
|
||||||
|
where.categoryId = categoryId;
|
||||||
|
}
|
||||||
|
return this.repository.findAndCount({ skip, take, where });
|
||||||
}
|
}
|
||||||
|
|
||||||
findOne(id: number) {
|
findOne(id: number) {
|
||||||
|
|
@ -48,10 +62,16 @@ export class UploadService extends BaseService {
|
||||||
* 更新文件信息
|
* 更新文件信息
|
||||||
* @param id 文件ID
|
* @param id 文件ID
|
||||||
* @param updateFileDto 更新信息
|
* @param updateFileDto 更新信息
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
update(id: number, updateFileDto: UpdateFileDto) {
|
async update(id: number, updateFileDto: UpdateFileDto) {
|
||||||
return this.repository.update(id, updateFileDto);
|
const { categoryId, ...rest } = updateFileDto;
|
||||||
|
let category;
|
||||||
|
if (categoryId) {
|
||||||
|
category = await this.fileCategoryService.findOne(categoryId);
|
||||||
|
}
|
||||||
|
console.log(category);
|
||||||
|
return this.repository.update(id, { ...rest, category });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -66,4 +86,13 @@ export class UploadService extends BaseService {
|
||||||
remove(id: number) {
|
remove(id: number) {
|
||||||
return this.repository.softDelete(id);
|
return this.repository.softDelete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除文件
|
||||||
|
* @param ids ID数组
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
removeMany(ids: number[]) {
|
||||||
|
return this.repository.softDelete(ids);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { IsInt, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateFileCategoryDto {
|
||||||
|
/**
|
||||||
|
* 分类名称
|
||||||
|
* @example '风景'
|
||||||
|
*/
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类编码
|
||||||
|
* @example 'view'
|
||||||
|
*/
|
||||||
|
@IsString()
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类描述
|
||||||
|
* @example '这是一段很长的描述'
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父级ID
|
||||||
|
* @example 0
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
|
parentId?: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { PaginationDto } from '@/middlewares/response';
|
||||||
|
import { IntersectionType } from '@nestjs/swagger';
|
||||||
|
import { IsInt, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class FindFileCategoryDto extends IntersectionType(PaginationDto) {
|
||||||
|
/**
|
||||||
|
* 分类名称
|
||||||
|
* @example '风景'
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { CreateFileCategoryDto } from './create-fileCategory.dto';
|
||||||
|
|
||||||
|
export class UpdateFileCategoryDto extends PartialType(CreateFileCategoryDto) {}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { BaseEntity } from '@/database';
|
||||||
|
import { File } from '@/storage/file';
|
||||||
|
import { ApiHideProperty } from '@nestjs/swagger';
|
||||||
|
import { Column, Entity, OneToMany, Tree, TreeChildren, TreeParent } from 'typeorm';
|
||||||
|
|
||||||
|
@Tree('materialized-path')
|
||||||
|
@Entity({ orderBy: { id: 'DESC' } })
|
||||||
|
export class FileCategory extends BaseEntity {
|
||||||
|
/**
|
||||||
|
* 分类名称
|
||||||
|
* @example '风景'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '分类描述' })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类编码
|
||||||
|
* @example 'view'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '分类编码' })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类描述
|
||||||
|
* @example '这是一段很长的描述'
|
||||||
|
*/
|
||||||
|
@Column({ comment: '分类描述', nullable: true })
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件列表
|
||||||
|
*/
|
||||||
|
@ApiHideProperty()
|
||||||
|
@OneToMany(() => File, file => file.category)
|
||||||
|
files: File[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父级分类
|
||||||
|
*/
|
||||||
|
@ApiHideProperty()
|
||||||
|
@TreeParent()
|
||||||
|
parent?: FileCategory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父级ID
|
||||||
|
*/
|
||||||
|
@Column({ comment: '父级ID', nullable: true })
|
||||||
|
parentId?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子项数组
|
||||||
|
*/
|
||||||
|
@ApiHideProperty()
|
||||||
|
@TreeChildren()
|
||||||
|
childrent: FileCategory[];
|
||||||
|
}
|
||||||
|
|
@ -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 { CreateFileCategoryDto } from './dto/create-fileCategory.dto';
|
||||||
|
import { FindFileCategoryDto } from './dto/find-fileCategory.dto';
|
||||||
|
import { UpdateFileCategoryDto } from './dto/update-fileCategory.dto';
|
||||||
|
import { FileCategory } from './entities/fileCategory.entity';
|
||||||
|
import { FileCategoryService } from './fileCategory.service';
|
||||||
|
|
||||||
|
@ApiTags('fileCategory')
|
||||||
|
@Controller('fileCategorys')
|
||||||
|
export class FileCategoryController extends BaseController {
|
||||||
|
constructor(private fileCategoryService: FileCategoryService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ description: '新增文件分类', operationId: 'addFileCategory' })
|
||||||
|
addFileCategory(@Body() createFileCategoryDto: CreateFileCategoryDto) {
|
||||||
|
return this.fileCategoryService.create(createFileCategoryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@Respond(RespondType.PAGINATION)
|
||||||
|
@ApiOkResponse({ isArray: true, type: FileCategory })
|
||||||
|
@ApiOperation({ description: '查询文件分类', operationId: 'getFileCategorys' })
|
||||||
|
getFileCategorys(@Query() query: FindFileCategoryDto) {
|
||||||
|
return this.fileCategoryService.findMany(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ description: '获取文件分类', operationId: 'getFileCategory' })
|
||||||
|
getFileCategory(@Param('id') id: number): Promise<FileCategory> {
|
||||||
|
return this.fileCategoryService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ description: '更新文件分类', operationId: 'setFileCategory' })
|
||||||
|
updateFileCategory(@Param('id') id: number, @Body() updateFileCategoryDto: UpdateFileCategoryDto) {
|
||||||
|
return this.fileCategoryService.update(+id, updateFileCategoryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ description: '删除文件分类', operationId: 'delFileCategory' })
|
||||||
|
delFileCategory(@Param('id') id: number) {
|
||||||
|
return this.fileCategoryService.remove(+id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { FileCategory } from './entities/fileCategory.entity';
|
||||||
|
import { FileCategoryController } from './fileCategory.controller';
|
||||||
|
import { FileCategoryService } from './fileCategory.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([FileCategory])],
|
||||||
|
controllers: [FileCategoryController],
|
||||||
|
providers: [FileCategoryService],
|
||||||
|
exports: [FileCategoryService],
|
||||||
|
})
|
||||||
|
export class FileCategoryModule {}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { BaseService } from '@/common/base';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { FindOptionsWhere, Like, Repository } from 'typeorm';
|
||||||
|
import { CreateFileCategoryDto } from './dto/create-fileCategory.dto';
|
||||||
|
import { FindFileCategoryDto } from './dto/find-fileCategory.dto';
|
||||||
|
import { UpdateFileCategoryDto } from './dto/update-fileCategory.dto';
|
||||||
|
import { FileCategory } from './entities/fileCategory.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FileCategoryService extends BaseService {
|
||||||
|
constructor(@InjectRepository(FileCategory) private fileCategoryRepository: Repository<FileCategory>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增文件分类
|
||||||
|
*/
|
||||||
|
async create(createFileCategoryDto: CreateFileCategoryDto) {
|
||||||
|
const fileCategory = this.fileCategoryRepository.create(createFileCategoryDto);
|
||||||
|
await this.fileCategoryRepository.save(fileCategory);
|
||||||
|
return fileCategory.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件/分页查询
|
||||||
|
*/
|
||||||
|
async findMany(findFileCategorydto: FindFileCategoryDto) {
|
||||||
|
const { page, size } = findFileCategorydto;
|
||||||
|
const { skip, take } = this.formatPagination(page, size, true);
|
||||||
|
return this.fileCategoryRepository.findAndCount({ skip, take });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询
|
||||||
|
*/
|
||||||
|
findOne(idOrOptions: number | Partial<FileCategory>) {
|
||||||
|
const where = typeof idOrOptions === 'number' ? { id: idOrOptions } : (idOrOptions as any);
|
||||||
|
return this.fileCategoryRepository.findOne({ where });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID更新
|
||||||
|
*/
|
||||||
|
update(id: number, updateFileCategoryDto: UpdateFileCategoryDto) {
|
||||||
|
return this.fileCategoryRepository.update(id, updateFileCategoryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID删除(软删除)
|
||||||
|
*/
|
||||||
|
remove(id: number) {
|
||||||
|
return this.fileCategoryRepository.softDelete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './entities/fileCategory.entity';
|
||||||
|
export * from './fileCategory.controller';
|
||||||
|
export * from './fileCategory.module';
|
||||||
|
export * from './fileCategory.service';
|
||||||
Loading…
Reference in New Issue