From 09687040777ab2ef42d785151dcde7a73ae70d79 Mon Sep 17 00:00:00 2001 From: Humberto Date: Sat, 2 May 2026 19:38:48 -0300 Subject: [PATCH 1/3] refactor(triage): Return category id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Altera o fluxo de triagem para retornar o id da categoria junto com o nome original vindo do banco, removendo a dependência do nome normalizado como identificador. --- .../useCases/create/create.usecase.ts | 2 +- .../triage/application/triage.service.ts | 19 ++++++++++-- .../modules/triage/domain/category.entity.ts | 1 + .../src/modules/triage/infra/nlp.provider.ts | 31 +++++++++++++------ .../src/modules/triage/infra/rules.engine.ts | 6 ++-- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/backend/src/modules/ticket/application/useCases/create/create.usecase.ts b/backend/src/modules/ticket/application/useCases/create/create.usecase.ts index 8592f6f..ba50b3c 100644 --- a/backend/src/modules/ticket/application/useCases/create/create.usecase.ts +++ b/backend/src/modules/ticket/application/useCases/create/create.usecase.ts @@ -36,7 +36,7 @@ export class CreateTicketUseCase { const ticket = Ticket.create({ ...input, - category: triageResult.category, + category: triageResult.categoryId, clientId: clientId, }); diff --git a/backend/src/modules/triage/application/triage.service.ts b/backend/src/modules/triage/application/triage.service.ts index 89cc6c2..6fc8a82 100644 --- a/backend/src/modules/triage/application/triage.service.ts +++ b/backend/src/modules/triage/application/triage.service.ts @@ -19,7 +19,12 @@ export class TriageService { const ruleMatch = await this.rulesEngine.match(description); if (ruleMatch) { - return new Category(ruleMatch.category, ruleMatch.confidence, 'rule'); + return new Category( + ruleMatch.categoryId, + ruleMatch.category, + ruleMatch.confidence, + 'rule', + ); } const nlpResult = await this.nlp.classify(description); @@ -28,13 +33,21 @@ export class TriageService { nlpResult.category && nlpResult.confidence >= this.CONFIDENCE_THRESHOLD ) { - return new Category(nlpResult.category, nlpResult.confidence, 'nlp'); + const fallbackCategory = await this.categoryService.findByName('OTHER'); + + return new Category( + nlpResult.categoryId ?? fallbackCategory.id, + nlpResult.category, + nlpResult.confidence, + 'nlp', + ); } const fallbackCategory = await this.categoryService.findByName('OTHER'); return new Category( - normalizeIntent(fallbackCategory.name), + fallbackCategory.id, + fallbackCategory.name, 0.5, 'fallback', ); diff --git a/backend/src/modules/triage/domain/category.entity.ts b/backend/src/modules/triage/domain/category.entity.ts index 5d1000a..b9dd6fe 100644 --- a/backend/src/modules/triage/domain/category.entity.ts +++ b/backend/src/modules/triage/domain/category.entity.ts @@ -1,5 +1,6 @@ export class Category { constructor( + public categoryId: string, public category: string, public confidence: number, public source: 'rule' | 'nlp' | 'fallback', diff --git a/backend/src/modules/triage/infra/nlp.provider.ts b/backend/src/modules/triage/infra/nlp.provider.ts index 76e29ba..b01e491 100644 --- a/backend/src/modules/triage/infra/nlp.provider.ts +++ b/backend/src/modules/triage/infra/nlp.provider.ts @@ -5,14 +5,13 @@ import { normalizeIntent } from '../../shared/utils/intent-normalizer'; @Injectable() export class NlpProvider implements OnModuleInit { - constructor(private readonly categoryService: CategoryService) {} + constructor(private readonly categoryService: CategoryService) { } private manager: NlpManager; async onModuleInit() { this.manager = new NlpManager({ languages: ['pt'] }); await this.addTrainingData(); - await this.manager.train(); } @@ -29,15 +28,29 @@ export class NlpProvider implements OnModuleInit { } async classify(text: string) { - const normalized = this.normalize(text); + const normalized = this.normalize(text); + const result = await this.manager.process('pt', normalized); + + if (result.intent === 'None') { + return { + categoryId: null, + category: null, + confidence: result.score, + }; + } + + const categories = await this.categoryService.findAll(); - const result = await this.manager.process('pt', normalized); + const found = categories.find( + (cat) => normalizeIntent(cat.name) === result.intent, + ); - return { - category: result.intent === 'None' ? null : result.intent, - confidence: result.score, - }; -} + return { + categoryId: found?.id || null, + category: found?.name || null, + confidence: result.score, + }; + } private normalize(text: string): string { return text.toLowerCase().replace(/\n/g, ' ').slice(0, 500); diff --git a/backend/src/modules/triage/infra/rules.engine.ts b/backend/src/modules/triage/infra/rules.engine.ts index c0f1d8d..5825a64 100644 --- a/backend/src/modules/triage/infra/rules.engine.ts +++ b/backend/src/modules/triage/infra/rules.engine.ts @@ -1,6 +1,5 @@ import { Injectable } from "@nestjs/common"; import { CategoryService } from "../../category/category.service"; -import { normalizeIntent } from '../../shared/utils/intent-normalizer'; @Injectable() export class RulesEngine { @@ -9,13 +8,14 @@ export class RulesEngine { async match(text: string) { const categories = await this.categoryService.findAll(); - const matches: { category: string; confidence: number }[] = []; + const matches: { categoryId: string; category: string; confidence: number }[] = []; for (const category of categories) { for (const keyword of category.keywords || []) { if (text.toLowerCase().includes(keyword.toLowerCase())) { matches.push({ - category: normalizeIntent(category.name), + categoryId: category.id, + category: category.name, confidence: 0.95, }); break; From 202a0923fa81e49d738ec2fa23860f26c9d4b8f3 Mon Sep 17 00:00:00 2001 From: Humberto Date: Sat, 2 May 2026 19:50:50 -0300 Subject: [PATCH 2/3] test(triage): Atualiza specs para nova Category Atualiza os testes do TriageController para refletir a nova assinatura da entidade Category, que agora inclui categoryId. --- .../src/modules/triage/presentation/triage.controller.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/modules/triage/presentation/triage.controller.spec.ts b/backend/src/modules/triage/presentation/triage.controller.spec.ts index dd6f335..cdeb197 100644 --- a/backend/src/modules/triage/presentation/triage.controller.spec.ts +++ b/backend/src/modules/triage/presentation/triage.controller.spec.ts @@ -42,6 +42,7 @@ describe('TriageController', () => { const mockResponse = new Category( + '1', 'WEB_APP', 0.95, 'rule' @@ -78,6 +79,7 @@ describe('TriageController', () => { const mockResponse = new Category( + '999', 'OTHER', 0.5, 'fallback' @@ -113,6 +115,7 @@ describe('TriageController', () => { triageService.classify .mockResolvedValue( new Category( + '2', 'BI', 0.9, 'rule' From d7b04e1b69fa450fbdfbb0e6f345c7b46fa136c6 Mon Sep 17 00:00:00 2001 From: Humberto Date: Sun, 3 May 2026 12:56:33 -0300 Subject: [PATCH 3/3] fix(chat): Usar JWT do SharedModule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrige erro de token inválido no chat ao remover secret hardcoded e utilizar a configuração centralizada do JWT. --- backend/src/modules/chat/chat.module.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/modules/chat/chat.module.ts b/backend/src/modules/chat/chat.module.ts index e21bc66..2d48d8f 100644 --- a/backend/src/modules/chat/chat.module.ts +++ b/backend/src/modules/chat/chat.module.ts @@ -1,6 +1,5 @@ import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; -import { JwtModule } from '@nestjs/jwt'; import { Chat, ChatSchema } from './infra/chat.schema'; import { ChatRepositoryMongodb } from './infra/chat.repository.mongodb'; import { ChatService } from './application/chat.service'; @@ -10,6 +9,7 @@ import { MessageRepositoryMongodb } from '../Messages/infra/message.repository.m import { ChatController } from './presentation/chat.controller'; import { TicketSchemaClass, TicketSchema } from '../ticket/infra/schemas/ticket.mongo.schema'; import { User, UserSchema } from '../user/user.schema'; +import { SharedModule } from '../shared/shared.module'; @Module({ imports: [ @@ -19,7 +19,7 @@ import { User, UserSchema } from '../user/user.schema'; { name: TicketSchemaClass.name, schema: TicketSchema }, { name: User.name, schema: UserSchema }, ]), - JwtModule.register({ secret: 'secret' }), + SharedModule, ], controllers: [ChatController], providers: [