From a4196e4fa221718cd1c2fee0a8a625d2769fd5fc Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Mon, 20 May 2024 12:33:19 -0300 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20adicionar=20servi=C3=A7o=20agenda?= =?UTF-8?q?do=20para=20remover=20itens(supplies)=20abaixo=20de=20urgente?= =?UTF-8?q?=20a=20cada=2048h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 48 ++++++++ package.json | 1 + .../migration.sql | 15 +++ .../migration.sql | 10 ++ prisma/schema.prisma | 17 +++ src/app.module.ts | 5 + src/schedule/item-cleanup.service.ts | 113 ++++++++++++++++++ src/schedule/schedule.module.ts | 9 ++ src/schedule/schedule.service.spec.ts | 18 +++ 9 files changed, 236 insertions(+) create mode 100644 prisma/migrations/20240520151114_create_supplu_auto_remove_logs/migration.sql create mode 100644 prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql create mode 100644 src/schedule/item-cleanup.service.ts create mode 100644 src/schedule/schedule.module.ts create mode 100644 src/schedule/schedule.service.spec.ts diff --git a/package-lock.json b/package-lock.json index 455fac11..e957faaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-fastify": "^10.3.8", + "@nestjs/schedule": "^4.0.2", "@nestjs/swagger": "^7.3.1", "@prisma/client": "^5.13.0", "bcrypt": "^5.1.1", @@ -2101,6 +2102,19 @@ } } }, + "node_modules/@nestjs/schedule": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-4.0.2.tgz", + "integrity": "sha512-po9oauE7fO0CjhDKvVC2tzEgjOUwhxYoIsXIVkgfu+xaDMmzzpmXY2s1LT4oP90Z+PaTtPoAHmhslnYmo4mSZg==", + "dependencies": { + "cron": "3.1.7", + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@nestjs/schematics": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.1.tgz", @@ -2544,6 +2558,11 @@ "@types/node": "*" } }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==" + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -4125,6 +4144,15 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cron": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.7.tgz", + "integrity": "sha512-tlBg7ARsAMQLzgwqVxy8AZl/qlTc5nibqYwtNGoCrd+cV+ugI+tvZC1oT/8dFH8W455YrywGykx/KMmAqOr7Jw==", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.4.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7026,6 +7054,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-string": { "version": "0.30.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", @@ -9658,6 +9694,18 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index 70a6153f..7a2ece26 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-fastify": "^10.3.8", + "@nestjs/schedule": "^4.0.2", "@nestjs/swagger": "^7.3.1", "@prisma/client": "^5.13.0", "bcrypt": "^5.1.1", diff --git a/prisma/migrations/20240520151114_create_supplu_auto_remove_logs/migration.sql b/prisma/migrations/20240520151114_create_supplu_auto_remove_logs/migration.sql new file mode 100644 index 00000000..77611eb9 --- /dev/null +++ b/prisma/migrations/20240520151114_create_supplu_auto_remove_logs/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "supply_auto_remove_logs" ( + "id" TEXT NOT NULL, + "supply_id" TEXT NOT NULL, + "shelter_id" TEXT NOT NULL, + "created_at" VARCHAR(32) NOT NULL, + + CONSTRAINT "supply_auto_remove_logs_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "supply_auto_remove_logs" ADD CONSTRAINT "supply_auto_remove_logs_shelter_id_fkey" FOREIGN KEY ("shelter_id") REFERENCES "shelters"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "supply_auto_remove_logs" ADD CONSTRAINT "supply_auto_remove_logs_supply_id_fkey" FOREIGN KEY ("supply_id") REFERENCES "supplies"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql b/prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql new file mode 100644 index 00000000..a39bfa27 --- /dev/null +++ b/prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `created_at` on the `supply_auto_remove_logs` table. All the data in the column will be lost. + - Added the required column `removed_at` to the `supply_auto_remove_logs` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "supply_auto_remove_logs" DROP COLUMN "created_at", +ADD COLUMN "removed_at" VARCHAR(32) NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 04d0ee48..cbdf8c70 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -85,6 +85,7 @@ model Supply { supplyCategory SupplyCategory @relation(fields: [supplyCategoryId], references: [id]) shelterSupplies ShelterSupply[] + supplyAutoRemoveLogs SupplyAutoRemoveLog[] @@map("supplies") } @@ -114,6 +115,7 @@ model Shelter { shelterManagers ShelterManagers[] shelterSupplies ShelterSupply[] + supplyAutoRemoveLogs SupplyAutoRemoveLog[] @@map("shelters") } @@ -151,3 +153,18 @@ model Supporters { @@map("supporters") } + + +model SupplyAutoRemoveLog { + id String @id @default(uuid()) + supplyId String @map("supply_id") + shelterId String @map("shelter_id") + removedAt String @map("removed_at") @db.VarChar(32) + + + + shelter Shelter @relation(fields: [shelterId], references: [id]) + supply Supply @relation(fields: [supplyId], references: [id]) + + @@map("supply_auto_remove_logs") +} \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 82663d34..62abf9a0 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -13,6 +13,9 @@ import { ShelterManagersModule } from './shelter-managers/shelter-managers.modul import { ShelterSupplyModule } from './shelter-supply/shelter-supply.module'; import { PartnersModule } from './partners/partners.module'; import { SupportersModule } from './supporters/supporters.module'; +import { ScheduleModule } from '@nestjs/schedule'; +import { ItemCleanupService } from './schedule/item-cleanup.service'; + @Module({ imports: [ @@ -26,6 +29,7 @@ import { SupportersModule } from './supporters/supporters.module'; ShelterSupplyModule, PartnersModule, SupportersModule, + ScheduleModule.forRoot(), ], controllers: [], providers: [ @@ -33,6 +37,7 @@ import { SupportersModule } from './supporters/supporters.module'; provide: APP_INTERCEPTOR, useClass: ServerResponseInterceptor, }, + ItemCleanupService ], }) export class AppModule implements NestModule { diff --git a/src/schedule/item-cleanup.service.ts b/src/schedule/item-cleanup.service.ts new file mode 100644 index 00000000..edc07edc --- /dev/null +++ b/src/schedule/item-cleanup.service.ts @@ -0,0 +1,113 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron } from '@nestjs/schedule'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { SupplyPriority } from 'src/supply/types'; + +@Injectable() +export class ItemCleanupService { + private logger = new Logger('ItemCleanupService'); + constructor(private readonly prismaService: PrismaService) {} + + @Cron('0 0 */2 * *') + async handleCron() { + const shelters = await this.prismaService.shelter.findMany({ + include: { + shelterSupplies: { + include: { + supply: true, + }, + }, + }, + }); + + for (const shelter of shelters) { + this.logger.log( + `Verificando necessidade de exclusão de itens no abrigo ${shelter.name}`, + ); + for (const shelterSupply of shelter.shelterSupplies) { + const supply = shelterSupply.supply; + if ( + this.canRemoveSupply( + supply.createdAt, + supply.updatedAt, + shelterSupply.priority, + ) + ) { + this.removeSupply(supply.id, shelter.id, supply.name); + } + } + } + } + + private hasPassed48Hours( + createdAt: string | null, + updatedAt: string | null, + ): boolean { + if (updatedAt !== null) { + const updatedAtTime = new Date(updatedAt).getTime(); + const now = new Date().getTime(); + const difference = now - updatedAtTime; + const hours = Math.floor(difference / (1000 * 60 * 60)); + return hours >= 48; + } else { + const createdAtTime = + createdAt !== null ? new Date(createdAt).getTime() : 0; + const now = new Date().getTime(); + const difference = now - createdAtTime; + const hours = Math.floor(difference / (1000 * 60 * 60)); + return hours >= 48; + } + } + + private async removeSupply( + supplyId: string, + shelterId: string, + supplyName: string, + ) { + this.logger.log( + `Suprimento ${supplyName} já está há 48 horas com baixa movimentação e prioridade 0. Removendo do abrigo ${shelterId}`, + ); + + try { + await this.prismaService.shelterSupply.deleteMany({ + where: { + supplyId, + shelterId, + }, + }); + + await this.prismaService.supplyAutoRemoveLog.create({ + data: { + supply: { + connect: { + id: supplyId, + }, + }, + shelter: { + connect: { + id: shelterId, + }, + }, + removedAt: new Date().toISOString(), + }, + }); + } catch (error) { + this.logger.error( + `Erro ao tentar remover o suprimento ${supplyId} do abrigo ${shelterId}`, + (error as Error).stack, + ); + } + } + + private canRemoveSupply( + createdAt: string | null, + updatedAt: string | null, + priority: SupplyPriority, + ): boolean { + const hasPassedTime = this.hasPassed48Hours(createdAt, updatedAt); + + const isNotUrgent = priority !== SupplyPriority.Urgent; + + return isNotUrgent && hasPassedTime; + } +} diff --git a/src/schedule/schedule.module.ts b/src/schedule/schedule.module.ts new file mode 100644 index 00000000..5a1fa0e2 --- /dev/null +++ b/src/schedule/schedule.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { ItemCleanupService } from './item-cleanup.service'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Module({ + providers: [ItemCleanupService, PrismaService], + exports: [ItemCleanupService], +}) +export class ScheduleModule {} diff --git a/src/schedule/schedule.service.spec.ts b/src/schedule/schedule.service.spec.ts new file mode 100644 index 00000000..a8e745ec --- /dev/null +++ b/src/schedule/schedule.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ScheduleService } from './item-cleanup.service'; + +describe('ScheduleService', () => { + let service: ScheduleService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ScheduleService], + }).compile(); + + service = module.get(ScheduleService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); From 7a877f32bc4fb715aaeaccf3d72f755499507c64 Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Mon, 20 May 2024 13:38:02 -0300 Subject: [PATCH 02/10] =?UTF-8?q?fix:=20remove=20modulo=20schedule=20gen?= =?UTF-8?q?=C3=A9rico=20e=20move=20o=20service=20para=20shelter-supply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 3 - src/schedule/item-cleanup.service.ts | 113 -------------------- src/schedule/schedule.module.ts | 9 -- src/shelter-supply/shelter-supply.module.ts | 3 +- 4 files changed, 2 insertions(+), 126 deletions(-) delete mode 100644 src/schedule/item-cleanup.service.ts delete mode 100644 src/schedule/schedule.module.ts diff --git a/src/app.module.ts b/src/app.module.ts index 62abf9a0..02350120 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,8 +14,6 @@ import { ShelterSupplyModule } from './shelter-supply/shelter-supply.module'; import { PartnersModule } from './partners/partners.module'; import { SupportersModule } from './supporters/supporters.module'; import { ScheduleModule } from '@nestjs/schedule'; -import { ItemCleanupService } from './schedule/item-cleanup.service'; - @Module({ imports: [ @@ -37,7 +35,6 @@ import { ItemCleanupService } from './schedule/item-cleanup.service'; provide: APP_INTERCEPTOR, useClass: ServerResponseInterceptor, }, - ItemCleanupService ], }) export class AppModule implements NestModule { diff --git a/src/schedule/item-cleanup.service.ts b/src/schedule/item-cleanup.service.ts deleted file mode 100644 index edc07edc..00000000 --- a/src/schedule/item-cleanup.service.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { SupplyPriority } from 'src/supply/types'; - -@Injectable() -export class ItemCleanupService { - private logger = new Logger('ItemCleanupService'); - constructor(private readonly prismaService: PrismaService) {} - - @Cron('0 0 */2 * *') - async handleCron() { - const shelters = await this.prismaService.shelter.findMany({ - include: { - shelterSupplies: { - include: { - supply: true, - }, - }, - }, - }); - - for (const shelter of shelters) { - this.logger.log( - `Verificando necessidade de exclusão de itens no abrigo ${shelter.name}`, - ); - for (const shelterSupply of shelter.shelterSupplies) { - const supply = shelterSupply.supply; - if ( - this.canRemoveSupply( - supply.createdAt, - supply.updatedAt, - shelterSupply.priority, - ) - ) { - this.removeSupply(supply.id, shelter.id, supply.name); - } - } - } - } - - private hasPassed48Hours( - createdAt: string | null, - updatedAt: string | null, - ): boolean { - if (updatedAt !== null) { - const updatedAtTime = new Date(updatedAt).getTime(); - const now = new Date().getTime(); - const difference = now - updatedAtTime; - const hours = Math.floor(difference / (1000 * 60 * 60)); - return hours >= 48; - } else { - const createdAtTime = - createdAt !== null ? new Date(createdAt).getTime() : 0; - const now = new Date().getTime(); - const difference = now - createdAtTime; - const hours = Math.floor(difference / (1000 * 60 * 60)); - return hours >= 48; - } - } - - private async removeSupply( - supplyId: string, - shelterId: string, - supplyName: string, - ) { - this.logger.log( - `Suprimento ${supplyName} já está há 48 horas com baixa movimentação e prioridade 0. Removendo do abrigo ${shelterId}`, - ); - - try { - await this.prismaService.shelterSupply.deleteMany({ - where: { - supplyId, - shelterId, - }, - }); - - await this.prismaService.supplyAutoRemoveLog.create({ - data: { - supply: { - connect: { - id: supplyId, - }, - }, - shelter: { - connect: { - id: shelterId, - }, - }, - removedAt: new Date().toISOString(), - }, - }); - } catch (error) { - this.logger.error( - `Erro ao tentar remover o suprimento ${supplyId} do abrigo ${shelterId}`, - (error as Error).stack, - ); - } - } - - private canRemoveSupply( - createdAt: string | null, - updatedAt: string | null, - priority: SupplyPriority, - ): boolean { - const hasPassedTime = this.hasPassed48Hours(createdAt, updatedAt); - - const isNotUrgent = priority !== SupplyPriority.Urgent; - - return isNotUrgent && hasPassedTime; - } -} diff --git a/src/schedule/schedule.module.ts b/src/schedule/schedule.module.ts deleted file mode 100644 index 5a1fa0e2..00000000 --- a/src/schedule/schedule.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ItemCleanupService } from './item-cleanup.service'; -import { PrismaService } from 'src/prisma/prisma.service'; - -@Module({ - providers: [ItemCleanupService, PrismaService], - exports: [ItemCleanupService], -}) -export class ScheduleModule {} diff --git a/src/shelter-supply/shelter-supply.module.ts b/src/shelter-supply/shelter-supply.module.ts index 0609a149..84910836 100644 --- a/src/shelter-supply/shelter-supply.module.ts +++ b/src/shelter-supply/shelter-supply.module.ts @@ -3,10 +3,11 @@ import { Module } from '@nestjs/common'; import { ShelterSupplyService } from './shelter-supply.service'; import { ShelterSupplyController } from './shelter-supply.controller'; import { PrismaModule } from '../prisma/prisma.module'; +import { ItemCleanupService } from './item-cleanup.service'; @Module({ imports: [PrismaModule], - providers: [ShelterSupplyService], + providers: [ShelterSupplyService, ItemCleanupService], controllers: [ShelterSupplyController], }) export class ShelterSupplyModule {} From da6583e6269b12a69d5a60bcbddd28eaa1bead17 Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Mon, 20 May 2024 13:38:48 -0300 Subject: [PATCH 03/10] =?UTF-8?q?fix:=20remove=20pasta=20de=20modulo=20gen?= =?UTF-8?q?=C3=A9rico=20desnecess=C3=A1ria?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schedule/schedule.service.spec.ts | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 src/schedule/schedule.service.spec.ts diff --git a/src/schedule/schedule.service.spec.ts b/src/schedule/schedule.service.spec.ts deleted file mode 100644 index a8e745ec..00000000 --- a/src/schedule/schedule.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ScheduleService } from './item-cleanup.service'; - -describe('ScheduleService', () => { - let service: ScheduleService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ScheduleService], - }).compile(); - - service = module.get(ScheduleService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); From d485a9dd87e9ca5d35d2bafe71e19874723bad9d Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Mon, 20 May 2024 13:40:17 -0300 Subject: [PATCH 04/10] =?UTF-8?q?refact:=20Adicionado=20cron=20job=20para?= =?UTF-8?q?=20remo=C3=A7=C3=A3o=20autom=C3=A1tica=20de=20suprimentos=20de?= =?UTF-8?q?=20abrigo=20n=C3=A3o=20urgentes=20ap=C3=B3s=2048=20horas=20de?= =?UTF-8?q?=20inatividade.=20Implementada=20l=C3=B3gica=20de=20verifica?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20tempo=20decorrido=20com=20base=20em=20date?= =?UTF-8?q?-fns=20e=20utiliza=C3=A7=C3=A3o=20de=20transa=C3=A7=C3=B5es=20d?= =?UTF-8?q?o=20Prisma=20para=20garantir=20consist=C3=AAncia=20de=20dados.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shelter-supply-cleanup.service.ts | 114 ++++++++++++++++++ src/shelter-supply/shelter-supply.module.ts | 4 +- 2 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/shelter-supply/shelter-supply-cleanup.service.ts diff --git a/src/shelter-supply/shelter-supply-cleanup.service.ts b/src/shelter-supply/shelter-supply-cleanup.service.ts new file mode 100644 index 00000000..888e1118 --- /dev/null +++ b/src/shelter-supply/shelter-supply-cleanup.service.ts @@ -0,0 +1,114 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron } from '@nestjs/schedule'; +import { differenceInHours, parseISO } from 'date-fns'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { SupplyPriority } from 'src/supply/types'; + +@Injectable() +export class ShelterSupplyCleanupService { + private logger = new Logger('ItemCleanupService'); + constructor(private readonly prismaService: PrismaService) {} + + @Cron('0 0 */2 * *') + async handleCron() { + const shelterSupplies = await this.prismaService.shelterSupply.findMany({ + include: { + supply: true, + shelter: true, + }, + }); + + for (const shelterSupply of shelterSupplies) { + this.logger.log( + `Verificando necessidade de exclusão de itens no abrigo ${shelterSupply.shelter.name}`, + ); + + const { supply } = shelterSupply; + + if ( + this.canRemoveSupply( + supply.createdAt, + supply.updatedAt, + shelterSupply.priority, + ) + ) { + await this.removeSupply( + supply.id, + shelterSupply.shelterId, + supply.name, + ); + } + } + } + + private hasPassed48Hours( + createdAt: string | null, + updatedAt: string | null, + ): boolean { + let targetDate: Date; + if (updatedAt !== null) { + targetDate = parseISO(updatedAt); + } else if (createdAt !== null) { + targetDate = parseISO(createdAt); + } else { + return false; + } + + const hoursDifference = differenceInHours(new Date(), targetDate); + + return hoursDifference >= 48; + } + + private async removeSupply( + supplyId: string, + shelterId: string, + supplyName: string, + ) { + this.logger.log( + `Suprimento ${supplyName} já está há 48 horas com baixa movimentação e não é urgente. Removendo relação com o abrigo ${shelterId}`, + ); + + try { + await this.prismaService.$transaction([ + this.prismaService.shelterSupply.deleteMany({ + where: { + supplyId, + shelterId, + }, + }), + this.prismaService.supplyAutoRemoveLog.create({ + data: { + supply: { + connect: { + id: supplyId, + }, + }, + shelter: { + connect: { + id: shelterId, + }, + }, + removedAt: new Date().toISOString(), + }, + }), + ]); + } catch (error) { + this.logger.error( + `Erro ao tentar remover o suprimento ${supplyId} do abrigo ${shelterId}`, + (error as Error).stack, + ); + } + } + + private canRemoveSupply( + createdAt: string | null, + updatedAt: string | null, + priority: SupplyPriority, + ): boolean { + const hasPassedTime = this.hasPassed48Hours(createdAt, updatedAt); + + const isNotUrgent = priority !== SupplyPriority.Urgent; + + return isNotUrgent && hasPassedTime; + } +} diff --git a/src/shelter-supply/shelter-supply.module.ts b/src/shelter-supply/shelter-supply.module.ts index 84910836..4775a120 100644 --- a/src/shelter-supply/shelter-supply.module.ts +++ b/src/shelter-supply/shelter-supply.module.ts @@ -3,11 +3,11 @@ import { Module } from '@nestjs/common'; import { ShelterSupplyService } from './shelter-supply.service'; import { ShelterSupplyController } from './shelter-supply.controller'; import { PrismaModule } from '../prisma/prisma.module'; -import { ItemCleanupService } from './item-cleanup.service'; +import { ShelterSupplyCleanupService } from './shelter-supply-cleanup.service'; @Module({ imports: [PrismaModule], - providers: [ShelterSupplyService, ItemCleanupService], + providers: [ShelterSupplyService, ShelterSupplyCleanupService], controllers: [ShelterSupplyController], }) export class ShelterSupplyModule {} From 22af9ea5d50df874688b2dac198551fb2e40ef6a Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Mon, 20 May 2024 13:55:59 -0300 Subject: [PATCH 05/10] =?UTF-8?q?fix:=20crrogie=20parametros=20passados=20?= =?UTF-8?q?para=20as=20fun=C3=A7=C3=B5es,=20renomeia=20fun=C3=A7=C3=B5es?= =?UTF-8?q?=20para=20facilitar=20leitura=20do=20c=C3=B3digo.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shelter-supply-cleanup.service.ts | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/shelter-supply/shelter-supply-cleanup.service.ts b/src/shelter-supply/shelter-supply-cleanup.service.ts index 888e1118..95625815 100644 --- a/src/shelter-supply/shelter-supply-cleanup.service.ts +++ b/src/shelter-supply/shelter-supply-cleanup.service.ts @@ -6,7 +6,7 @@ import { SupplyPriority } from 'src/supply/types'; @Injectable() export class ShelterSupplyCleanupService { - private logger = new Logger('ItemCleanupService'); + private logger = new Logger('ShelterSupplyCleanupService'); constructor(private readonly prismaService: PrismaService) {} @Cron('0 0 */2 * *') @@ -23,34 +23,33 @@ export class ShelterSupplyCleanupService { `Verificando necessidade de exclusão de itens no abrigo ${shelterSupply.shelter.name}`, ); - const { supply } = shelterSupply; - if ( - this.canRemoveSupply( - supply.createdAt, - supply.updatedAt, + this.canRemoveShelterSupply( + shelterSupply.createdAt, + shelterSupply.updatedAt, shelterSupply.priority, ) ) { - await this.removeSupply( - supply.id, + await this.removeShelterSupply( + shelterSupply.supply.id, shelterSupply.shelterId, - supply.name, + shelterSupply.supply.name, ); } } } - private hasPassed48Hours( + hasPassed48Hours( createdAt: string | null, updatedAt: string | null, ): boolean { - let targetDate: Date; - if (updatedAt !== null) { - targetDate = parseISO(updatedAt); - } else if (createdAt !== null) { - targetDate = parseISO(createdAt); - } else { + const targetDate = updatedAt + ? parseISO(updatedAt) + : createdAt + ? parseISO(createdAt) + : null; + + if (!targetDate) { return false; } @@ -59,7 +58,7 @@ export class ShelterSupplyCleanupService { return hoursDifference >= 48; } - private async removeSupply( + private async removeShelterSupply( supplyId: string, shelterId: string, supplyName: string, @@ -100,7 +99,7 @@ export class ShelterSupplyCleanupService { } } - private canRemoveSupply( + private canRemoveShelterSupply( createdAt: string | null, updatedAt: string | null, priority: SupplyPriority, From 9c0cc2b31ab212c83908618e9c161b09f6c923d2 Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Mon, 20 May 2024 14:11:49 -0300 Subject: [PATCH 06/10] =?UTF-8?q?refact:=20ajuste=20parametro=20das=20fun?= =?UTF-8?q?=C3=A7=C3=B5es=20e=20move=20l=C3=B3gica=20de=20canRemove=20para?= =?UTF-8?q?=20dentro=20de=20removeShelterSupply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shelter-supply-cleanup.service.ts | 47 +++++++------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/shelter-supply/shelter-supply-cleanup.service.ts b/src/shelter-supply/shelter-supply-cleanup.service.ts index 95625815..056dc303 100644 --- a/src/shelter-supply/shelter-supply-cleanup.service.ts +++ b/src/shelter-supply/shelter-supply-cleanup.service.ts @@ -1,5 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; +import { ShelterSupply } from '@prisma/client'; import { differenceInHours, parseISO } from 'date-fns'; import { PrismaService } from 'src/prisma/prisma.service'; import { SupplyPriority } from 'src/supply/types'; @@ -23,19 +24,7 @@ export class ShelterSupplyCleanupService { `Verificando necessidade de exclusão de itens no abrigo ${shelterSupply.shelter.name}`, ); - if ( - this.canRemoveShelterSupply( - shelterSupply.createdAt, - shelterSupply.updatedAt, - shelterSupply.priority, - ) - ) { - await this.removeShelterSupply( - shelterSupply.supply.id, - shelterSupply.shelterId, - shelterSupply.supply.name, - ); - } + await this.removeShelterSupply(shelterSupply); } } @@ -58,33 +47,30 @@ export class ShelterSupplyCleanupService { return hoursDifference >= 48; } - private async removeShelterSupply( - supplyId: string, - shelterId: string, - supplyName: string, - ) { + private async removeShelterSupply(shelterSupply: ShelterSupply) { this.logger.log( - `Suprimento ${supplyName} já está há 48 horas com baixa movimentação e não é urgente. Removendo relação com o abrigo ${shelterId}`, + `Suprimento ${shelterSupply.supplyId} já está há 48 horas com baixa movimentação e não é urgente. Removendo relação com o abrigo ${shelterSupply.shelterId}`, ); + if (!this.canRemoveShelterSupply(shelterSupply)) return; try { await this.prismaService.$transaction([ this.prismaService.shelterSupply.deleteMany({ where: { - supplyId, - shelterId, + supplyId: shelterSupply.supplyId, + shelterId: shelterSupply.shelterId, }, }), this.prismaService.supplyAutoRemoveLog.create({ data: { supply: { connect: { - id: supplyId, + id: shelterSupply.supplyId, }, }, shelter: { connect: { - id: shelterId, + id: shelterSupply.shelterId, }, }, removedAt: new Date().toISOString(), @@ -93,20 +79,19 @@ export class ShelterSupplyCleanupService { ]); } catch (error) { this.logger.error( - `Erro ao tentar remover o suprimento ${supplyId} do abrigo ${shelterId}`, + `Erro ao tentar remover o suprimento ${shelterSupply.supplyId} do abrigo ${shelterSupply.shelterId}`, (error as Error).stack, ); } } - private canRemoveShelterSupply( - createdAt: string | null, - updatedAt: string | null, - priority: SupplyPriority, - ): boolean { - const hasPassedTime = this.hasPassed48Hours(createdAt, updatedAt); + private canRemoveShelterSupply(shelterSupply: ShelterSupply): boolean { + const hasPassedTime = this.hasPassed48Hours( + shelterSupply.createdAt, + shelterSupply.updatedAt, + ); - const isNotUrgent = priority !== SupplyPriority.Urgent; + const isNotUrgent = shelterSupply.priority !== SupplyPriority.Urgent; return isNotUrgent && hasPassedTime; } From 6027b3eccf105bd1e6807ae65103406696df3ea7 Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Tue, 21 May 2024 08:53:31 -0300 Subject: [PATCH 07/10] =?UTF-8?q?refact:=20ajusta=20findMany=20do=20shelte?= =?UTF-8?q?rSupply=20aplicando=20condi=C3=A7=C3=B5es=20diretamente=20na=20?= =?UTF-8?q?busca=20com=20o=20Prisma,=20removendo=20func=C3=A7=C3=B5es=20au?= =?UTF-8?q?xiliares=20de=20valida=C3=A7=C3=A3o.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shelter-supply-cleanup.service.ts | 68 ++++++++----------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/shelter-supply/shelter-supply-cleanup.service.ts b/src/shelter-supply/shelter-supply-cleanup.service.ts index 056dc303..12ea020d 100644 --- a/src/shelter-supply/shelter-supply-cleanup.service.ts +++ b/src/shelter-supply/shelter-supply-cleanup.service.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { ShelterSupply } from '@prisma/client'; -import { differenceInHours, parseISO } from 'date-fns'; +import { subDays } from 'date-fns'; import { PrismaService } from 'src/prisma/prisma.service'; import { SupplyPriority } from 'src/supply/types'; @@ -12,47 +12,50 @@ export class ShelterSupplyCleanupService { @Cron('0 0 */2 * *') async handleCron() { - const shelterSupplies = await this.prismaService.shelterSupply.findMany({ - include: { - supply: true, - shelter: true, - }, - }); + const shelterSuppliesToDelete = await this.getShelterSuppliesToDelete(); - for (const shelterSupply of shelterSupplies) { + for (const shelterSupply of shelterSuppliesToDelete) { this.logger.log( - `Verificando necessidade de exclusão de itens no abrigo ${shelterSupply.shelter.name}`, + `Verificando necessidade de exclusão de itens no abrigo ${shelterSupply.shelterId}`, ); await this.removeShelterSupply(shelterSupply); } } - hasPassed48Hours( - createdAt: string | null, - updatedAt: string | null, - ): boolean { - const targetDate = updatedAt - ? parseISO(updatedAt) - : createdAt - ? parseISO(createdAt) - : null; - - if (!targetDate) { - return false; - } - - const hoursDifference = differenceInHours(new Date(), targetDate); + private async getShelterSuppliesToDelete(): Promise { + const thresholdDate = subDays(new Date(), 2).toISOString(); - return hoursDifference >= 48; + return this.prismaService.shelterSupply.findMany({ + where: { + OR: [ + { + createdAt: { + lte: thresholdDate, + }, + updatedAt: null, + }, + { + updatedAt: { + lte: thresholdDate, + }, + }, + ], + priority: { + not: SupplyPriority.Urgent, + }, + }, + }); } - private async removeShelterSupply(shelterSupply: ShelterSupply) { + private async removeShelterSupply( + shelterSupply: ShelterSupply, + ): Promise { + this.logger.log( `Suprimento ${shelterSupply.supplyId} já está há 48 horas com baixa movimentação e não é urgente. Removendo relação com o abrigo ${shelterSupply.shelterId}`, ); - if (!this.canRemoveShelterSupply(shelterSupply)) return; try { await this.prismaService.$transaction([ this.prismaService.shelterSupply.deleteMany({ @@ -84,15 +87,4 @@ export class ShelterSupplyCleanupService { ); } } - - private canRemoveShelterSupply(shelterSupply: ShelterSupply): boolean { - const hasPassedTime = this.hasPassed48Hours( - shelterSupply.createdAt, - shelterSupply.updatedAt, - ); - - const isNotUrgent = shelterSupply.priority !== SupplyPriority.Urgent; - - return isNotUrgent && hasPassedTime; - } } From 8a4983b5e45e1e1b2b4983f947d5f3eac00eea5c Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Tue, 21 May 2024 08:59:50 -0300 Subject: [PATCH 08/10] chore: adiciona await no findMany --- src/shelter-supply/shelter-supply-cleanup.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shelter-supply/shelter-supply-cleanup.service.ts b/src/shelter-supply/shelter-supply-cleanup.service.ts index 12ea020d..926c61ec 100644 --- a/src/shelter-supply/shelter-supply-cleanup.service.ts +++ b/src/shelter-supply/shelter-supply-cleanup.service.ts @@ -26,7 +26,7 @@ export class ShelterSupplyCleanupService { private async getShelterSuppliesToDelete(): Promise { const thresholdDate = subDays(new Date(), 2).toISOString(); - return this.prismaService.shelterSupply.findMany({ + return await this.prismaService.shelterSupply.findMany({ where: { OR: [ { From 7ff9df064fc033cd770b7e258b423e0452e6c3dc Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Tue, 21 May 2024 09:42:59 -0300 Subject: [PATCH 09/10] refact: ajustado coluna remove_at no model de supplyAutoRemoveLogs --- .../migration.sql | 10 ---------- .../migration.sql | 2 +- prisma/schema.prisma | 2 +- 3 files changed, 2 insertions(+), 12 deletions(-) delete mode 100644 prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql rename prisma/migrations/{20240520151114_create_supplu_auto_remove_logs => 20240521123041_create_supply_remove_log}/migration.sql (90%) diff --git a/prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql b/prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql deleted file mode 100644 index a39bfa27..00000000 --- a/prisma/migrations/20240520151515_update_auto_remove_logs/migration.sql +++ /dev/null @@ -1,10 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `created_at` on the `supply_auto_remove_logs` table. All the data in the column will be lost. - - Added the required column `removed_at` to the `supply_auto_remove_logs` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "supply_auto_remove_logs" DROP COLUMN "created_at", -ADD COLUMN "removed_at" VARCHAR(32) NOT NULL; diff --git a/prisma/migrations/20240520151114_create_supplu_auto_remove_logs/migration.sql b/prisma/migrations/20240521123041_create_supply_remove_log/migration.sql similarity index 90% rename from prisma/migrations/20240520151114_create_supplu_auto_remove_logs/migration.sql rename to prisma/migrations/20240521123041_create_supply_remove_log/migration.sql index 77611eb9..c278aa68 100644 --- a/prisma/migrations/20240520151114_create_supplu_auto_remove_logs/migration.sql +++ b/prisma/migrations/20240521123041_create_supply_remove_log/migration.sql @@ -3,7 +3,7 @@ CREATE TABLE "supply_auto_remove_logs" ( "id" TEXT NOT NULL, "supply_id" TEXT NOT NULL, "shelter_id" TEXT NOT NULL, - "created_at" VARCHAR(32) NOT NULL, + "removed_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "supply_auto_remove_logs_pkey" PRIMARY KEY ("id") ); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cbdf8c70..5f2dc82f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -159,7 +159,7 @@ model SupplyAutoRemoveLog { id String @id @default(uuid()) supplyId String @map("supply_id") shelterId String @map("shelter_id") - removedAt String @map("removed_at") @db.VarChar(32) + removedAt DateTime @default(value: now()) @map("removed_at") @db.Timestamp(6) From 44e00f6787468c451c5e17740f3fa6f654bfa0d1 Mon Sep 17 00:00:00 2001 From: Luiz Bello Date: Tue, 21 May 2024 09:43:31 -0300 Subject: [PATCH 10/10] =?UTF-8?q?refact:=20remove=20cria=C3=A7=C3=A3o=20?= =?UTF-8?q?=20do=20removed=5Fat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shelter-supply/shelter-supply-cleanup.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/shelter-supply/shelter-supply-cleanup.service.ts b/src/shelter-supply/shelter-supply-cleanup.service.ts index 926c61ec..419beacc 100644 --- a/src/shelter-supply/shelter-supply-cleanup.service.ts +++ b/src/shelter-supply/shelter-supply-cleanup.service.ts @@ -51,7 +51,6 @@ export class ShelterSupplyCleanupService { private async removeShelterSupply( shelterSupply: ShelterSupply, ): Promise { - this.logger.log( `Suprimento ${shelterSupply.supplyId} já está há 48 horas com baixa movimentação e não é urgente. Removendo relação com o abrigo ${shelterSupply.shelterId}`, ); @@ -76,7 +75,6 @@ export class ShelterSupplyCleanupService { id: shelterSupply.shelterId, }, }, - removedAt: new Date().toISOString(), }, }), ]);