feat: 文件存储添加业务模块和业务记录id冗余

This commit is contained in:
louis 2024-03-08 13:19:33 +08:00
parent f65a4c0b99
commit 46a4132877
11 changed files with 64 additions and 26 deletions

View File

@ -10,6 +10,7 @@ const app: FastifyAdapter = new FastifyAdapter({
export { app as fastifyApp }; export { app as fastifyApp };
app.register(FastifyMultipart, { app.register(FastifyMultipart, {
attachFieldsToBody:true,
limits: { limits: {
fields: 10, // Max number of non-file fields fields: 10, // Max number of non-file fields
fileSize: 1024 * 1024 * 6, // limit size 6M fileSize: 1024 * 1024 * 6, // limit size 6M

View File

@ -33,7 +33,6 @@ async function bootstrap() {
// class-validator 的 DTO 类中注入 nest 容器的依赖 (用于自定义验证器) // class-validator 的 DTO 类中注入 nest 容器的依赖 (用于自定义验证器)
useContainer(app.select(AppModule), { fallbackOnErrors: true }); useContainer(app.select(AppModule), { fallbackOnErrors: true });
app.enableCors({ origin: '*', credentials: true }); app.enableCors({ origin: '*', credentials: true });
app.setGlobalPrefix(globalPrefix); app.setGlobalPrefix(globalPrefix);
app.useStaticAssets({ root: path.join(__dirname, '..', 'public') }); app.useStaticAssets({ root: path.join(__dirname, '..', 'public') });

View File

@ -62,11 +62,12 @@ export class ContractService {
{ fileIds, contractNumber, ...ext }: Partial<ContractUpdateDto> { fileIds, contractNumber, ...ext }: Partial<ContractUpdateDto>
): Promise<void> { ): Promise<void> {
await this.entityManager.transaction(async manager => { await this.entityManager.transaction(async manager => {
if (await this.checkIsContractNumberExsit(contractNumber, id)) { if (contractNumber && (await this.checkIsContractNumberExsit(contractNumber, id))) {
throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST); throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST);
} }
await manager.update(ContractEntity, id, { await manager.update(ContractEntity, id, {
...ext ...ext,
contractNumber
}); });
if (fileIds?.length) { if (fileIds?.length) {

View File

@ -129,9 +129,9 @@ export class MaterialsInOutQueryDto extends PagerDto<MaterialsInOutQueryDto> {
remark?: string; remark?: string;
@IsOptional() @IsOptional()
@IsString() @IsNumber()
@ApiProperty({ description: '项目' }) @ApiProperty({ description: '项目Id' })
project?: string; projectId?: number;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()

View File

@ -35,7 +35,7 @@ export class MaterialsInOutService {
page, page,
pageSize, pageSize,
product: productName, product: productName,
project: projectName, projectId,
isCreateOut, isCreateOut,
...ext ...ext
}: MaterialsInOutQueryDto): Promise<Pagination<MaterialsInOutEntity>> { }: MaterialsInOutQueryDto): Promise<Pagination<MaterialsInOutEntity>> {
@ -46,15 +46,24 @@ export class MaterialsInOutService {
.leftJoin('materialsInOut.product', 'product') .leftJoin('materialsInOut.product', 'product')
.leftJoin('product.unit', 'unit') .leftJoin('product.unit', 'unit')
.leftJoin('product.company', 'company') .leftJoin('product.company', 'company')
.addSelect(['files.id','files.path', 'project.name', 'product.name', 'unit.label', 'company.name']) .addSelect([
'files.id',
'files.path',
'project.name',
'product.name',
'unit.label',
'company.name'
])
.where(fieldSearch(ext)) .where(fieldSearch(ext))
.andWhere('materialsInOut.isDelete = 0') .andWhere('materialsInOut.isDelete = 0')
.addOrderBy('materialsInOut.createdAt', 'DESC'); .addOrderBy('materialsInOut.createdAt', 'DESC');
if (productName) { if (productName) {
sqb.andWhere('product.name like :productName', { productName: `%${productName}%` }); sqb.andWhere('product.name like :productName', { productName: `%${productName}%` });
} }
if (projectName) {
sqb.andWhere('project.name like :projectName', { projectName: `%${projectName}%` }); if (projectId) {
sqb.andWhere('project.id = :projectId', { projectId });
} }
if (isCreateOut) { if (isCreateOut) {

View File

@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer'; import { Transform } from 'class-transformer';
import { ArrayNotEmpty, IsArray, IsOptional, IsString } from 'class-validator'; import { ArrayNotEmpty, IsArray, IsNumber, IsOptional, IsString } from 'class-validator';
import { PagerDto } from '~/common/dto/pager.dto'; import { PagerDto } from '~/common/dto/pager.dto';
@ -34,6 +34,16 @@ export class StoragePageDto extends PagerDto {
@IsOptional() @IsOptional()
username: string; username: string;
@ApiProperty({ description: '文件所属模块' })
@IsString()
@IsOptional()
businessModule: string;
@ApiProperty({ description: '文件上传的业务记录ID' })
@IsNumber()
@IsOptional()
bussinessRecordId: string;
@ApiProperty({ description: '附件' }) @ApiProperty({ description: '附件' })
@IsOptional() @IsOptional()
@Transform( @Transform(

View File

@ -45,6 +45,15 @@ export class Storage extends CommonEntity {
@ApiProperty({ description: '用户ID' }) @ApiProperty({ description: '用户ID' })
userId: number; userId: number;
@Column({ nullable: true, name: 'bussiness_module' })
@ApiProperty({ description: '文件上传的业务模块' })
bussinessModule: string;
@Column({ nullable: true, name: 'bussiness_record_id' })
@ApiProperty({ description: '文件上传的业务记录ID' })
bussinessRecordId: number;
@ApiHideProperty() @ApiHideProperty()
@ManyToMany(() => ContractEntity, contract => contract.files) @ManyToMany(() => ContractEntity, contract => contract.files)
contracts: Relation<ContractEntity[]>; contracts: Relation<ContractEntity[]>;

View File

@ -93,7 +93,9 @@ export class StorageService {
type: e.storage_type, type: e.storage_type,
size: e.storage_size, size: e.storage_size,
createdAt: e.storage_created_at, createdAt: e.storage_created_at,
username: e.user_username username: e.user_username,
bussinessRecordId:e.storage_bussiness_record_id,
bussinessModule:e.storage_bussiness_module
}; };
}); });
} }

View File

@ -1,4 +1,4 @@
import { BadRequestException, Controller, Post, Req } from '@nestjs/common'; import { BadRequestException, Body, Controller, Post, Req, UseInterceptors } from '@nestjs/common';
import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
import { FastifyRequest } from 'fastify'; import { FastifyRequest } from 'fastify';
@ -27,18 +27,18 @@ export class UploadController {
@ApiBody({ @ApiBody({
type: FileUploadDto type: FileUploadDto
}) })
async upload(@Req() req: FastifyRequest, @AuthUser() user: IAuthUser) { async upload(@Req() req: FastifyRequest, @AuthUser() user: IAuthUser, @Body() body: any) {
if (!req.isMultipart()) throw new BadRequestException('Request is not multipart'); if (!req.isMultipart()) throw new BadRequestException('Request is not multipart');
const { file } = body;
const file = await req.file(); const bussinessModule = body.bussinessModule?.value;
const bussinessRecordId = Number(body.bussinessRecordId?.value) || null;
// https://github.com/fastify/fastify-multipart
// const parts = req.files()
// for await (const part of parts)
// console.log(part.file)
try { try {
const savedFile = await this.uploadService.saveFile(file, user.uid); const savedFile = await this.uploadService.saveFile(
file,
user.uid,
bussinessModule,
bussinessRecordId
);
return { return {
filename: savedFile filename: savedFile

View File

@ -1,7 +1,7 @@
import { MultipartFile } from '@fastify/multipart'; import { MultipartFile } from '@fastify/multipart';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsDefined } from 'class-validator'; import { IsDefined, IsNumber, IsOptional, IsString } from 'class-validator';
import { IsFile } from './file.constraint'; import { IsFile } from './file.constraint';

View File

@ -25,7 +25,12 @@ export class UploadService {
/** /**
* *
*/ */
async saveFile(file: MultipartFile, userId: number): Promise<{ id: number; path: string }> { async saveFile(
file: MultipartFile,
userId: number,
bussinessModule?: string,
bussinessRecordId?: number
): Promise<{ id: number; path: string }> {
if (isNil(file)) throw new NotFoundException('Have not any file to upload!'); if (isNil(file)) throw new NotFoundException('Have not any file to upload!');
const fileName = file.filename; const fileName = file.filename;
@ -44,7 +49,9 @@ export class UploadService {
path, path,
type, type,
size, size,
userId userId,
bussinessModule,
bussinessRecordId
}); });
return { path, id: storage.id }; return { path, id: storage.id };