diff --git a/README.md b/README.md index af27223..eff84ea 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ pnpm migration:revert 4.执行sql覆盖docker中的数据库 ```bash -docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-05_222748.sql +docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-07_171919.sql ``` 更多细节,请移步至[官方文档](https://typeorm.io/migrations) diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 1d18590..7cc6079 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -23,3 +23,10 @@ export enum MaterialsInOrOutEnum { export enum ParamConfigEnum { MaterialsInOutPrefix = 'materials_in_out_prefix' } + +// 合同审核状态 +export enum ContractStatusEnum { + Pending = 0, // 待审核 + Approved = 1, // 已通过 + Rejected = 2 // 已拒绝 +} diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 8c187a6..2dd82b4 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -52,5 +52,8 @@ export enum ErrorEnum { STORAGE_REFRENCE_EXISTS = '1405:文件存在关联,无法删除,请先找到该文件关联的业务解除关联。', // Product - PRODUCT_EXIST = '1406:产品已存在' + PRODUCT_EXIST = '1406:产品已存在', + + // Contract + CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号' } diff --git a/src/modules/company/company.service.ts b/src/modules/company/company.service.ts index c0f2465..aca6da4 100644 --- a/src/modules/company/company.service.ts +++ b/src/modules/company/company.service.ts @@ -69,12 +69,12 @@ export class CompanyService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(CompanyEntity, 'files') .of(id) - .addAndRemove(fileIds, company.files); + .add(fileIds); } }); } diff --git a/src/modules/contract/contract.dto.ts b/src/modules/contract/contract.dto.ts index a1b7be7..3ada817 100644 --- a/src/modules/contract/contract.dto.ts +++ b/src/modules/contract/contract.dto.ts @@ -3,6 +3,7 @@ import { IsArray, IsDate, IsDateString, + IsEnum, IsIn, IsInt, IsNumber, @@ -13,6 +14,7 @@ import { } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; +import { ContractStatusEnum } from '~/constants/enum'; export class ContractDto { @ApiProperty({ description: '合同编号' }) @@ -37,15 +39,18 @@ export class ContractDto { partyB: string; @ApiProperty({ description: '签订日期' }) + @IsOptional() @IsDateString() - signingDate: string; + signingDate?: string; @ApiProperty({ description: '交付期限' }) + @IsOptional() @IsDateString() - deliveryDeadline: string; + deliveryDeadline?: string; @ApiProperty({ description: '审核状态(字典)' }) - @IsIn([0, 1, 2]) + @IsOptional() + @IsEnum(ContractStatusEnum) status: number; @ApiProperty({ description: '附件' }) diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 030ea1a..587ff1b 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; import { ContractEntity } from './contract.entity'; -import { EntityManager, Like, Repository } from 'typeorm'; +import { EntityManager, Like, Not, Repository } from 'typeorm'; import { ContractDto, ContractQueryDto, ContractUpdateDto } from './contract.dto'; import { Pagination } from '~/helper/paginate/pagination'; import { isNumber } from 'lodash'; @@ -45,23 +45,30 @@ export class ContractService { /** * 新增 */ - async create(dto: ContractDto): Promise { - await this.contractRepository.insert(dto); + async create({ contractNumber, ...ext }: ContractDto): Promise { + if (await this.checkIsContractNumberExsit(contractNumber)) { + throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST); + } + await this.contractRepository.insert( + this.contractRepository.create({ contractNumber, ...ext }) + ); } /** * 更新 */ - async update(id: number, { fileIds, ...data }: Partial): Promise { + async update( + id: number, + { fileIds, contractNumber, ...ext }: Partial + ): Promise { await this.entityManager.transaction(async manager => { + if (await this.checkIsContractNumberExsit(contractNumber, id)) { + throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST); + } await manager.update(ContractEntity, id, { - ...data + ...ext }); - const contract = await this.contractRepository - .createQueryBuilder('contract') - .leftJoinAndSelect('contract.files', 'files') - .where('contract.id = :id', { id }) - .getOne(); + if (fileIds?.length) { const count = await this.storageRepository .createQueryBuilder('storage') @@ -70,16 +77,24 @@ export class ContractService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 - await manager - .createQueryBuilder() - .relation(ContractEntity, 'files') - .of(id) - .addAndRemove(fileIds, contract.files); + // 附件要批量插入 + await manager.createQueryBuilder().relation(ContractEntity, 'files').of(id).add(fileIds); } }); } + /** + * 是否存在相同编号的合同 + * @param contractNumber 合同编号 + */ + async checkIsContractNumberExsit(contractNumber: string, id?: number): Promise { + return !!(await this.contractRepository.findOne({ + where: { + contractNumber: contractNumber, + id: Not(id) + } + })); + } /** * 删除 */ diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index 833684f..5351eb2 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -120,7 +120,7 @@ export class MaterialsInOutEntity extends CommonEntity { @JoinColumn({ name: 'product_id' }) product: ProductEntity; - @ManyToMany(() => Storage, storage => storage.materialsInOut) + @ManyToMany(() => Storage, storage => storage.materialsInOuts) @JoinTable({ name: 'materials_in_out_storage', joinColumn: { name: 'materials_in_out_id', referencedColumnName: 'id' }, diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 7c685aa..3c82855 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -46,7 +46,7 @@ export class MaterialsInOutService { .leftJoin('materialsInOut.product', 'product') .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') - .addSelect(['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)) .andWhere('materialsInOut.isDelete = 0') .addOrderBy('materialsInOut.createdAt', 'DESC'); @@ -97,11 +97,6 @@ export class MaterialsInOutService { await manager.update(MaterialsInOutEntity, id, { ...data }); - const materialsInOut = await this.materialsInOutRepository - .createQueryBuilder('materialsInOut') - .leftJoinAndSelect('materialsInOut.files', 'files') - .where('materialsInOut.id = :id', { id }) - .getOne(); if (fileIds?.length) { const count = await this.storageRepository .createQueryBuilder('storage') @@ -110,12 +105,12 @@ export class MaterialsInOutService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(MaterialsInOutEntity, 'files') .of(id) - .addAndRemove(fileIds, materialsInOut.files); + .add(fileIds); } }); } diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts index 5428a3e..88cce60 100644 --- a/src/modules/product/product.service.ts +++ b/src/modules/product/product.service.ts @@ -90,12 +90,12 @@ export class ProductService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(ProductEntity, 'files') .of(id) - .addAndRemove(fileIds, product.files); + .add(fileIds); } }); } diff --git a/src/modules/project/project.service.ts b/src/modules/project/project.service.ts index 627299c..fcd4dd0 100644 --- a/src/modules/project/project.service.ts +++ b/src/modules/project/project.service.ts @@ -69,12 +69,12 @@ export class ProjectService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(ProjectEntity, 'files') .of(id) - .addAndRemove(fileIds, project.files); + .add(fileIds); } }); } diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index cc58f42..588b033 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -55,7 +55,7 @@ export class Storage extends CommonEntity { @ApiHideProperty() @ManyToMany(() => MaterialsInOutEntity, materialsInOut => materialsInOut.files) - materialsInOut: Relation; + materialsInOuts: Relation; @ApiHideProperty() @ManyToMany(() => ProductEntity, product => product.files) diff --git a/src/modules/vehicle_usage/vehicle_usage.dto.ts b/src/modules/vehicle_usage/vehicle_usage.dto.ts index 15100a1..e210ffb 100644 --- a/src/modules/vehicle_usage/vehicle_usage.dto.ts +++ b/src/modules/vehicle_usage/vehicle_usage.dto.ts @@ -10,17 +10,22 @@ export class VehicleUsageDto { @ApiProperty({ description: '外出使用的车辆名称(字典)' }) @IsNumber() - vechicleId: number; + vehicleId: number; @ApiProperty({ description: '申请人' }) @IsString() applicant: string; - @ApiProperty({ description: '出行司机', nullable: true }) + @ApiProperty({ description: '出行司机' }) @IsOptional() @IsString() driver: string; + @ApiProperty({ description: '随行人员' }) + @IsOptional() + @IsString() + partner?: string; + @ApiProperty({ description: '当前车辆里程数(KM)' }) @IsOptional() @IsNumber() @@ -69,4 +74,8 @@ export class VehicleUsageUpdateDto extends PartialType(VehicleUsageDto) { export class VehicleUsageQueryDto extends IntersectionType( PagerDto, PartialType(VehicleUsageDto) -) {} +) { + @ApiProperty({ description: '车辆名称或者车牌号' }) + @IsOptional() + vehicle?: string; +} diff --git a/src/modules/vehicle_usage/vehicle_usage.entity.ts b/src/modules/vehicle_usage/vehicle_usage.entity.ts index 73c5dc9..6821433 100644 --- a/src/modules/vehicle_usage/vehicle_usage.entity.ts +++ b/src/modules/vehicle_usage/vehicle_usage.entity.ts @@ -12,7 +12,7 @@ export class VehicleUsageEntity extends CommonEntity { @Column({ name: 'vehicle_id', type: 'int', comment: '外出使用的车辆名称(字典)' }) @ApiProperty({ description: '外出使用的车辆名称(字典)' }) - vechicleId: number; + vehicleId: number; @Column({ name: 'applicant', @@ -27,11 +27,22 @@ export class VehicleUsageEntity extends CommonEntity { name: 'driver', type: 'varchar', length: 50, - comment: '出行司机' + comment: '出行司机', + nullable: true }) - @ApiProperty({ description: '出行司机', nullable: true }) + @ApiProperty({ description: '出行司机' }) driver: string; + @Column({ + name: 'partner', + type: 'varchar', + length: 50, + comment: '随行人员', + nullable: true + }) + @ApiProperty({ description: '随行人员' }) + partner: string; + @Column({ name: 'current_mileage', type: 'int', comment: '当前车辆里程数(KM)', nullable: true }) @ApiProperty({ description: '当前车辆里程数(KM)' }) currentMileage: number; diff --git a/src/modules/vehicle_usage/vehicle_usage.service.ts b/src/modules/vehicle_usage/vehicle_usage.service.ts index 2c064b7..b1516d2 100644 --- a/src/modules/vehicle_usage/vehicle_usage.service.ts +++ b/src/modules/vehicle_usage/vehicle_usage.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; -import { EntityManager, Repository } from 'typeorm'; +import { EntityManager, Repository, SelectQueryBuilder } from 'typeorm'; import { VehicleUsageEntity } from './vehicle_usage.entity'; import { Storage } from '../tools/storage/storage.entity'; import { ErrorEnum } from '~/constants/error-code.constant'; @@ -27,15 +27,14 @@ export class VehicleUsageService { async findAll({ page, pageSize, + ...fields }: VehicleUsageQueryDto): Promise> { - const { ...ext } = fields; - const sqb = this.vehicleUsageRepository - .createQueryBuilder('vehicle_usage') - .leftJoin('vehicle_usage.files', 'files') - .leftJoin('vehicle_usage.vehicle', 'vehicle') - .addSelect(['files.id', 'files.path', 'vehicle.id', 'vehicle.label']) - .where(fieldSearch(ext)) + const { vehicle, ...ext } = fields; + const sqb = this.buildLeftJoinRelations().where(fieldSearch(ext)); + if (vehicle) { + sqb.andWhere('vehicle.label like :vehicleName', { vehicleName: `%${vehicle}%` }); + } return paginate(sqb, { page, pageSize @@ -81,12 +80,12 @@ export class VehicleUsageService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(VehicleUsageEntity, 'files') .of(id) - .addAndRemove(fileIds, vehicleUsage.files); + .add(fileIds); } }); } @@ -102,14 +101,10 @@ export class VehicleUsageService { * 获取单个信息 */ async info(id: number) { - const info = await this.vehicleUsageRepository - .createQueryBuilder('vehicle_usage') - .leftJoin('vehicle_usage.company', 'company') - .addSelect(['company.name', 'company.id']) + const info = this.buildLeftJoinRelations() .where({ id }) - .andWhere('vehicle_usage.isDelete = 0') .getOne(); return info; } @@ -137,4 +132,15 @@ export class VehicleUsageService { .addAndRemove(linkedFiles, vehicle_usage.files); }); } + + /** + * 封装和查询关联关系 + */ + buildLeftJoinRelations() { + return this.vehicleUsageRepository + .createQueryBuilder('vehicle_usage') + .leftJoin('vehicle_usage.files', 'files') + .leftJoin('vehicle_usage.vehicle', 'vehicle') + .addSelect(['files.id', 'files.path', 'vehicle.id', 'vehicle.label']); + } }