mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-26 18:24:54 +00:00
370 lines
11 KiB
TypeScript
370 lines
11 KiB
TypeScript
import { In, QueryRunner } from 'typeorm';
|
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
|
|
|
import {
|
|
SpaceModelEntity,
|
|
SpaceModelProductAllocationEntity,
|
|
SpaceModelProductAllocationRepoitory,
|
|
SubspaceModelProductAllocationEntity,
|
|
} from '@app/common/modules/space-model';
|
|
import { TagService as NewTagService } from 'src/tags/services';
|
|
import { ProcessTagDto } from 'src/tags/dtos';
|
|
import { ModifySubspaceModelDto, ModifyTagModelDto } from '../dtos';
|
|
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
|
import { NewTagEntity } from '@app/common/modules/tag';
|
|
import { ProductEntity } from '@app/common/modules/product/entities';
|
|
|
|
@Injectable()
|
|
export class SpaceModelProductAllocationService {
|
|
constructor(
|
|
private readonly tagService: NewTagService,
|
|
private readonly spaceModelProductAllocationRepository: SpaceModelProductAllocationRepoitory,
|
|
) {}
|
|
|
|
async createProductAllocations(
|
|
projectUuid: string,
|
|
spaceModel: SpaceModelEntity,
|
|
tags: ProcessTagDto[],
|
|
queryRunner?: QueryRunner,
|
|
modifySubspaceModels?: ModifySubspaceModelDto[],
|
|
): Promise<SpaceModelProductAllocationEntity[]> {
|
|
try {
|
|
if (!tags.length) return [];
|
|
|
|
const processedTags = await this.tagService.processTags(
|
|
tags,
|
|
projectUuid,
|
|
queryRunner,
|
|
);
|
|
|
|
const productAllocations: SpaceModelProductAllocationEntity[] = [];
|
|
const existingAllocations = new Map<
|
|
string,
|
|
SpaceModelProductAllocationEntity
|
|
>();
|
|
|
|
for (const tag of processedTags) {
|
|
let isTagNeeded = true;
|
|
|
|
if (modifySubspaceModels) {
|
|
const relatedSubspaces = await queryRunner.manager.find(
|
|
SubspaceModelProductAllocationEntity,
|
|
{
|
|
where: {
|
|
product: tag.product,
|
|
subspaceModel: { spaceModel: { uuid: spaceModel.uuid } },
|
|
tags: { uuid: tag.uuid },
|
|
},
|
|
relations: ['subspaceModel', 'tags'],
|
|
},
|
|
);
|
|
|
|
for (const subspaceWithTag of relatedSubspaces) {
|
|
const modifyingSubspace = modifySubspaceModels.find(
|
|
(subspace) =>
|
|
subspace.action === ModifyAction.UPDATE &&
|
|
subspace.uuid === subspaceWithTag.subspaceModel.uuid,
|
|
);
|
|
|
|
if (
|
|
modifyingSubspace &&
|
|
modifyingSubspace.tags &&
|
|
modifyingSubspace.tags.some(
|
|
(subspaceTag) =>
|
|
subspaceTag.action === ModifyAction.DELETE &&
|
|
subspaceTag.tagUuid === tag.uuid,
|
|
)
|
|
) {
|
|
isTagNeeded = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isTagNeeded) {
|
|
await this.validateTagWithinSpaceModel(queryRunner, tag, spaceModel);
|
|
|
|
let allocation = existingAllocations.get(tag.product.uuid);
|
|
if (!allocation) {
|
|
allocation = await this.getAllocationByProduct(
|
|
tag.product,
|
|
spaceModel,
|
|
queryRunner,
|
|
);
|
|
if (allocation) {
|
|
existingAllocations.set(tag.product.uuid, allocation);
|
|
}
|
|
}
|
|
|
|
if (!allocation) {
|
|
allocation = this.createNewAllocation(spaceModel, tag, queryRunner);
|
|
productAllocations.push(allocation);
|
|
} else if (!allocation.tags.some((t) => t.uuid === tag.uuid)) {
|
|
allocation.tags.push(tag);
|
|
await this.saveAllocation(allocation, queryRunner);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (productAllocations.length > 0) {
|
|
await this.saveAllocations(productAllocations, queryRunner);
|
|
}
|
|
|
|
return productAllocations;
|
|
} catch (error) {
|
|
throw this.handleError(error, 'Failed to create product allocations');
|
|
}
|
|
}
|
|
|
|
async updateProductAllocations(
|
|
dtos: ModifyTagModelDto[],
|
|
projectUuid: string,
|
|
spaceModel: SpaceModelEntity,
|
|
queryRunner: QueryRunner,
|
|
modifySubspaceModels?: ModifySubspaceModelDto[],
|
|
): Promise<void> {
|
|
try {
|
|
await Promise.all([
|
|
this.processAddActions(
|
|
dtos,
|
|
projectUuid,
|
|
spaceModel,
|
|
queryRunner,
|
|
modifySubspaceModels,
|
|
),
|
|
this.processDeleteActions(dtos, queryRunner),
|
|
]);
|
|
} catch (error) {
|
|
throw this.handleError(error, 'Error while updating product allocations');
|
|
}
|
|
}
|
|
|
|
private async processAddActions(
|
|
dtos: ModifyTagModelDto[],
|
|
projectUuid: string,
|
|
spaceModel: SpaceModelEntity,
|
|
queryRunner: QueryRunner,
|
|
modifySubspaceModels?: ModifySubspaceModelDto[],
|
|
): Promise<void> {
|
|
const addDtos: ProcessTagDto[] = dtos
|
|
.filter((dto) => dto.action === ModifyAction.ADD)
|
|
.map((dto) => ({
|
|
name: dto.name,
|
|
productUuid: dto.productUuid,
|
|
uuid: dto.newTagUuid,
|
|
}));
|
|
|
|
if (addDtos.length > 0) {
|
|
await this.createProductAllocations(
|
|
projectUuid,
|
|
spaceModel,
|
|
addDtos,
|
|
queryRunner,
|
|
modifySubspaceModels,
|
|
);
|
|
}
|
|
}
|
|
|
|
private createNewAllocation(
|
|
spaceModel: SpaceModelEntity,
|
|
tag: NewTagEntity,
|
|
queryRunner?: QueryRunner,
|
|
): SpaceModelProductAllocationEntity {
|
|
return queryRunner
|
|
? queryRunner.manager.create(SpaceModelProductAllocationEntity, {
|
|
spaceModel,
|
|
product: tag.product,
|
|
tags: [tag],
|
|
})
|
|
: this.spaceModelProductAllocationRepository.create({
|
|
spaceModel,
|
|
product: tag.product,
|
|
tags: [tag],
|
|
});
|
|
}
|
|
|
|
private async getAllocationByProduct(
|
|
product: ProductEntity,
|
|
spaceModel: SpaceModelEntity,
|
|
queryRunner?: QueryRunner,
|
|
): Promise<SpaceModelProductAllocationEntity | null> {
|
|
return queryRunner
|
|
? queryRunner.manager.findOne(SpaceModelProductAllocationEntity, {
|
|
where: { spaceModel, product: product },
|
|
relations: ['tags'],
|
|
})
|
|
: this.spaceModelProductAllocationRepository.findOne({
|
|
where: { spaceModel, product: product },
|
|
relations: ['tags'],
|
|
});
|
|
}
|
|
|
|
private async saveAllocation(
|
|
allocation: SpaceModelProductAllocationEntity,
|
|
queryRunner?: QueryRunner,
|
|
) {
|
|
queryRunner
|
|
? await queryRunner.manager.save(
|
|
SpaceModelProductAllocationEntity,
|
|
allocation,
|
|
)
|
|
: await this.spaceModelProductAllocationRepository.save(allocation);
|
|
}
|
|
|
|
private async saveAllocations(
|
|
allocations: SpaceModelProductAllocationEntity[],
|
|
queryRunner?: QueryRunner,
|
|
) {
|
|
queryRunner
|
|
? await queryRunner.manager.save(
|
|
SpaceModelProductAllocationEntity,
|
|
allocations,
|
|
)
|
|
: await this.spaceModelProductAllocationRepository.save(allocations);
|
|
}
|
|
|
|
private handleError(error: any, message: string): HttpException {
|
|
return new HttpException(
|
|
error instanceof HttpException ? error.message : message,
|
|
error instanceof HttpException
|
|
? error.getStatus()
|
|
: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
|
|
private async processDeleteActions(
|
|
dtos: ModifyTagModelDto[],
|
|
queryRunner: QueryRunner,
|
|
): Promise<SpaceModelProductAllocationEntity[]> {
|
|
try {
|
|
if (!dtos || dtos.length === 0) {
|
|
throw new Error('No DTOs provided for deletion.');
|
|
}
|
|
|
|
const tagUuidsToDelete = dtos
|
|
.filter((dto) => dto.action === ModifyAction.DELETE && dto.tagUuid)
|
|
.map((dto) => dto.tagUuid);
|
|
|
|
if (tagUuidsToDelete.length === 0) return [];
|
|
|
|
const allocationsToUpdate = await queryRunner.manager.find(
|
|
SpaceModelProductAllocationEntity,
|
|
{
|
|
where: { tags: { uuid: In(tagUuidsToDelete) } },
|
|
relations: ['tags'],
|
|
},
|
|
);
|
|
|
|
if (!allocationsToUpdate || allocationsToUpdate.length === 0) return [];
|
|
|
|
const deletedAllocations: SpaceModelProductAllocationEntity[] = [];
|
|
const allocationUpdates: SpaceModelProductAllocationEntity[] = [];
|
|
|
|
for (const allocation of allocationsToUpdate) {
|
|
const updatedTags = allocation.tags.filter(
|
|
(tag) => !tagUuidsToDelete.includes(tag.uuid),
|
|
);
|
|
|
|
if (updatedTags.length === allocation.tags.length) {
|
|
continue;
|
|
}
|
|
|
|
if (updatedTags.length === 0) {
|
|
deletedAllocations.push(allocation);
|
|
} else {
|
|
allocation.tags = updatedTags;
|
|
allocationUpdates.push(allocation);
|
|
}
|
|
}
|
|
|
|
if (allocationUpdates.length > 0) {
|
|
await queryRunner.manager.save(
|
|
SpaceModelProductAllocationEntity,
|
|
allocationUpdates,
|
|
);
|
|
}
|
|
|
|
if (deletedAllocations.length > 0) {
|
|
await queryRunner.manager.remove(
|
|
SpaceModelProductAllocationEntity,
|
|
deletedAllocations,
|
|
);
|
|
}
|
|
|
|
await queryRunner.manager
|
|
.createQueryBuilder()
|
|
.delete()
|
|
.from('space_model_product_tags')
|
|
.where(
|
|
'space_model_product_allocation_id NOT IN ' +
|
|
queryRunner.manager
|
|
.createQueryBuilder()
|
|
.select('uuid')
|
|
.from(SpaceModelProductAllocationEntity, 'allocation')
|
|
.getQuery(),
|
|
)
|
|
.execute();
|
|
|
|
return deletedAllocations;
|
|
} catch (error) {
|
|
throw this.handleError(error, `Failed to delete tags in space model`);
|
|
}
|
|
}
|
|
|
|
private async validateTagWithinSpaceModel(
|
|
queryRunner: QueryRunner,
|
|
tag: NewTagEntity,
|
|
spaceModel: SpaceModelEntity,
|
|
) {
|
|
const existingAllocationsForProduct = await queryRunner.manager.find(
|
|
SpaceModelProductAllocationEntity,
|
|
{
|
|
where: { spaceModel, product: tag.product },
|
|
relations: ['tags'],
|
|
},
|
|
);
|
|
|
|
//Flatten all existing tags for this product in the space
|
|
const existingTagsForProduct = existingAllocationsForProduct.flatMap(
|
|
(allocation) => allocation.tags,
|
|
);
|
|
|
|
// Check if the tag is already assigned to the same product in this subspace
|
|
const isDuplicateTag = existingTagsForProduct.some(
|
|
(existingTag) => existingTag.uuid === tag.uuid,
|
|
);
|
|
|
|
if (isDuplicateTag) {
|
|
throw new HttpException(
|
|
`Tag ${tag.uuid} is already allocated to product ${tag.product.uuid} within this space (${spaceModel.uuid}).`,
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
}
|
|
|
|
async clearAllAllocations(spaceModelUuid: string, queryRunner: QueryRunner) {
|
|
try {
|
|
await queryRunner.manager
|
|
.createQueryBuilder()
|
|
.delete()
|
|
.from(SpaceModelProductAllocationEntity, 'allocation')
|
|
.relation(SpaceModelProductAllocationEntity, 'tags')
|
|
.of(
|
|
await queryRunner.manager.find(SpaceModelProductAllocationEntity, {
|
|
where: { spaceModel: { uuid: spaceModelUuid } },
|
|
}),
|
|
)
|
|
.execute();
|
|
|
|
await queryRunner.manager
|
|
.createQueryBuilder()
|
|
.delete()
|
|
.from(SpaceModelProductAllocationEntity)
|
|
.where('spaceModelUuid = :spaceModelUuid', { spaceModelUuid })
|
|
.execute();
|
|
} catch (error) {
|
|
throw this.handleError(error, `Failed to clear all allocations`);
|
|
}
|
|
}
|
|
}
|