From b43af08d04c2a3994b0f1edb1557aa765cadf217 Mon Sep 17 00:00:00 2001 From: JurTI-BR Date: Fri, 14 Mar 2025 09:26:28 -0300 Subject: [PATCH] Cache departamento --- .../address-customer.service.ts | 21 +- src/sales/customer/customer.service.ts | 23 -- src/sales/order/order.module.ts | 3 +- src/sales/order/order.service.ts | 205 ++++++++++++------ src/sales/sales/sales.service.ts | 107 ++++++--- 5 files changed, 223 insertions(+), 136 deletions(-) diff --git a/src/sales/address-customer/address-customer.service.ts b/src/sales/address-customer/address-customer.service.ts index 745e49f..2a42580 100644 --- a/src/sales/address-customer/address-customer.service.ts +++ b/src/sales/address-customer/address-customer.service.ts @@ -83,26 +83,7 @@ export class AddressCustomerService { const address = await queryRunner.query( sql, [idCustomer, idAddress] ) ; - // .getRepository(Pcclientendent) - // .createQueryBuilder('pcclientendent') - // .select('\"pcclientendent\".codcli', 'customerId') - // .addSelect('\"pcclientendent\".codendentcli', 'idAddress') - // .addSelect('\"pcclientendent\".bairroent', 'neighbourhood') - // .addSelect('\"pcclientendent\".municent', 'city') - // .addSelect('\"pcclientendent\".estent', 'state') - // .addSelect('\"pcclientendent\".cepent', 'zipCode') - // .addSelect('\"pcclientendent\".enderent', 'street') - // .addSelect('\"pcclientendent\".complementoent', 'complement') - // .addSelect('\"pcclientendent\".numeroent', 'numberAddress') - // .addSelect('\"pcclientendent\".codcidade', 'cityCode') - // .addSelect('\"pcclientendent\".pontorefer', 'referencePoint') - // .addSelect('\"pcclientendent\".observacao', 'note') - // .addSelect('\"pcclientendent\".telent', 'phone') - // .addSelect('\"pcclientendent\".telent', 'cellPhone') - // .addSelect('\"pcclientendent\".codpracaent', 'placeId') - // .where("\"pcclientendent\".codcli = :idCustomer", { idCustomer }) - // .andWhere("\"pcclientendent\".codendentcli = :idAddress", { idAddress }) - // .getOne(); + return address[0]; } catch (error) { console.log(error); diff --git a/src/sales/customer/customer.service.ts b/src/sales/customer/customer.service.ts index f855837..1515fa4 100644 --- a/src/sales/customer/customer.service.ts +++ b/src/sales/customer/customer.service.ts @@ -152,29 +152,6 @@ export class CustomerService { ' WHERE pcclient.codcidade = pccidade.codcidade (+)'; const where = ` AND pcclient.codcli = ${idCustomer}`; const customer = await queryRunner.query(sql + where); - // const customer = await queryRunner.manager - // .getRepository(Pcclient) - // .createQueryBuilder('pcclient') - // .select("\"pcclient\".codcli as \"customerId\"") - // .addSelect("\"pcclient\".cliente as \"name\"") - // .addSelect("\"pcclient\".emailnfe as \"email\"") - // .addSelect("\"pcclient\".cgcent as \"cpfCnpj\"") - // .addSelect("\"pcclient\".ieent as \"numberState\"") - // .addSelect("\"pcclient\".enderent as \"address\"") - // .addSelect("\"pcclient\".numeroent as \"addressNumber\"") - // .addSelect("\"pcclient\".bairroent as \"neighborhood\"") - // .addSelect("\"pcclient\".complementoent as \"complement\"") - // .addSelect("\"pcclient\".municent as \"city\"") - // .addSelect("\"pcclient\".estent as \"state\"") - // .addSelect("\"pcclient\".cepent as \"zipCode\"") - // .addSelect("\"pcclient\".telent as \"phone\"") - // .addSelect("\"pcclient\".telcelent as \"cellPhone\"") - // .addSelect("\"pcclient\".codcategoria as \"categoryId\"") - // .addSelect("\"pcclient\".codsubcategoria as \"subCategoryId\"") - // .addSelect("\"pcclient\".codpraca as \"placeId\"") - // .where("\"pcclient\".codcli = :codcli", { codcli: idCustomer }) - // .andWhere("\"pcclient\".CODCLI NOT IN (2)") - // .getOne(); return customer[0]; } catch (error) { console.log(error); diff --git a/src/sales/order/order.module.ts b/src/sales/order/order.module.ts index 10fc8e9..389d4fc 100644 --- a/src/sales/order/order.module.ts +++ b/src/sales/order/order.module.ts @@ -10,9 +10,10 @@ import { CustomerService } from '../customer/customer.service'; import { AddressCustomerService } from '../address-customer/address-customer.service'; import { ShoppingService } from '../shopping/shopping.service'; import { UserService } from 'src/Auth/services/user.service'; +import { RedisModule } from '../../redis/redis.module'; @Module({ - imports: [HttpModule], + imports: [HttpModule,RedisModule], controllers: [ OrderController, OrderController,], diff --git a/src/sales/order/order.service.ts b/src/sales/order/order.service.ts index f4f43a7..a1b342f 100644 --- a/src/sales/order/order.service.ts +++ b/src/sales/order/order.service.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable,Inject } from '@nestjs/common'; import { ShoppingItens } from 'src/domain/entity/tables/estprevendai.entity'; import { Sale } from 'src/domain/entity/tables/estvenda.entity'; import { Pcpedctemp } from 'src/domain/entity/tables/pcpedctemp.entity'; @@ -13,11 +13,14 @@ import { ListsService } from 'src/backoffice/lists/lists.service'; import { CustomerService } from '../customer/customer.service'; import { AddressCustomerService } from '../address-customer/address-customer.service'; import { ShoppingService } from '../shopping/shopping.service'; +import Redis = require('ioredis'); + @Injectable() export class OrderService { constructor( + @Inject('REDIS_CLIENT') private readonly redisClient: Redis.Redis, private readonly listsService: ListsService, private readonly customerService: CustomerService, private readonly addressCustomerService: AddressCustomerService, @@ -669,7 +672,6 @@ export class OrderService { numeroSeq = numeroSeq + 1; } - // execute some operations on this transaction: await queryRunner.manager .createQueryBuilder() .insert() @@ -687,7 +689,7 @@ export class OrderService { await queryRunner.release(); await connection.close(); } - //const idOrder = await this.getIdOrder(cart.idSeller); + } @@ -742,15 +744,10 @@ export class OrderService { let sql = 'SELECT NVL(PROXNUMPEDWEB,0) as "proxnumpedweb" FROM PCUSUARI ' + ' WHERE PCUSUARI.CODUSUR = :1 FOR UPDATE'; const seller = await queryRunner.query(sql, [idSeller]); - /* const seller = await queryRunner.manager - .getRepository(Pcusuari) - .createQueryBuilder('pcusuari') - .where("\"pcusuari\".codusur = :idseller", { idseller: idSeller }) - .getOne(); */ + const idOrder = seller[0].proxnumpedweb; console.log(idOrder); - // lets now open a new transaction: await queryRunner.startTransaction(); try { @@ -759,27 +756,18 @@ export class OrderService { ' WHERE PCUSUARI.CODUSUR = :1'; await queryRunner.query(sql, [idSeller]); - // await queryRunner.manager - // .createQueryBuilder() - // .update(Pcusuari) - // .set({ proxnumpedweb: idOrder + 1 }) - // .where("\"PCUSUARI\".codusur = :idseller", { idseller: idSeller }) - // .execute(); - - // commit transaction now: + await queryRunner.commitTransaction(); return idOrder; } catch (err) { - // since we have errors let's rollback changes we made await queryRunner.rollbackTransaction(); console.log(err); return -1; } finally { - // you need to release query runner which is manually created: await queryRunner.release(); await connection.close(); } @@ -807,77 +795,160 @@ export class OrderService { } } - async getOrders(store: string, initialDate: Date, finalDate: Date, - document: string, name: string, sellerId: number, idOrder: string) { - const connection = new Connection(connectionOptions); - await connection.connect(); - const queryRunner = connection.createQueryRunner(); - await queryRunner.connect(); - let sqlWhere = ''; - let sql = ''; + async getOrders( + store: string, + initialDate: any, + finalDate: any, + document: string, + name: string, + sellerId: number, + idOrder: string + ): Promise { + const initialDateObj = initialDate instanceof Date ? initialDate : new Date(initialDate); + const finalDateObj = finalDate instanceof Date ? finalDate : new Date(finalDate); + + const cacheKey = + 'getOrders:' + + store + + '_' + + initialDateObj.toISOString() + + '_' + + finalDateObj.toISOString() + + '_' + + document + + '_' + + name + + '_' + + sellerId + + '_' + + idOrder; + + const lockKey = 'lock:' + cacheKey; + const lockTimeout = 30; // lock expira em 30 segundos + try { + const cachedResult = await this.redisClient.get(cacheKey); + if (cachedResult) { + console.log('Retornando resultado do cache (getOrders)'); + return JSON.parse(cachedResult); + } + } catch (err) { + console.error('Erro ao acessar o Redis no getOrders:', err?.message || err); + } + + const lockValue = Date.now() + lockTimeout * 1000 + 1; + let acquiredLock: string | null = null; + try { + acquiredLock = await this.redisClient.set(lockKey, lockValue, 'NX', 'EX', lockTimeout); + } catch (err) { + console.error('Erro ao adquirir lock no Redis (getOrders):', err?.message || err); + } + + if (acquiredLock === 'OK') { - sql = ` SELECT TO_CHAR(PCPEDC.DATA, \'DD/MM/YYYY\') as "createDate" ` + - ` ,PCPEDC.NUMPED as "orderId" ` + - ` ,PCPEDC.CODFILIAL as "store" ` + - ` ,CASE WHEN PCPEDC.POSICAO = 'B' THEN 'BLOQUEADO' ` + - ` WHEN PCPEDC.POSICAO = 'P' THEN 'PENDENTE' ` + - ` WHEN PCPEDC.POSICAO = 'L' THEN 'LIBERADO' ` + - ` WHEN PCPEDC.POSICAO = 'M' THEN 'MONTADO' ` + - ` WHEN PCPEDC.POSICAO = 'F' THEN 'FATURADO' END as "status" ` + - ` ,PCPEDC.CODCLI as "customerId" ` + - ` ,PCPEDC.CODUSUR as "sellerId" ` + - ` ,PCCLIENT.CLIENTE as "customerName" ` + - ` ,PCPEDC.VLATEND as "orderValue" ` + - ` ,PCPEDC.NUMITENS as "itens" ` + - ` ,CASE WHEN ( SELECT COUNT(1) FROM ESTPIX WHERE ESTPIX.NUMPED = PCPEDC.NUMPED ) > 0 THEN 'S' ELSE 'N' END as "pixCreate" ` + - ` FROM PCPEDC, PCCLIENT ` + - ` WHERE PCPEDC.CODCLI = PCCLIENT.CODCLI ` + - ` AND PCPEDC.CONDVENDA IN (1,7) ` + - ` AND PCPEDC.DTCANCEL IS NULL AND PCPEDC.POSICAO NOT IN ('C') `; - ` AND PCPEDC.ROTINALANC = 'VENDAWEB' `; - if (store != null && store != '') { - sqlWhere += ` AND PCPEDC.CODFILIAL = '${store}' `; + const connection = new Connection(connectionOptions); + await connection.connect(); + const queryRunner = connection.createQueryRunner(); + await queryRunner.connect(); + let sqlWhere = ''; + let sql = ''; + + try { + sql = ` SELECT TO_CHAR(PCPEDC.DATA, 'DD/MM/YYYY') as "createDate", + PCPEDC.NUMPED as "orderId", + PCPEDC.CODFILIAL as "store", + CASE + WHEN PCPEDC.POSICAO = 'B' THEN 'BLOQUEADO' + WHEN PCPEDC.POSICAO = 'P' THEN 'PENDENTE' + WHEN PCPEDC.POSICAO = 'L' THEN 'LIBERADO' + WHEN PCPEDC.POSICAO = 'M' THEN 'MONTADO' + WHEN PCPEDC.POSICAO = 'F' THEN 'FATURADO' + END as "status", + PCPEDC.CODCLI as "customerId", + PCPEDC.CODUSUR as "sellerId", + PCCLIENT.CLIENTE as "customerName", + PCPEDC.VLATEND as "orderValue", + PCPEDC.NUMITENS as "itens", + CASE WHEN ( SELECT COUNT(1) FROM ESTPIX WHERE ESTPIX.NUMPED = PCPEDC.NUMPED ) > 0 THEN 'S' ELSE 'N' END as "pixCreate" + FROM PCPEDC, PCCLIENT + WHERE PCPEDC.CODCLI = PCCLIENT.CODCLI + AND PCPEDC.CONDVENDA IN (1,7) + AND PCPEDC.DTCANCEL IS NULL + AND PCPEDC.POSICAO NOT IN ('C') + AND PCPEDC.ROTINALANC = 'VENDAWEB' `; + + if (store && store !== '') { + sqlWhere += ` AND PCPEDC.CODFILIAL = '${store}' `; } - if (document != null && document != '') { - sqlWhere += ` AND REGEXP_REPLACE(PCCLIENT.CGCENT, '[^0-9]', '') = REGEXP_REPLACE('${document}', '[^0-9]', '')`; + if (document && document !== '') { + sqlWhere += ` AND REGEXP_REPLACE(PCCLIENT.CGCENT, '[^0-9]', '') = REGEXP_REPLACE('${document}', '[^0-9]', '') `; } - if (name != null && name != '') { - sqlWhere += ` AND PCCLIENT.CLIENTE LIKE '${name.replace('@', '%')}'||'%'`; + if (name && name !== '') { + sqlWhere += ` AND PCCLIENT.CLIENTE LIKE '${name.replace('@', '%')}'||'%' `; } if (sellerId > 0) { - sqlWhere += ` AND PCPEDC.CODUSUR = ${sellerId} `; + sqlWhere += ` AND PCPEDC.CODUSUR = ${sellerId} `; } - if (idOrder.trim() != null && idOrder.trim() != '') { - sqlWhere += ` AND PCPEDC.NUMPED = ${idOrder} `; + if (idOrder && idOrder.trim() !== '') { + sqlWhere += ` AND PCPEDC.NUMPED = ${idOrder} `; } - - //tratamento de data// - const startDate = new Date(initialDate); + + // Formatação das datas para o SQL + const startDate = initialDateObj; let day = startDate.getDate(); let month = ("00" + (startDate.getMonth() + 1)).slice(-2); let year = startDate.getFullYear(); const startFormat = day + "/" + month + "/" + year; - const endDate = new Date(finalDate); + + const endDate = finalDateObj; day = endDate.getDate(); month = ("00" + (endDate.getMonth() + 1)).slice(-2); year = endDate.getFullYear(); const endFormat = day + "/" + month + "/" + year; - - sqlWhere += ` AND PCPEDC.DATA BETWEEN TO_DATE('${startFormat}', 'DD/MM/YYYY') AND TO_DATE('${endFormat}', 'DD/MM/YYYY') `; - + + sqlWhere += ` AND PCPEDC.DATA BETWEEN TO_DATE('${startFormat}', 'DD/MM/YYYY') AND TO_DATE('${endFormat}', 'DD/MM/YYYY') `; + const result = await queryRunner.query(sql + sqlWhere); + + // Armazena o resultado no cache com TTL de 1 hora (3600 segundos) + try { + await this.redisClient.set(cacheKey, JSON.stringify(result), 'EX', 3600); + } catch (cacheSetErr) { + console.error('Erro ao salvar o resultado no cache (getOrders):', cacheSetErr?.message || cacheSetErr); + } + return result; - } catch (error) { - console.log(error); + } catch (error) { + console.error('Erro ao executar a query no getOrders:', error?.message || error); throw error; - } finally { + } finally { await queryRunner.release(); await connection.close(); + + // Libera o lock, somente se o valor armazenado for o mesmo deste processo + try { + const currentLockValue = await this.redisClient.get(lockKey); + if (currentLockValue === lockValue.toString()) { + await this.redisClient.del(lockKey); + } + } catch (lockErr) { + console.error('Erro ao liberar o lock do Redis (getOrders):', lockErr?.message || lockErr); + } + } + } else { + // Se não conseguir adquirir o lock, aguarda 1 segundo e tenta novamente + console.log('Lock não adquirido (getOrders), aguardando e tentando novamente...'); + await this.sleep(1000); + return this.getOrders(store, initialDate, finalDate, document, name, sellerId, idOrder); } - - } + } + + // Função auxiliar para aguardar um período determinado (em milissegundos) + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + async getItensOrder(idOrder: number) { const connection = new Connection(connectionOptions); diff --git a/src/sales/sales/sales.service.ts b/src/sales/sales/sales.service.ts index 1563bec..f7e1921 100644 --- a/src/sales/sales/sales.service.ts +++ b/src/sales/sales/sales.service.ts @@ -352,36 +352,97 @@ export class SalesService { } - async searchByDepartment(store: string, pageSize: number, pageNumber: number, urlDepartment: string): Promise { - const connectionDb = new Connection(connectionOptions); - await connectionDb.connect(); - const queryRunner = connectionDb.createQueryRunner(); - await queryRunner.connect(); + async searchByDepartment( + store: string, + pageSize: number, + pageNumber: number, + urlDepartment: string + ): Promise { + const cacheKey = + 'searchByDepartment:' + + store + + '_' + + pageSize + + '_' + + pageNumber + + '_' + + urlDepartment; + const lockKey = 'lock:' + cacheKey; + const lockTimeout = 30; // lock expira em 30 segundos + try { - if (pageSize == 0) - pageSize = 50; - if (pageNumber == 0) - pageNumber = 1; + const cachedResult = await this.redisClient.get(cacheKey); + if (cachedResult) { + console.log('Retornando resultado do cache (searchByDepartment)'); + return JSON.parse(cachedResult); + } + } catch (err) { + console.error('Erro ao acessar o Redis no searchByDepartment:', err?.message || err); + } + + const lockValue = Date.now() + lockTimeout * 1000 + 1; + let acquiredLock: string | null = null; + try { + acquiredLock = await this.redisClient.set(lockKey, lockValue, 'NX', 'EX', lockTimeout); + } catch (err) { + console.error('Erro ao adquirir lock no Redis (searchByDepartment):', err?.message || err); + } + + if (acquiredLock === 'OK') { + const connectionDb = new Connection(connectionOptions); + await connectionDb.connect(); + const queryRunner = connectionDb.createQueryRunner(); + await queryRunner.connect(); + try { + if (pageSize === 0) pageSize = 50; + if (pageNumber === 0) pageNumber = 1; const offSet = (pageNumber - 1) * pageSize; + let products = await queryRunner.manager - .getRepository(SalesProduct) - .createQueryBuilder('esvlistaprodutos') - .where('\"esvlistaprodutos\".urldepartamento = :urlDepartment', { urlDepartment }) - .andWhere("(\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99')", { codfilial: store }) - .limit(pageSize) - .offset(offSet) - .orderBy("\"esvlistaprodutos\".DESCRICAO", "ASC") - .getMany(); + .getRepository(SalesProduct) + .createQueryBuilder('esvlistaprodutos') + .where('"esvlistaprodutos".urldepartamento = :urlDepartment', { urlDepartment }) + .andWhere('("esvlistaprodutos".codfilial = :codfilial OR :codfilial = \'99\')', { codfilial: store }) + .limit(pageSize) + .offset(offSet) + .orderBy('"esvlistaprodutos".DESCRICAO', 'ASC') + .getMany(); + products = this.createListImages(products); + + try { + await this.redisClient.set(cacheKey, JSON.stringify(products), 'EX', 3600); + } catch (cacheErr) { + console.error('Erro ao salvar o resultado no cache (searchByDepartment):', cacheErr?.message || cacheErr); + } + return products; - } catch (error) { - console.log(error); + } catch (error) { + console.error('Erro ao executar a query no searchByDepartment:', error?.message || error); throw error; - } finally { + } finally { await queryRunner.release(); await connectionDb.close(); + + try { + const currentLockValue = await this.redisClient.get(lockKey); + if (currentLockValue === lockValue.toString()) { + await this.redisClient.del(lockKey); + } + } catch (lockErr) { + console.error('Erro ao liberar o lock do Redis (searchByDepartment):', lockErr?.message || lockErr); + } + } + } else { + console.log('Lock não adquirido (searchByDepartment), aguardando e tentando novamente...'); + await this.sleep(1000); + return this.searchByDepartment(store, pageSize, pageNumber, urlDepartment); } - } + } + + + + async searchBySection(store: string, pageSize: number, pageNumber: number, urlDepartment: string, urlSection: string): Promise { const connectionDb = new Connection(connectionOptions); @@ -1157,17 +1218,14 @@ export class SalesService { async searchProduct2(store: string, pageSize: number, pageNumber: number, filter: FilterProduct) { console.log('searchProduct2'); - // Cria uma chave única para o cache utilizando os parâmetros da função const cacheKey = `searchProduct2:${store}:${pageSize}:${pageNumber}:${JSON.stringify(filter)}`; - // Tenta recuperar os produtos do cache const cachedProducts = await this.redisClient.get(cacheKey); if (cachedProducts) { console.log('Retornando produtos do cache'); return JSON.parse(cachedProducts); } - // Monta a consulta SQL const sql = 'SELECT esvlistaprodutos.CODPROD "idProduct" ' + ` ,esvlistaprodutos.SEQ "seq" ` + ' ,esvlistaprodutos.DESCRICAO "smallDescription" ' + @@ -1210,7 +1268,6 @@ export class SalesService { ' FROM esvlistaprodutos ' + ' WHERE 1 = 1'; - // Concatena as condições WHERE baseadas nos filtros let where = ""; if (filter.text != null) { where += ` AND ( ESF_REMOVE_ACENTUACAO(UPPER(esvlistaprodutos.descricao)) LIKE ` +