diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 4b0b0f7..1d201d3 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -24,6 +24,7 @@ import { PowerClampEnergyEnum } from '@app/common/constants/power.clamp.enargy.e import { PresenceSensorEnum } from '@app/common/constants/presence.sensor.enum'; import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { DataSource, QueryRunner } from 'typeorm'; @Injectable() export class DeviceStatusFirebaseService { private tuya: TuyaContext; @@ -35,6 +36,7 @@ export class DeviceStatusFirebaseService { private readonly occupancyService: OccupancyService, private readonly aqiDataService: AqiDataService, private deviceStatusLogRepository: DeviceStatusLogRepository, + private readonly dataSource: DataSource, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -79,28 +81,46 @@ export class DeviceStatusFirebaseService { async addDeviceStatusToFirebase( addDeviceStatusDto: AddDeviceStatusDto, ): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); try { const device = await this.getDeviceByDeviceTuyaUuid( addDeviceStatusDto.deviceTuyaUuid, + queryRunner, ); if (device?.uuid) { - return await this.createDeviceStatusFirebase({ - deviceUuid: device.uuid, - ...addDeviceStatusDto, - productType: device.productDevice.prodType, - }); + const result = await this.createDeviceStatusFirebase( + { + deviceUuid: device.uuid, + ...addDeviceStatusDto, + productType: device.productDevice.prodType, + }, + queryRunner, + ); + await queryRunner.commitTransaction(); + return result; } // Return null if device not found or no UUID + await queryRunner.rollbackTransaction(); return null; } catch (error) { - // Handle the error silently, perhaps log it internally or ignore it + await queryRunner.rollbackTransaction(); return null; + } finally { + await queryRunner.release(); } } + async getDeviceByDeviceTuyaUuid( + deviceTuyaUuid: string, + queryRunner?: QueryRunner, + ) { + const repo = queryRunner + ? queryRunner.manager.getRepository(this.deviceRepository.target) + : this.deviceRepository; - async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) { - return await this.deviceRepository.findOne({ + return await repo.findOne({ where: { deviceTuyaUuid, isActive: true, @@ -108,6 +128,7 @@ export class DeviceStatusFirebaseService { relations: ['productDevice'], }); } + async getDevicesInstructionStatus(deviceUuid: string) { try { const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); @@ -153,9 +174,14 @@ export class DeviceStatusFirebaseService { } async getDeviceByDeviceUuid( deviceUuid: string, - withProductDevice: boolean = true, + withProductDevice = true, + queryRunner?: QueryRunner, ) { - return await this.deviceRepository.findOne({ + const repo = queryRunner + ? queryRunner.manager.getRepository(this.deviceRepository.target) + : this.deviceRepository; + + return await repo.findOne({ where: { uuid: deviceUuid, isActive: true, @@ -163,21 +189,20 @@ export class DeviceStatusFirebaseService { ...(withProductDevice && { relations: ['productDevice'] }), }); } + async createDeviceStatusFirebase( addDeviceStatusDto: AddDeviceStatusDto, + queryRunner?: QueryRunner, ): Promise { const dataRef = ref( this.firebaseDb, `device-status/${addDeviceStatusDto.deviceUuid}`, ); - // Use a transaction to handle concurrent updates + // Step 1: Update Firebase Realtime Database await runTransaction(dataRef, (existingData) => { - if (!existingData) { - existingData = {}; - } + if (!existingData) existingData = {}; - // Assign default values if fields are not present if (!existingData.deviceTuyaUuid) { existingData.deviceTuyaUuid = addDeviceStatusDto.deviceTuyaUuid; } @@ -191,18 +216,15 @@ export class DeviceStatusFirebaseService { existingData.status = []; } - // Create a map to track existing status codes + // Merge incoming status with existing status const statusMap = new Map( existingData.status.map((item) => [item.code, item.value]), ); - // Update or add status codes - for (const statusItem of addDeviceStatusDto.status) { statusMap.set(statusItem.code, statusItem.value); } - // Convert the map back to an array format existingData.status = Array.from(statusMap, ([code, value]) => ({ code, value, @@ -211,9 +233,9 @@ export class DeviceStatusFirebaseService { return existingData; }); - // Save logs to your repository - const newLogs = addDeviceStatusDto.log.properties.map((property) => { - return this.deviceStatusLogRepository.create({ + // Step 2: Save device status log entries + const newLogs = addDeviceStatusDto.log.properties.map((property) => + this.deviceStatusLogRepository.create({ deviceId: addDeviceStatusDto.deviceUuid, deviceTuyaId: addDeviceStatusDto.deviceTuyaUuid, productId: addDeviceStatusDto.log.productId, @@ -222,10 +244,19 @@ export class DeviceStatusFirebaseService { value: property.value, eventId: addDeviceStatusDto.log.dataId, eventTime: new Date(property.time).toISOString(), - }); - }); - await this.deviceStatusLogRepository.save(newLogs); + }), + ); + if (queryRunner) { + const repo = queryRunner.manager.getRepository( + this.deviceStatusLogRepository.target, + ); + await repo.save(newLogs); + } else { + await this.deviceStatusLogRepository.save(newLogs); + } + + // Step 3: Trigger additional data services if (addDeviceStatusDto.productType === ProductType.PC) { const energyCodes = new Set([ PowerClampEnergyEnum.ENERGY_CONSUMED, @@ -269,7 +300,8 @@ export class DeviceStatusFirebaseService { addDeviceStatusDto.deviceUuid, ); } - // Return the updated data + + // Step 4: Return updated Firebase status const snapshot: DataSnapshot = await get(dataRef); return snapshot.val(); } diff --git a/libs/common/src/helper/services/aqi.data.service.ts b/libs/common/src/helper/services/aqi.data.service.ts index 3e19b6c..c8f5c1a 100644 --- a/libs/common/src/helper/services/aqi.data.service.ts +++ b/libs/common/src/helper/services/aqi.data.service.ts @@ -36,9 +36,18 @@ export class AqiDataService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + try { + const query = this.loadQuery(procedureFolderName, procedureFileName); + await queryRunner.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + console.error(`Failed to execute procedure ${procedureFileName}:`, err); + throw err; + } finally { + await queryRunner.release(); + } } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/occupancy.service.ts b/libs/common/src/helper/services/occupancy.service.ts index ea99b7c..b3d50cf 100644 --- a/libs/common/src/helper/services/occupancy.service.ts +++ b/libs/common/src/helper/services/occupancy.service.ts @@ -57,9 +57,18 @@ export class OccupancyService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + try { + const query = this.loadQuery(procedureFolderName, procedureFileName); + await queryRunner.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + console.error(`Failed to execute procedure ${procedureFileName}:`, err); + throw err; + } finally { + await queryRunner.release(); + } } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/power.clamp.service.ts b/libs/common/src/helper/services/power.clamp.service.ts index 7c83208..6cb667b 100644 --- a/libs/common/src/helper/services/power.clamp.service.ts +++ b/libs/common/src/helper/services/power.clamp.service.ts @@ -46,12 +46,21 @@ export class PowerClampService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const query = this.loadQuery( - 'fact_device_energy_consumed', - procedureFileName, - ); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + try { + const query = this.loadQuery( + 'fact_device_energy_consumed', + procedureFileName, + ); + await queryRunner.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + console.error(`Failed to execute procedure ${procedureFileName}:`, err); + throw err; + } finally { + await queryRunner.release(); + } } private loadQuery(folderName: string, fileName: string): string {