mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-08-26 00:49:40 +00:00
227 lines
7.0 KiB
TypeScript
227 lines
7.0 KiB
TypeScript
import * as csv from 'csv-parser';
|
|
import * as fs from 'fs';
|
|
|
|
import { ProjectParam } from '@app/common/dto/project-param.dto';
|
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
|
import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service';
|
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
|
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
|
import { SpaceRepository } from '@app/common/modules/space';
|
|
import { SpaceProductAllocationEntity } from '@app/common/modules/space/entities/space-product-allocation.entity';
|
|
import { SubspaceProductAllocationEntity } from '@app/common/modules/space/entities/subspace/subspace-product-allocation.entity';
|
|
import { SubspaceEntity } from '@app/common/modules/space/entities/subspace/subspace.entity';
|
|
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
|
import { DeviceService } from 'src/device/services';
|
|
|
|
@Injectable()
|
|
export class DeviceCommissionService {
|
|
constructor(
|
|
private readonly tuyaService: TuyaService,
|
|
private readonly deviceService: DeviceService,
|
|
private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService,
|
|
private readonly communityRepository: CommunityRepository,
|
|
private readonly spaceRepository: SpaceRepository,
|
|
private readonly subspaceRepository: SubspaceRepository,
|
|
private readonly deviceRepository: DeviceRepository,
|
|
private readonly projectRepository: ProjectRepository,
|
|
) {}
|
|
|
|
async processCsv(param: ProjectParam, filePath: string) {
|
|
const successCount = { value: 0 };
|
|
const failureCount = { value: 0 };
|
|
|
|
const projectId = param.projectUuid;
|
|
|
|
const project = await this.projectRepository.findOne({
|
|
where: { uuid: projectId },
|
|
});
|
|
|
|
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;
|
|
let deviceName: string;
|
|
|
|
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;
|
|
}
|
|
|
|
if (device && typeof device === 'object' && 'name' in device) {
|
|
deviceName = (device as any).name || '';
|
|
}
|
|
|
|
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.tag',
|
|
'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.tag',
|
|
'productAllocations.product',
|
|
],
|
|
});
|
|
|
|
if (!subspace) {
|
|
console.error(`Subspace not found: ${subspaceId}`);
|
|
failureCount.value++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
const allocations =
|
|
subspace?.productAllocations || space.productAllocations;
|
|
|
|
const match = allocations
|
|
.map(
|
|
({
|
|
product,
|
|
tag,
|
|
}:
|
|
| SpaceProductAllocationEntity
|
|
| SubspaceProductAllocationEntity) => ({ product, tag }),
|
|
)
|
|
.find(
|
|
({ tag, product }) =>
|
|
tag.name === tagName && product.name === productName,
|
|
);
|
|
|
|
if (!match) {
|
|
console.error(
|
|
`No matching tag-product combination found for Device ID: ${rawDeviceId}`,
|
|
);
|
|
failureCount.value++;
|
|
return;
|
|
}
|
|
|
|
const devices = await this.deviceRepository.find({
|
|
where: {
|
|
spaceDevice: space,
|
|
},
|
|
relations: ['productDevice', 'tag'],
|
|
});
|
|
|
|
if (devices.length > 0) {
|
|
devices.forEach((device) => {
|
|
if (device.tag.uuid === match.tag.uuid) {
|
|
console.error(
|
|
`Device with same tag already exists: ${device.tag.name}`,
|
|
);
|
|
failureCount.value++;
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
|
|
const middlewareDevice = this.deviceRepository.create({
|
|
deviceTuyaUuid: rawDeviceId,
|
|
isActive: true,
|
|
spaceDevice: space,
|
|
subspace: subspace || null,
|
|
productDevice: match.product,
|
|
tag: match.tag,
|
|
name: deviceName ?? '',
|
|
});
|
|
|
|
await this.deviceRepository.save(middlewareDevice);
|
|
|
|
await this.deviceService.transferDeviceInSpacesTuya(
|
|
rawDeviceId,
|
|
tuyaSpaceId,
|
|
);
|
|
|
|
await this.deviceStatusFirebaseService.addDeviceStatusByDeviceUuid(
|
|
rawDeviceId,
|
|
);
|
|
successCount.value++;
|
|
console.log(
|
|
`Device ${rawDeviceId} successfully processed and transferred to Tuya space ${tuyaSpaceId}`,
|
|
);
|
|
} catch (err) {
|
|
failureCount.value++;
|
|
}
|
|
}
|
|
}
|