diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts new file mode 100644 index 0000000..5444016 --- /dev/null +++ b/src/constants/enum/index.ts @@ -0,0 +1,6 @@ +export enum DictTypeStatusEnum { + /** 启用 */ + ENABLE = 1, + /** 禁用 */ + DISABLE = 0 +} diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index c5d1b06..f9c0cdc 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -1,4 +1,70 @@ -import { Controller } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; +import { ContractQueryDto, ContractDto, ContractUpdateDto } from '../contract/contract.dto'; +import { MaterialsInventoryService } from './materials_inventory.service'; +import { MaterialsInventoryEntity } from './materials_inventory.entity'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +export const permissions = definePermission('app:materials_inventory', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('MaterialsI Inventory - 原材料盘点') +@ApiSecurityAuth() @Controller('materials-inventory') -export class MaterialsInventoryController {} +export class MaterialsInventoryController { + constructor(private miService: MaterialsInventoryService) {} + @Get() + @ApiOperation({ summary: '获取原材料盘点列表' }) + @ApiResult({ type: [MaterialsInventoryEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: ContractQueryDto) { + return this.miService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取原材料盘点信息' }) + @ApiResult({ type: ContractDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.miService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增原材料盘点' }) + @Perm(permissions.CREATE) + async create(@Body() dto: ContractDto): Promise { + await this.miService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新原材料盘点' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: ContractUpdateDto): Promise { + await this.miService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除原材料盘点' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.miService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: ContractUpdateDto + ): Promise { + await this.miService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts new file mode 100644 index 0000000..85c839f --- /dev/null +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { + IsArray, + IsDate, + IsDateString, + IsIn, + IsInt, + IsNumber, + IsOptional, + IsString, + Matches, + MinLength +} from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '../tools/storage/storage.entity'; + +export class MaterialsInventoryDto { + @ApiProperty({ description: '附件' }) + files: Storage[]; +} + +export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} +export class MaterialsInventoryQueryDto extends IntersectionType( + PagerDto, + PartialType(MaterialsInventoryDto) +) {} diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index c0d429e..039574a 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -1,9 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity } from 'typeorm'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '../tools/storage/storage.entity'; -@Entity({ name: 'meterials_inventory' }) -export class MeterialsInventoryEntity extends CommonEntity { +@Entity({ name: 'materials_inventory' }) +export class MaterialsInventoryEntity extends CommonEntity { @Column({ name: 'company_name', type: 'varchar', length: 255, comment: '公司名称' }) @ApiProperty({ description: '公司名称' }) companyName: number; @@ -182,4 +183,20 @@ export class MeterialsInventoryEntity extends CommonEntity { @Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true }) @ApiProperty({ description: '备注' }) remark: string; + + @Column({ name: 'project', type: 'varchar', length: 255, comment: '项目', nullable: false }) + @ApiProperty({ description: '项目' }) + project: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ManyToMany(() => Storage, storage => storage.contracts) + @JoinTable({ + name: 'materials_inventory_storage', + joinColumn: { name: 'materials_inventory_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; } diff --git a/src/modules/materials_inventory/materials_inventory.module.ts b/src/modules/materials_inventory/materials_inventory.module.ts index 0572e3d..b376835 100644 --- a/src/modules/materials_inventory/materials_inventory.module.ts +++ b/src/modules/materials_inventory/materials_inventory.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common'; import { MaterialsInventoryController } from './materials_inventory.controller'; import { MaterialsInventoryService } from './materials_inventory.service'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { MeterialsInventoryEntity } from './materials_inventory.entity'; +import { MaterialsInventoryEntity } from './materials_inventory.entity'; +import { StorageModule } from '../tools/storage/storage.module'; @Module({ - imports: [TypeOrmModule.forFeature([MeterialsInventoryEntity])], + imports: [TypeOrmModule.forFeature([MaterialsInventoryEntity]), StorageModule], controllers: [MaterialsInventoryController], providers: [MaterialsInventoryService] }) diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 44c7be2..00a7b7e 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -1,4 +1,140 @@ import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { MaterialsInventoryEntity } from './materials_inventory.entity'; +import { Storage } from '../tools/storage/storage.entity'; +import { EntityManager, Repository } from 'typeorm'; +import { + MaterialsInventoryDto, + MaterialsInventoryQueryDto, + MaterialsInventoryUpdateDto +} from './materials_inventory.dto'; +import { Pagination } from '~/helper/paginate/pagination'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { paginate } from '~/helper/paginate'; @Injectable() -export class MaterialsInventoryService {} +export class MaterialsInventoryService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(MaterialsInventoryEntity) + private materialsInventoryRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + /** + * 列举所有角色:除去超级管理员 + */ + async findAll({ + page, + pageSize + // materialsInventoryNumber, + // title, + // type, + // status + }: MaterialsInventoryQueryDto): Promise> { + const queryBuilder = this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoin('materialsInventory.files', 'files') + .addSelect(['files.id', 'files.path']) + // .where({ + // ...(materialsInventoryNumber ? { materialsInventoryNumber: Like(`%${materialsInventoryNumber}%`) } : null), + // ...(title ? { title: Like(`%${title}%`) } : null), + // ...(isNumber(type) ? { type } : null), + // ...(isNumber(status) ? { status } : null) + // }) + .andWhere('materialsInventory.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: MaterialsInventoryDto): Promise { + await this.materialsInventoryRepository.insert(dto); + } + + /** + * 更新 + */ + async update( + id: number, + { fileIds, ...data }: Partial + ): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(MaterialsInventoryEntity, id, { + ...data + }); + const materialsInventory = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoinAndSelect('materialsInventory.files', 'files') + .where('materialsInventory.id = :id', { id }) + .getOne(); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(MaterialsInventoryEntity, 'files') + .of(id) + .addAndRemove(fileIds, materialsInventory.files); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 合同比较重要,做逻辑删除 + await this.materialsInventoryRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个合同信息 + */ + async info(id: number) { + const info = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .where({ + id + }) + .andWhere('materialsInventory.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 合同ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const materialsInventory = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoinAndSelect('materialsInventory.files', 'files') + .where('materialsInventory.id = :id', { id }) + .getOne(); + const linkedFiles = materialsInventory.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(MaterialsInventoryEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, materialsInventory.files); + }); + } +} diff --git a/src/modules/system/dict-type/dict-type.controller.ts b/src/modules/system/dict-type/dict-type.controller.ts index 8c3c347..d434dc8 100644 --- a/src/modules/system/dict-type/dict-type.controller.ts +++ b/src/modules/system/dict-type/dict-type.controller.ts @@ -34,12 +34,12 @@ export class DictTypeController { return this.dictTypeService.page(dto); } - @Get('select-options') + @Post('all') @ApiOperation({ summary: '一次性获取所有的字典类型(不分页)' }) @ApiResult({ type: [DictTypeEntity] }) @Perm(permissions.LIST) - async getAll(): Promise { - return this.dictTypeService.getAll(); + async getAll(@Body() dto: DictTypeQueryDto): Promise { + return this.dictTypeService.getAll(dto); } @Post() diff --git a/src/modules/system/dict-type/dict-type.dto.ts b/src/modules/system/dict-type/dict-type.dto.ts index 010479f..9ee5771 100644 --- a/src/modules/system/dict-type/dict-type.dto.ts +++ b/src/modules/system/dict-type/dict-type.dto.ts @@ -1,9 +1,10 @@ import { ApiProperty, PartialType } from '@nestjs/swagger'; -import { IsInt, IsOptional, IsString, MinLength } from 'class-validator'; +import { IsArray, IsBoolean, IsInt, IsOptional, IsString, MinLength } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; import { DictTypeEntity } from './dict-type.entity'; +import { isBoolean } from 'lodash'; export class DictTypeDto extends PartialType(DictTypeEntity) { @ApiProperty({ description: '字典类型名称' }) @@ -37,4 +38,14 @@ export class DictTypeQueryDto extends PagerDto { @IsString() @IsOptional() code: string; + + @ApiProperty({ description: '是否用于前端store缓存' }) + @IsOptional() + @IsBoolean() + withItems: boolean; + + @ApiProperty({ description: '需要前端store缓存的code' }) + @IsArray() + @IsOptional() + storeCodes: string[]; } diff --git a/src/modules/system/dict-type/dict-type.entity.ts b/src/modules/system/dict-type/dict-type.entity.ts index e4f0e8c..d9e46ad 100644 --- a/src/modules/system/dict-type/dict-type.entity.ts +++ b/src/modules/system/dict-type/dict-type.entity.ts @@ -1,7 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity } from 'typeorm'; +import { Column, Entity, OneToMany, Relation } from 'typeorm'; import { CompleteEntity } from '~/common/entity/common.entity'; +import { DictItemEntity } from '../dict-item/dict-item.entity'; @Entity({ name: 'sys_dict_type' }) export class DictTypeEntity extends CompleteEntity { @@ -20,4 +21,9 @@ export class DictTypeEntity extends CompleteEntity { @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '备注' }) remark: string; + + @OneToMany(() => DictItemEntity, dictItem => dictItem.type, { + cascade: true + }) + dictItems: Relation; } diff --git a/src/modules/system/dict-type/dict-type.service.ts b/src/modules/system/dict-type/dict-type.service.ts index cda4d92..dd422dd 100644 --- a/src/modules/system/dict-type/dict-type.service.ts +++ b/src/modules/system/dict-type/dict-type.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Like, Repository } from 'typeorm'; +import { In, Like, Repository } from 'typeorm'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; @@ -10,6 +10,7 @@ import { Pagination } from '~/helper/paginate/pagination'; import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity'; import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto'; +import { DictTypeStatusEnum } from '~/constants/enum'; @Injectable() export class DictTypeService { @@ -36,8 +37,19 @@ export class DictTypeService { } /** 一次性获取所有的字典类型 */ - async getAll() { - return this.dictTypeRepository.find(); + async getAll({ withItems, storeCodes }: DictTypeQueryDto) { + const sqb = this.dictTypeRepository + .createQueryBuilder('dict_type') + .addSelect(['dict_type.name', 'dict_type.code', 'dict_type.status']); + if (withItems) { + sqb + .leftJoin('dict_type.dictItems', 'dictItems') + .addSelect(['dictItems.id', 'dictItems.value', 'dictItems.label', 'dictItems.status']); + } + sqb.where('dict_type.status =:status', { status: DictTypeStatusEnum.ENABLE }); + withItems && sqb.andWhere('dictItems.status =:status', { status: DictTypeStatusEnum.ENABLE }); + storeCodes && sqb.andWhere({ code: In(storeCodes) }); + return sqb.getMany(); } /**