mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 18:27:05 +00:00
Merge pull request #339 from SyncrowIOT/feat/commission-device
Feat/commission device
This commit is contained in:
@ -43,6 +43,11 @@ export class ControllerRoute {
|
|||||||
'Get user by uuid in project';
|
'Get user by uuid in project';
|
||||||
public static readonly GET_USER_BY_UUID_IN_PROJECT_DESCRIPTION =
|
public static readonly GET_USER_BY_UUID_IN_PROJECT_DESCRIPTION =
|
||||||
'This endpoint retrieves a user by their unique identifier (UUID) associated with a specific project.';
|
'This endpoint retrieves a user by their unique identifier (UUID) associated with a specific project.';
|
||||||
|
|
||||||
|
public static readonly EXPORT_STRUCTURE_CSV_SUMMARY =
|
||||||
|
'Export project with their full structure to a CSV file';
|
||||||
|
public static readonly EXPORT_STRUCTURE_CSV_DESCRIPTION =
|
||||||
|
'This endpoint exports project along with their associated communities, spaces, and nested space hierarchy into a downloadable CSV file. Useful for backups, reports, or audits';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
static PROJECT_USER = class {
|
static PROJECT_USER = class {
|
||||||
|
@ -3,6 +3,7 @@ import { RoleType } from './role.type.enum';
|
|||||||
export const RolePermissions = {
|
export const RolePermissions = {
|
||||||
[RoleType.SUPER_ADMIN]: [
|
[RoleType.SUPER_ADMIN]: [
|
||||||
'DEVICE_SINGLE_CONTROL',
|
'DEVICE_SINGLE_CONTROL',
|
||||||
|
'COMMISSION_DEVICE',
|
||||||
'DEVICE_VIEW',
|
'DEVICE_VIEW',
|
||||||
'DEVICE_DELETE',
|
'DEVICE_DELETE',
|
||||||
'DEVICE_UPDATE',
|
'DEVICE_UPDATE',
|
||||||
@ -58,6 +59,7 @@ export const RolePermissions = {
|
|||||||
'PRODUCT_ADD',
|
'PRODUCT_ADD',
|
||||||
],
|
],
|
||||||
[RoleType.ADMIN]: [
|
[RoleType.ADMIN]: [
|
||||||
|
'COMMISSION_DEVICE',
|
||||||
'DEVICE_SINGLE_CONTROL',
|
'DEVICE_SINGLE_CONTROL',
|
||||||
'DEVICE_VIEW',
|
'DEVICE_VIEW',
|
||||||
'DEVICE_DELETE',
|
'DEVICE_DELETE',
|
||||||
@ -127,6 +129,7 @@ export const RolePermissions = {
|
|||||||
'SCENES_CONTROL',
|
'SCENES_CONTROL',
|
||||||
],
|
],
|
||||||
[RoleType.SPACE_OWNER]: [
|
[RoleType.SPACE_OWNER]: [
|
||||||
|
'COMMISSION_DEVICE',
|
||||||
'DEVICE_SINGLE_CONTROL',
|
'DEVICE_SINGLE_CONTROL',
|
||||||
'DEVICE_VIEW',
|
'DEVICE_VIEW',
|
||||||
'DEVICE_DELETE',
|
'DEVICE_DELETE',
|
||||||
|
@ -78,7 +78,7 @@ export class DeviceEntity extends AbstractEntity<DeviceDto> {
|
|||||||
|
|
||||||
@OneToMany(() => NewTagEntity, (tag) => tag.devices)
|
@OneToMany(() => NewTagEntity, (tag) => tag.devices)
|
||||||
// @JoinTable({ name: 'device_tags' })
|
// @JoinTable({ name: 'device_tags' })
|
||||||
public tag: NewTagEntity[];
|
public tag: NewTagEntity;
|
||||||
|
|
||||||
constructor(partial: Partial<DeviceEntity>) {
|
constructor(partial: Partial<DeviceEntity>) {
|
||||||
super();
|
super();
|
||||||
|
@ -8,7 +8,6 @@ import { SpaceLinkEntity } from './space-link.entity';
|
|||||||
import { SceneEntity } from '../../scene/entities';
|
import { SceneEntity } from '../../scene/entities';
|
||||||
import { SpaceModelEntity } from '../../space-model';
|
import { SpaceModelEntity } from '../../space-model';
|
||||||
import { InviteUserSpaceEntity } from '../../Invite-user/entities';
|
import { InviteUserSpaceEntity } from '../../Invite-user/entities';
|
||||||
import { TagEntity } from './tag.entity';
|
|
||||||
import { SpaceProductAllocationEntity } from './space-product-allocation.entity';
|
import { SpaceProductAllocationEntity } from './space-product-allocation.entity';
|
||||||
import { SubspaceEntity } from './subspace/subspace.entity';
|
import { SubspaceEntity } from './subspace/subspace.entity';
|
||||||
|
|
||||||
@ -103,9 +102,6 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
|||||||
)
|
)
|
||||||
invitedUsers: InviteUserSpaceEntity[];
|
invitedUsers: InviteUserSpaceEntity[];
|
||||||
|
|
||||||
@OneToMany(() => TagEntity, (tag) => tag.space)
|
|
||||||
tags: TagEntity[];
|
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany(
|
||||||
() => SpaceProductAllocationEntity,
|
() => SpaceProductAllocationEntity,
|
||||||
(allocation) => allocation.space,
|
(allocation) => allocation.space,
|
||||||
|
@ -3,7 +3,6 @@ import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
|||||||
import { ProductEntity } from '../../product/entities';
|
import { ProductEntity } from '../../product/entities';
|
||||||
import { TagDto } from '../dtos';
|
import { TagDto } from '../dtos';
|
||||||
import { TagModel } from '../../space-model/entities/tag-model.entity';
|
import { TagModel } from '../../space-model/entities/tag-model.entity';
|
||||||
import { SpaceEntity } from './space.entity';
|
|
||||||
import { DeviceEntity } from '../../device/entities';
|
import { DeviceEntity } from '../../device/entities';
|
||||||
import { SubspaceEntity } from './subspace/subspace.entity';
|
import { SubspaceEntity } from './subspace/subspace.entity';
|
||||||
|
|
||||||
@ -22,9 +21,6 @@ export class TagEntity extends AbstractEntity<TagDto> {
|
|||||||
})
|
})
|
||||||
product: ProductEntity;
|
product: ProductEntity;
|
||||||
|
|
||||||
@ManyToOne(() => SpaceEntity, (space) => space.tags, { nullable: true })
|
|
||||||
space: SpaceEntity;
|
|
||||||
|
|
||||||
@ManyToOne(() => SubspaceEntity, (subspace) => subspace.tags, {
|
@ManyToOne(() => SubspaceEntity, (subspace) => subspace.tags, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
|
39
package-lock.json
generated
39
package-lock.json
generated
@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fast-csv/format": "^5.0.2",
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.0.0",
|
||||||
"@nestjs/config": "^3.2.0",
|
"@nestjs/config": "^3.2.0",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
@ -913,6 +914,19 @@
|
|||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fast-csv/format": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-fRYcWvI8vs0Zxa/8fXd/QlmQYWWkJqKZPAXM+vksnplb3owQFKTPPh9JqOtD0L3flQw/AZjjXdPkD7Kp/uHm8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.escaperegexp": "^4.1.2",
|
||||||
|
"lodash.isboolean": "^3.0.3",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"lodash.isfunction": "^3.0.9",
|
||||||
|
"lodash.isnil": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@firebase/analytics": {
|
"node_modules/@firebase/analytics": {
|
||||||
"version": "0.10.8",
|
"version": "0.10.8",
|
||||||
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.8.tgz",
|
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.8.tgz",
|
||||||
@ -9441,6 +9455,12 @@
|
|||||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.escaperegexp": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.includes": {
|
"node_modules/lodash.includes": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||||
@ -9459,12 +9479,31 @@
|
|||||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.isequal": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||||
|
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isfunction": {
|
||||||
|
"version": "3.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
|
||||||
|
"integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.isinteger": {
|
"node_modules/lodash.isinteger": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.isnil": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.isnumber": {
|
"node_modules/lodash.isnumber": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"test:e2e": "jest --config ./apps/backend/test/jest-e2e.json"
|
"test:e2e": "jest --config ./apps/backend/test/jest-e2e.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fast-csv/format": "^5.0.2",
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.0.0",
|
||||||
"@nestjs/config": "^3.2.0",
|
"@nestjs/config": "^3.2.0",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
|
@ -19,6 +19,8 @@ import {
|
|||||||
SceneRepository,
|
SceneRepository,
|
||||||
} from '@app/common/modules/scene/repositories';
|
} from '@app/common/modules/scene/repositories';
|
||||||
import { AutomationRepository } from '@app/common/modules/automation/repositories';
|
import { AutomationRepository } from '@app/common/modules/automation/repositories';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, SpaceRepositoryModule],
|
imports: [ConfigModule, SpaceRepositoryModule],
|
||||||
@ -39,6 +41,8 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie
|
|||||||
SceneIconRepository,
|
SceneIconRepository,
|
||||||
SceneRepository,
|
SceneRepository,
|
||||||
AutomationRepository,
|
AutomationRepository,
|
||||||
|
CommunityRepository,
|
||||||
|
SubspaceRepository,
|
||||||
],
|
],
|
||||||
exports: [],
|
exports: [],
|
||||||
})
|
})
|
||||||
|
@ -22,9 +22,10 @@ import { ControllerRoute } from '@app/common/constants/controller-route';
|
|||||||
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
||||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
import { CommissionDeviceCsvDto } from '../dto';
|
import { CommissionDeviceCsvDto } from '../dto';
|
||||||
import { CommunityParam } from '@app/common/dto/community-space.param';
|
|
||||||
import { DeviceCommissionService } from '../services';
|
import { DeviceCommissionService } from '../services';
|
||||||
import { ProjectParam } from '@app/common/dto/project-param.dto';
|
import { ProjectParam } from '@app/common/dto/project-param.dto';
|
||||||
|
import { Permissions } from 'src/decorators/permissions.decorator';
|
||||||
|
import { PermissionsGuard } from 'src/guards/permissions.guard';
|
||||||
|
|
||||||
@ApiTags('Commission Devices Module')
|
@ApiTags('Commission Devices Module')
|
||||||
@Controller({
|
@Controller({
|
||||||
@ -34,6 +35,8 @@ import { ProjectParam } from '@app/common/dto/project-param.dto';
|
|||||||
export class DeviceCommissionController {
|
export class DeviceCommissionController {
|
||||||
constructor(private readonly commissionService: DeviceCommissionService) {}
|
constructor(private readonly commissionService: DeviceCommissionService) {}
|
||||||
|
|
||||||
|
@UseGuards(PermissionsGuard)
|
||||||
|
@Permissions('COMMISSION_DEVICE')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Post()
|
@Post()
|
||||||
@ApiConsumes('multipart/form-data')
|
@ApiConsumes('multipart/form-data')
|
||||||
@ -65,7 +68,7 @@ export class DeviceCommissionController {
|
|||||||
@Param() param: ProjectParam,
|
@Param() param: ProjectParam,
|
||||||
@Req() req: any,
|
@Req() req: any,
|
||||||
): Promise<BaseResponseDto> {
|
): Promise<BaseResponseDto> {
|
||||||
await this.commissionService.processCsv(file.path);
|
await this.commissionService.processCsv(param, file.path);
|
||||||
return {
|
return {
|
||||||
message: 'CSV file received and processing started',
|
message: 'CSV file received and processing started',
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -2,44 +2,185 @@ import * as fs from 'fs';
|
|||||||
import * as csv from 'csv-parser';
|
import * as csv from 'csv-parser';
|
||||||
|
|
||||||
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { DeviceService } from 'src/device/services';
|
import { DeviceService } from 'src/device/services';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
import { SpaceRepository } from '@app/common/modules/space';
|
||||||
|
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
||||||
|
import { SubspaceEntity } from '@app/common/modules/space/entities/subspace/subspace.entity';
|
||||||
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||||
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
|
import { ProjectParam } from '@app/common/dto/project-param.dto';
|
||||||
|
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DeviceCommissionService {
|
export class DeviceCommissionService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly tuyaService: TuyaService,
|
private readonly tuyaService: TuyaService,
|
||||||
private readonly deviceService: DeviceService,
|
private readonly deviceService: DeviceService,
|
||||||
|
private readonly communityRepository: CommunityRepository,
|
||||||
|
private readonly spaceRepository: SpaceRepository,
|
||||||
|
private readonly subspaceRepository: SubspaceRepository,
|
||||||
|
private readonly deviceRepository: DeviceRepository,
|
||||||
|
private readonly projectRepository: ProjectRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async processCsv(filePath: string): Promise<void> {
|
async processCsv(param: ProjectParam, filePath: string) {
|
||||||
return new Promise((resolve, reject) => {
|
const successCount = { value: 0 };
|
||||||
const results = [];
|
const failureCount = { value: 0 };
|
||||||
|
|
||||||
fs.createReadStream(filePath)
|
const projectId = param.projectUuid;
|
||||||
.pipe(csv())
|
|
||||||
.on('data', async (row) => {
|
|
||||||
console.log(`Device: ${JSON.stringify(row)}`);
|
|
||||||
const deviceId = row.deviceId?.trim();
|
|
||||||
|
|
||||||
if (!deviceId) {
|
const project = await this.projectRepository.findOne({
|
||||||
console.error('Missing deviceId or deviceName in row:', row);
|
where: { uuid: projectId },
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
const device = await this.tuyaService.getDeviceDetails(
|
|
||||||
row.deviceId,
|
|
||||||
);
|
|
||||||
console.log(device);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('end', () => {
|
|
||||||
console.log(`Finished processing ${results.length} devices.`);
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.on('error', (error) => {
|
|
||||||
console.error('Error reading CSV', error);
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
throw new HttpException('Project not found', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows: any[] = [];
|
||||||
|
try {
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
fs.createReadStream(filePath)
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (row) => rows.push(row))
|
||||||
|
.on('end', () => resolve())
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
await this.processCsvRow(param, row, successCount, failureCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SuccessResponseDto({
|
||||||
|
message: `Successfully processed CSV file`,
|
||||||
|
data: {
|
||||||
|
successCount: successCount.value,
|
||||||
|
failureCount: failureCount.value,
|
||||||
|
},
|
||||||
|
statusCode: HttpStatus.ACCEPTED,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Failed to process CSV file',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processCsvRow(
|
||||||
|
param: ProjectParam,
|
||||||
|
row: any,
|
||||||
|
successCount: { value: number },
|
||||||
|
failureCount: { value: number },
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const rawDeviceId = row['Device ID']?.trim();
|
||||||
|
const communityId = row['Community UUID']?.trim();
|
||||||
|
const spaceId = row['Space UUID']?.trim();
|
||||||
|
const subspaceId = row['Subspace UUID']?.trim();
|
||||||
|
const tagName = row['Tag']?.trim();
|
||||||
|
const productName = row['Product Name']?.trim();
|
||||||
|
const projectId = param.projectUuid;
|
||||||
|
|
||||||
|
if (!rawDeviceId) {
|
||||||
|
console.error('Missing Device ID in row:', row);
|
||||||
|
failureCount.value++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = await this.tuyaService.getDeviceDetails(rawDeviceId);
|
||||||
|
if (!device) {
|
||||||
|
console.error(`Device not found for Device ID: ${rawDeviceId}`);
|
||||||
|
failureCount.value++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const community = await this.communityRepository.findOne({
|
||||||
|
where: { uuid: communityId, project: { uuid: projectId } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!community) {
|
||||||
|
console.error(`Community not found: ${communityId}`);
|
||||||
|
failureCount.value++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tuyaSpaceId = community.externalId;
|
||||||
|
|
||||||
|
const space = await this.spaceRepository.findOne({
|
||||||
|
where: { uuid: spaceId },
|
||||||
|
relations: [
|
||||||
|
'productAllocations',
|
||||||
|
'productAllocations.tags',
|
||||||
|
'productAllocations.product',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!space) {
|
||||||
|
console.error(`Space not found: ${spaceId}`);
|
||||||
|
failureCount.value++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let subspace: SubspaceEntity | null = null;
|
||||||
|
if (subspaceId?.trim()) {
|
||||||
|
subspace = await this.subspaceRepository.findOne({
|
||||||
|
where: { uuid: subspaceId },
|
||||||
|
relations: [
|
||||||
|
'productAllocations',
|
||||||
|
'productAllocations.tags',
|
||||||
|
'productAllocations.product',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!subspace) {
|
||||||
|
console.error(`Subspace not found: ${subspaceId}`);
|
||||||
|
failureCount.value++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allocations =
|
||||||
|
subspace?.productAllocations || space.productAllocations;
|
||||||
|
|
||||||
|
const match = allocations
|
||||||
|
.flatMap((pa) =>
|
||||||
|
(pa.tags || []).map((tag) => ({ product: pa.product, tag })),
|
||||||
|
)
|
||||||
|
.find(({ tag }) => tag.name === tagName);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
console.error(`No matching tag found for Device ID: ${rawDeviceId}`);
|
||||||
|
failureCount.value++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match.product.name !== productName) {
|
||||||
|
console.error(`Product name mismatch for Device ID: ${rawDeviceId}`);
|
||||||
|
failureCount.value++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const middlewareDevice = this.deviceRepository.create({
|
||||||
|
deviceTuyaUuid: rawDeviceId,
|
||||||
|
isActive: true,
|
||||||
|
spaceDevice: space,
|
||||||
|
subspace: subspace || null,
|
||||||
|
productDevice: match.product,
|
||||||
|
tag: match.tag,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.deviceRepository.save(middlewareDevice);
|
||||||
|
|
||||||
|
await this.deviceService.transferDeviceInSpacesTuya(
|
||||||
|
rawDeviceId,
|
||||||
|
tuyaSpaceId,
|
||||||
|
);
|
||||||
|
successCount.value++;
|
||||||
|
} catch (err) {
|
||||||
|
failureCount.value++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,23 @@ import {
|
|||||||
Controller,
|
Controller,
|
||||||
Delete,
|
Delete,
|
||||||
Get,
|
Get,
|
||||||
|
Header,
|
||||||
|
HttpStatus,
|
||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
Res,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { Response } from 'express';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiOperation,
|
||||||
|
ApiProduces,
|
||||||
|
ApiResponse,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
import { ProjectService } from '../services';
|
import { ProjectService } from '../services';
|
||||||
import { CreateProjectDto, GetProjectParam } from '../dto';
|
import { CreateProjectDto, GetProjectParam } from '../dto';
|
||||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
@ -86,4 +96,34 @@ export class ProjectController {
|
|||||||
async findOne(@Param() params: GetProjectParam): Promise<BaseResponseDto> {
|
async findOne(@Param() params: GetProjectParam): Promise<BaseResponseDto> {
|
||||||
return this.projectService.getProject(params.projectUuid);
|
return this.projectService.getProject(params.projectUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ControllerRoute.PROJECT.ACTIONS.EXPORT_STRUCTURE_CSV_SUMMARY,
|
||||||
|
description:
|
||||||
|
ControllerRoute.PROJECT.ACTIONS.EXPORT_STRUCTURE_CSV_DESCRIPTION,
|
||||||
|
})
|
||||||
|
@Get(':projectUuid/structure/export-csv')
|
||||||
|
@ApiProduces('text/csv')
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description:
|
||||||
|
'A CSV file containing project details and their structural hierarchy (Project, Community, Space, Parent Space).',
|
||||||
|
})
|
||||||
|
@Header('Content-Type', 'text/csv')
|
||||||
|
@Header('Content-Disposition', 'attachment; filename=project-structure.csv')
|
||||||
|
async exportStructures(
|
||||||
|
@Param() params: GetProjectParam,
|
||||||
|
@Res() res: Response,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
const csvStream = await this.projectService.exportToCsv(params);
|
||||||
|
csvStream.pipe(res as unknown as NodeJS.WritableStream);
|
||||||
|
} catch (error) {
|
||||||
|
res
|
||||||
|
.status(error.status || HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
.json({ message: error.message || 'Failed to generate CSV file.' });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,13 @@ import { ProjectController } from './controllers';
|
|||||||
import { ProjectService } from './services';
|
import { ProjectService } from './services';
|
||||||
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
||||||
import { CreateOrphanSpaceHandler } from './handler';
|
import { CreateOrphanSpaceHandler } from './handler';
|
||||||
import { SpaceRepository } from '@app/common/modules/space';
|
import {
|
||||||
|
InviteSpaceRepository,
|
||||||
|
SpaceLinkRepository,
|
||||||
|
SpaceProductAllocationRepository,
|
||||||
|
SpaceRepository,
|
||||||
|
TagRepository,
|
||||||
|
} from '@app/common/modules/space';
|
||||||
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories';
|
import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories';
|
||||||
import { ProjectUserController } from './controllers/project-user.controller';
|
import { ProjectUserController } from './controllers/project-user.controller';
|
||||||
@ -13,6 +19,47 @@ import {
|
|||||||
UserRepository,
|
UserRepository,
|
||||||
UserSpaceRepository,
|
UserSpaceRepository,
|
||||||
} from '@app/common/modules/user/repositories';
|
} from '@app/common/modules/user/repositories';
|
||||||
|
import {
|
||||||
|
SpaceLinkService,
|
||||||
|
SpaceService,
|
||||||
|
SubspaceDeviceService,
|
||||||
|
SubSpaceService,
|
||||||
|
ValidationService,
|
||||||
|
} from 'src/space/services';
|
||||||
|
import { TagService } from 'src/tags/services';
|
||||||
|
import {
|
||||||
|
SpaceModelService,
|
||||||
|
SubSpaceModelService,
|
||||||
|
} from 'src/space-model/services';
|
||||||
|
import { DeviceService } from 'src/device/services';
|
||||||
|
import { SpaceProductAllocationService } from 'src/space/services/space-product-allocation.service';
|
||||||
|
import {
|
||||||
|
SubspaceProductAllocationRepository,
|
||||||
|
SubspaceRepository,
|
||||||
|
} from '@app/common/modules/space/repositories/subspace.repository';
|
||||||
|
import { SubspaceProductAllocationService } from 'src/space/services/subspace/subspace-product-allocation.service';
|
||||||
|
import { CommunityService } from 'src/community/services';
|
||||||
|
import {
|
||||||
|
SpaceModelProductAllocationRepoitory,
|
||||||
|
SpaceModelRepository,
|
||||||
|
SubspaceModelProductAllocationRepoitory,
|
||||||
|
SubspaceModelRepository,
|
||||||
|
} from '@app/common/modules/space-model';
|
||||||
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||||
|
import { NewTagRepository } from '@app/common/modules/tag/repositories/tag-repository';
|
||||||
|
import { ProductRepository } from '@app/common/modules/product/repositories';
|
||||||
|
import { SpaceModelProductAllocationService } from 'src/space-model/services/space-model-product-allocation.service';
|
||||||
|
import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories';
|
||||||
|
import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service';
|
||||||
|
import { SceneService } from 'src/scene/services';
|
||||||
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
|
import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service';
|
||||||
|
import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories';
|
||||||
|
import {
|
||||||
|
SceneIconRepository,
|
||||||
|
SceneRepository,
|
||||||
|
} from '@app/common/modules/scene/repositories';
|
||||||
|
import { AutomationRepository } from '@app/common/modules/automation/repositories';
|
||||||
|
|
||||||
const CommandHandlers = [CreateOrphanSpaceHandler];
|
const CommandHandlers = [CreateOrphanSpaceHandler];
|
||||||
|
|
||||||
@ -30,6 +77,41 @@ const CommandHandlers = [CreateOrphanSpaceHandler];
|
|||||||
InviteUserRepository,
|
InviteUserRepository,
|
||||||
UserSpaceRepository,
|
UserSpaceRepository,
|
||||||
UserRepository,
|
UserRepository,
|
||||||
|
SpaceService,
|
||||||
|
InviteSpaceRepository,
|
||||||
|
SpaceLinkService,
|
||||||
|
SubSpaceService,
|
||||||
|
ValidationService,
|
||||||
|
TagService,
|
||||||
|
SpaceModelService,
|
||||||
|
DeviceService,
|
||||||
|
SpaceProductAllocationService,
|
||||||
|
SpaceLinkRepository,
|
||||||
|
SubspaceRepository,
|
||||||
|
SubspaceDeviceService,
|
||||||
|
SubspaceProductAllocationService,
|
||||||
|
CommunityService,
|
||||||
|
SpaceModelRepository,
|
||||||
|
DeviceRepository,
|
||||||
|
NewTagRepository,
|
||||||
|
ProductRepository,
|
||||||
|
SubSpaceModelService,
|
||||||
|
SpaceModelProductAllocationService,
|
||||||
|
SpaceProductAllocationRepository,
|
||||||
|
SubspaceProductAllocationRepository,
|
||||||
|
SceneDeviceRepository,
|
||||||
|
DeviceStatusFirebaseService,
|
||||||
|
SceneService,
|
||||||
|
TuyaService,
|
||||||
|
TagRepository,
|
||||||
|
SubspaceModelRepository,
|
||||||
|
SubspaceModelProductAllocationService,
|
||||||
|
SpaceModelProductAllocationRepoitory,
|
||||||
|
DeviceStatusLogRepository,
|
||||||
|
SceneIconRepository,
|
||||||
|
SceneRepository,
|
||||||
|
AutomationRepository,
|
||||||
|
SubspaceModelProductAllocationRepoitory,
|
||||||
],
|
],
|
||||||
exports: [ProjectService, CqrsModule],
|
exports: [ProjectService, CqrsModule],
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import {
|
||||||
import { CreateProjectDto } from '../dto';
|
forwardRef,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { CreateProjectDto, GetProjectParam } from '../dto';
|
||||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
import { ProjectEntity } from '@app/common/modules/project/entities';
|
import { ProjectEntity } from '@app/common/modules/project/entities';
|
||||||
@ -13,6 +19,11 @@ import { PageResponse } from '@app/common/dto/pagination.response.dto';
|
|||||||
import { CommandBus } from '@nestjs/cqrs';
|
import { CommandBus } from '@nestjs/cqrs';
|
||||||
import { CreateOrphanSpaceCommand } from '../command/create-orphan-space-command';
|
import { CreateOrphanSpaceCommand } from '../command/create-orphan-space-command';
|
||||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||||
|
import { format } from '@fast-csv/format';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
|
import { SpaceEntity } from '@app/common/modules/space/entities/space.entity';
|
||||||
|
import { SpaceService } from 'src/space/services';
|
||||||
|
import { ORPHAN_COMMUNITY_NAME } from '@app/common/constants/orphan-constant';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProjectService {
|
export class ProjectService {
|
||||||
@ -20,6 +31,8 @@ export class ProjectService {
|
|||||||
private readonly projectRepository: ProjectRepository,
|
private readonly projectRepository: ProjectRepository,
|
||||||
private readonly userRepository: UserRepository,
|
private readonly userRepository: UserRepository,
|
||||||
private commandBus: CommandBus,
|
private commandBus: CommandBus,
|
||||||
|
@Inject(forwardRef(() => SpaceService))
|
||||||
|
private readonly spaceService: SpaceService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async createProject(
|
async createProject(
|
||||||
@ -213,4 +226,129 @@ export class ProjectService {
|
|||||||
async validate(name: string): Promise<boolean> {
|
async validate(name: string): Promise<boolean> {
|
||||||
return await this.projectRepository.exists({ where: { name } });
|
return await this.projectRepository.exists({ where: { name } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async exportToCsv(param: GetProjectParam): Promise<PassThrough> {
|
||||||
|
const project = await this.projectRepository.findOne({
|
||||||
|
where: { uuid: param.projectUuid },
|
||||||
|
relations: [
|
||||||
|
'communities',
|
||||||
|
'communities.spaces',
|
||||||
|
'communities.spaces.parent',
|
||||||
|
'communities.spaces.productAllocations',
|
||||||
|
'communities.spaces.productAllocations.product',
|
||||||
|
'communities.spaces.productAllocations.tags',
|
||||||
|
'communities.spaces.subspaces',
|
||||||
|
'communities.spaces.subspaces.productAllocations',
|
||||||
|
'communities.spaces.subspaces.productAllocations.product',
|
||||||
|
'communities.spaces.subspaces.productAllocations.tags',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Project with UUID ${param.projectUuid} not found`,
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream = new PassThrough();
|
||||||
|
const csvStream = format({
|
||||||
|
headers: [
|
||||||
|
'Device ID',
|
||||||
|
'Community Name',
|
||||||
|
'Space Name',
|
||||||
|
'Space Location',
|
||||||
|
'Subspace Name',
|
||||||
|
'Tag',
|
||||||
|
'Product Name',
|
||||||
|
'Community UUID',
|
||||||
|
'Space UUID',
|
||||||
|
'Subspace UUID',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
csvStream.pipe(stream);
|
||||||
|
|
||||||
|
const allSpaces: SpaceEntity[] = [];
|
||||||
|
for (const community of project.communities) {
|
||||||
|
if (community.name === `${ORPHAN_COMMUNITY_NAME}-${project.name}`)
|
||||||
|
continue;
|
||||||
|
for (const space of community.spaces) {
|
||||||
|
if (!space.disabled) {
|
||||||
|
space.community = community;
|
||||||
|
allSpaces.push(space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const spaceMap = new Map<string, SpaceEntity>();
|
||||||
|
for (const space of allSpaces) {
|
||||||
|
spaceMap.set(space.uuid, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSpaceLocation(space: SpaceEntity): string {
|
||||||
|
const path: string[] = [];
|
||||||
|
let current = space.parent;
|
||||||
|
while (current) {
|
||||||
|
path.unshift(current.spaceName);
|
||||||
|
current = current.parent ?? spaceMap.get(current.uuid)?.parent ?? null;
|
||||||
|
}
|
||||||
|
return path.join(' > ');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const space of allSpaces) {
|
||||||
|
const spaceLocation = buildSpaceLocation(space);
|
||||||
|
|
||||||
|
for (const subspace of space.subspaces || []) {
|
||||||
|
if (subspace.disabled) continue;
|
||||||
|
|
||||||
|
for (const productAllocation of subspace.productAllocations || []) {
|
||||||
|
for (const tag of productAllocation.tags || []) {
|
||||||
|
csvStream.write({
|
||||||
|
'Device ID': '',
|
||||||
|
'Community Name': space.community?.name || '',
|
||||||
|
'Space Name': space.spaceName,
|
||||||
|
'Space Location': spaceLocation,
|
||||||
|
'Subspace Name': subspace.subspaceName || '',
|
||||||
|
Tag: tag.name,
|
||||||
|
'Product Name': productAllocation.product.name || '',
|
||||||
|
'Community UUID': space.community?.uuid || '',
|
||||||
|
'Space UUID': space.uuid,
|
||||||
|
'Subspace UUID': subspace.uuid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const productAllocation of space.productAllocations || []) {
|
||||||
|
for (const tag of productAllocation.tags || []) {
|
||||||
|
csvStream.write({
|
||||||
|
'Device ID': '',
|
||||||
|
'Community Name': space.community?.name || '',
|
||||||
|
'Space Name': space.spaceName,
|
||||||
|
'Space Location': spaceLocation,
|
||||||
|
'Subspace Name': '',
|
||||||
|
Tag: tag.name,
|
||||||
|
'Product Name': productAllocation.product.name || '',
|
||||||
|
'Community UUID': space.community?.uuid || '',
|
||||||
|
'Space UUID': space.uuid,
|
||||||
|
'Subspace UUID': '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
csvStream.end();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpaceLocation(space: SpaceEntity): string {
|
||||||
|
const names = [];
|
||||||
|
let current = space.parent;
|
||||||
|
while (current) {
|
||||||
|
names.unshift(current.spaceName);
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
return names.join(' > ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ export class DisableSpaceHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagUuids = space.tags?.map((tag) => tag.uuid) || [];
|
const tagUuids = space.productAllocations?.map((tag) => tag.uuid) || [];
|
||||||
/* const subspaceDtos =
|
/* const subspaceDtos =
|
||||||
space.subspaces?.map((subspace) => ({
|
space.subspaces?.map((subspace) => ({
|
||||||
subspaceUuid: subspace.uuid,
|
subspaceUuid: subspace.uuid,
|
||||||
|
@ -582,8 +582,8 @@ export class SpaceService {
|
|||||||
queryRunner: QueryRunner,
|
queryRunner: QueryRunner,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (space.subspaces || space.tags) {
|
if (space.subspaces || space.productAllocations) {
|
||||||
if (space.tags) {
|
if (space.productAllocations) {
|
||||||
await this.spaceProductAllocationService.unlinkModels(
|
await this.spaceProductAllocationService.unlinkModels(
|
||||||
space,
|
space,
|
||||||
queryRunner,
|
queryRunner,
|
||||||
|
@ -18,7 +18,6 @@ import { In, QueryRunner } from 'typeorm';
|
|||||||
import { SubspaceModelEntity } from '@app/common/modules/space-model';
|
import { SubspaceModelEntity } from '@app/common/modules/space-model';
|
||||||
import { ValidationService } from '../space-validation.service';
|
import { ValidationService } from '../space-validation.service';
|
||||||
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
||||||
import { TagService } from '../tag';
|
|
||||||
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
||||||
import { SubspaceDeviceService } from './subspace-device.service';
|
import { SubspaceDeviceService } from './subspace-device.service';
|
||||||
import { ModifyTagDto } from 'src/space/dtos/tag/modify-tag.dto';
|
import { ModifyTagDto } from 'src/space/dtos/tag/modify-tag.dto';
|
||||||
@ -34,7 +33,6 @@ export class SubSpaceService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly subspaceRepository: SubspaceRepository,
|
private readonly subspaceRepository: SubspaceRepository,
|
||||||
private readonly validationService: ValidationService,
|
private readonly validationService: ValidationService,
|
||||||
private readonly tagService: TagService,
|
|
||||||
private readonly newTagService: NewTagService,
|
private readonly newTagService: NewTagService,
|
||||||
public readonly deviceService: SubspaceDeviceService,
|
public readonly deviceService: SubspaceDeviceService,
|
||||||
private readonly subspaceProductAllocationService: SubspaceProductAllocationService,
|
private readonly subspaceProductAllocationService: SubspaceProductAllocationService,
|
||||||
@ -481,21 +479,6 @@ export class SubSpaceService {
|
|||||||
{ disabled: true },
|
{ disabled: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (subspace.tags?.length) {
|
|
||||||
const modifyTagDtos: ProcessTagDto[] = subspace.tags.map((tag) => ({
|
|
||||||
uuid: tag.uuid,
|
|
||||||
action: ModifyAction.ADD,
|
|
||||||
name: tag.tag,
|
|
||||||
productUuid: tag.product.uuid,
|
|
||||||
}));
|
|
||||||
await this.tagService.moveTags(
|
|
||||||
modifyTagDtos,
|
|
||||||
queryRunner,
|
|
||||||
subspace.space,
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subspace.devices.length > 0) {
|
if (subspace.devices.length > 0) {
|
||||||
await this.deviceService.deleteSubspaceDevices(
|
await this.deviceService.deleteSubspaceDevices(
|
||||||
subspace.devices,
|
subspace.devices,
|
||||||
|
@ -118,7 +118,7 @@ export class TagService {
|
|||||||
await queryRunner.manager.update(
|
await queryRunner.manager.update(
|
||||||
this.tagRepository.target,
|
this.tagRepository.target,
|
||||||
{ uuid: tag.uuid },
|
{ uuid: tag.uuid },
|
||||||
{ subspace, space: null },
|
{ subspace },
|
||||||
);
|
);
|
||||||
tag.subspace = subspace;
|
tag.subspace = subspace;
|
||||||
}
|
}
|
||||||
@ -127,10 +127,9 @@ export class TagService {
|
|||||||
await queryRunner.manager.update(
|
await queryRunner.manager.update(
|
||||||
this.tagRepository.target,
|
this.tagRepository.target,
|
||||||
{ uuid: tag.uuid },
|
{ uuid: tag.uuid },
|
||||||
{ subspace: null, space: space },
|
{ subspace: null },
|
||||||
);
|
);
|
||||||
tag.subspace = null;
|
tag.subspace = null;
|
||||||
tag.space = space;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tag;
|
return tag;
|
||||||
@ -367,7 +366,6 @@ export class TagService {
|
|||||||
where: [
|
where: [
|
||||||
{
|
{
|
||||||
tag,
|
tag,
|
||||||
space: { uuid: spaceUuid },
|
|
||||||
product: { uuid: productUuid },
|
product: { uuid: productUuid },
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user