Merge branch 'dev' of https://github.com/SyncrowIOT/backend into feat/commission-device

This commit is contained in:
hannathkadher
2025-04-15 20:01:45 +04:00
9 changed files with 228 additions and 4 deletions

View File

@ -345,6 +345,9 @@ export class ControllerRoute {
static PRODUCT = class {
public static readonly ROUTE = 'products';
static ACTIONS = class {
public static readonly CREATE_PRODUCT_SUMMARY = 'Create a new product';
public static readonly CREATE_PRODUCT_DESCRIPTION =
'This endpoint allows you to create a new product in the system.';
public static readonly LIST_PRODUCT_SUMMARY = 'Retrieve all products';
public static readonly LIST_PRODUCT_DESCRIPTION =
'Fetches a list of all products along with their associated device details';

View File

@ -55,6 +55,7 @@ export const RolePermissions = {
'USER_ADD',
'SPACE_MEMBER_ADD',
'COMMISSION_DEVICE',
'PRODUCT_ADD',
],
[RoleType.ADMIN]: [
'DEVICE_SINGLE_CONTROL',
@ -110,6 +111,7 @@ export const RolePermissions = {
'USER_ADD',
'SPACE_MEMBER_ADD',
'COMMISSION_DEVICE',
'PRODUCT_ADD',
],
[RoleType.SPACE_MEMBER]: [
'DEVICE_SINGLE_CONTROL',

View File

@ -61,5 +61,8 @@ JOIN energy_phase_B
and total_energy.date=energy_phase_B.date
JOIN energy_phase_C
ON total_energy.device_id=energy_phase_C.device_id
and total_energy.date=energy_phase_C.date
and total_energy.date=energy_phase_C.date;

View File

@ -0,0 +1,65 @@
-- model shows the energy consumed per day per device
WITH total_energy AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='ActivePower'
GROUP BY device_id, date
)
, energy_phase_A AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='ActivePowerA'
GROUP BY device_id, date
)
, energy_phase_B AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='ActivePowerB'
GROUP BY device_id, date
)
, energy_phase_C AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='ActivePowerC'
GROUP BY device_id, date
)
SELECT
total_energy.device_id,
total_energy.date,
(total_energy.max_value-total_energy.min_value) as energy_consumed_kW,
(energy_phase_A.max_value-energy_phase_A.min_value) as energy_consumed_A,
(energy_phase_B.max_value-energy_phase_B.min_value) as energy_consumed_B,
(energy_phase_C.max_value-energy_phase_C.min_value) as energy_consumed_C
FROM total_energy
JOIN energy_phase_A
ON total_energy.device_id=energy_phase_A.device_id
and total_energy.date=energy_phase_A.date
JOIN energy_phase_B
ON total_energy.device_id=energy_phase_B.device_id
and total_energy.date=energy_phase_B.date
JOIN energy_phase_C
ON total_energy.device_id=energy_phase_C.device_id
and total_energy.date=energy_phase_C.date;

View File

@ -0,0 +1,71 @@
WITH total_energy AS (
SELECT
device_id,
event_time::date AS date,
EXTRACT(HOUR FROM event_time) AS hour,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
WHERE code = 'EnergyConsumed'
GROUP BY device_id, date, hour
),
energy_phase_A AS (
SELECT
device_id,
event_time::date AS date,
EXTRACT(HOUR FROM event_time) AS hour,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
WHERE code = 'EnergyConsumedA'
GROUP BY device_id, date, hour
),
energy_phase_B AS (
SELECT
device_id,
event_time::date AS date,
EXTRACT(HOUR FROM event_time) AS hour,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
WHERE code = 'EnergyConsumedB'
GROUP BY device_id, date, hour
),
energy_phase_C AS (
SELECT
device_id,
event_time::date AS date,
EXTRACT(HOUR FROM event_time) AS hour,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
WHERE code = 'EnergyConsumedC'
GROUP BY device_id, date, hour
)
SELECT
total_energy.device_id,
total_energy.date,
total_energy.hour,
(total_energy.max_value - total_energy.min_value) AS energy_consumed_kW,
(energy_phase_A.max_value - energy_phase_A.min_value) AS energy_consumed_A,
(energy_phase_B.max_value - energy_phase_B.min_value) AS energy_consumed_B,
(energy_phase_C.max_value - energy_phase_C.min_value) AS energy_consumed_C
FROM total_energy
JOIN energy_phase_A
ON total_energy.device_id = energy_phase_A.device_id
AND total_energy.date = energy_phase_A.date
AND total_energy.hour = energy_phase_A.hour
JOIN energy_phase_B
ON total_energy.device_id = energy_phase_B.device_id
AND total_energy.date = energy_phase_B.date
AND total_energy.hour = energy_phase_B.hour
JOIN energy_phase_C
ON total_energy.device_id = energy_phase_C.device_id
AND total_energy.date = energy_phase_C.date
AND total_energy.hour = energy_phase_C.hour
ORDER BY total_energy.device_id, total_energy.date, total_energy.hour;

View File

@ -1,10 +1,13 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { ControllerRoute } from '@app/common/constants/controller-route';
import { ProductService } from '../services';
import { Permissions } from 'src/decorators/permissions.decorator';
import { PermissionsGuard } from 'src/guards/permissions.guard';
import { AddProductDto } from '../dtos';
@ApiTags('Product Module')
@Controller({
@ -13,7 +16,19 @@ import { ProductService } from '../services';
})
export class ProductController {
constructor(private readonly productService: ProductService) {}
@ApiBearerAuth()
@UseGuards(PermissionsGuard)
@Permissions('PRODUCT_ADD')
@Post()
@ApiOperation({
summary: ControllerRoute.PRODUCT.ACTIONS.CREATE_PRODUCT_SUMMARY,
description: ControllerRoute.PRODUCT.ACTIONS.CREATE_PRODUCT_DESCRIPTION,
})
async addNewProductType(
@Body() addProductDto: AddProductDto,
): Promise<BaseResponseDto> {
return await this.productService.addNewProductType(addProductDto);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Get()

View File

@ -0,0 +1,36 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class AddProductDto {
@ApiProperty({
description: 'tuyaCategoryName',
required: true,
})
@IsString()
@IsNotEmpty()
public tuyaCategoryName: string;
@ApiProperty({
description: 'tuyaProductId',
required: true,
})
@IsString()
@IsNotEmpty()
public tuyaProductId: string;
@ApiProperty({
description: 'syncrowProductType',
required: true,
})
@IsString()
@IsNotEmpty()
public syncrowProductType: string;
@ApiProperty({
description: 'syncrowProductName',
required: true,
})
@IsString()
@IsNotEmpty()
public syncrowProductName: string;
}

View File

@ -0,0 +1 @@
export * from './add.product.dto';

View File

@ -2,11 +2,39 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
import { ProductRepository } from '@app/common/modules/product/repositories';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { AddProductDto } from '../dtos';
@Injectable()
export class ProductService {
constructor(private readonly productRepository: ProductRepository) {}
async addNewProductType(
addProductDto: AddProductDto,
): Promise<BaseResponseDto> {
try {
const {
tuyaCategoryName,
tuyaProductId,
syncrowProductType,
syncrowProductName,
} = addProductDto;
const product = await this.productRepository.save({
catName: tuyaCategoryName,
prodId: tuyaProductId,
prodType: syncrowProductType,
name: syncrowProductName,
});
return new SuccessResponseDto({
data: product,
message: 'Successfully added new product type',
});
} catch (error) {
throw new HttpException(
`Error adding new product type: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async list(): Promise<BaseResponseDto> {
const products = await this.productRepository.find();
@ -37,7 +65,7 @@ export class ProductService {
return new SuccessResponseDto({
data: product,
message: 'Succefully retrieved product',
message: 'Successfully retrieved product',
});
}
}