2 次代碼提交 5f66a0f768 ... 6bbd3700c2

作者 SHA1 備註 提交日期
  GabrielRamison 6bbd3700c2 Merge branch 'main' of https://git.tutech.com.br/star_oraculo/backend 2 月之前
  GabrielRamison 04940949b4 ajustes filtros avaliacao 2 月之前
共有 3 個文件被更改,包括 76 次插入11 次删除
  1. 26 2
      src/controllers/Atendimentos.Controller.js
  2. 28 3
      src/services/atendimentoAvaliacaoService.js
  3. 22 6
      src/utils/atendimentoFormat.js

+ 26 - 2
src/controllers/Atendimentos.Controller.js

@@ -13,6 +13,7 @@ import {
   listarAvaliacoes,
   estatisticasAvaliacoes
 } from "../services/atendimentoAvaliacaoService.js";
+import { estruturarMensagem } from "../utils/atendimentoFormat.js";
 
 export const AtendimentosController = {
   Ingerir: async function (req, res, next) {
@@ -77,7 +78,28 @@ export const AtendimentosController = {
     try {
       const atendimento = await obterAtendimento(req.params.id);
       if (!atendimento) throw new NotFoundError("atendimento_not_found");
-      res.json(atendimento);
+
+      // o Body cru vem com HTML do ifbot (nome do atendente embutido em "<b>Nome:</b>");
+      // entrega já estruturado para a tela de conversa não repetir esse parse
+      const mensagens = (atendimento.mensagens ?? [])
+        .map((m) => {
+          const { papel, autor, texto } = estruturarMensagem(m);
+          if (!texto) return null;
+          const transcricao = typeof m.Transcricao === "string" && m.Transcricao.trim() ? m.Transcricao.trim() : null;
+          return {
+            Id: m.Id,
+            Papel: papel,
+            Autor: autor,
+            Texto: texto,
+            Transcricao: transcricao,
+            Timestamp: m.Timestamp,
+            Tipodemidia: m.Tipodemidia,
+            Midia: m.Midia
+          };
+        })
+        .filter(Boolean);
+
+      res.json({ ...atendimento.toJSON(), mensagens });
     } catch (err) {
       next(err);
     }
@@ -109,6 +131,7 @@ export const AtendimentosController = {
       const resolvido = typeof req.query.resolvido === "string" && req.query.resolvido.trim() ? req.query.resolvido.trim() : undefined;
       const sentimento = typeof req.query.sentimento === "string" && req.query.sentimento.trim() ? req.query.sentimento.trim() : undefined;
       const scoreMax = req.query.scoreMax !== undefined ? Number(req.query.scoreMax) : undefined;
+      const busca = typeof req.query.busca === "string" && req.query.busca.trim() ? req.query.busca.trim() : undefined;
 
       const result = await listarAvaliacoes({
         page,
@@ -116,7 +139,8 @@ export const AtendimentosController = {
         setor,
         resolvido,
         sentimento,
-        scoreMax: Number.isFinite(scoreMax) ? scoreMax : undefined
+        scoreMax: Number.isFinite(scoreMax) ? scoreMax : undefined,
+        busca
       });
       res.json(result);
     } catch (err) {

+ 28 - 3
src/services/atendimentoAvaliacaoService.js

@@ -348,7 +348,16 @@ export async function avaliarPendentes({ limite = config.avaliacao.batchSize } =
   }
 }
 
-export async function listarAvaliacoes({ page = 1, pageSize = 20, setor, resolvido, sentimento, scoreMax } = {}) {
+// busca digitada: "07/11/2024" ou "2024-11-07" vira filtro pela data de abertura
+function parseDataBusca(termo) {
+  let m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(termo);
+  if (m) return `${m[3]}-${m[2]}-${m[1]}`;
+  m = /^\d{4}-\d{2}-\d{2}$/.exec(termo);
+  if (m) return termo;
+  return null;
+}
+
+export async function listarAvaliacoes({ page = 1, pageSize = 20, setor, resolvido, sentimento, scoreMax, busca } = {}) {
   const query = AtendimentoAvaliacao.query()
     .where("Avaliavel", true)
     .page(page - 1, pageSize)
@@ -358,11 +367,27 @@ export async function listarAvaliacoes({ page = 1, pageSize = 20, setor, resolvi
   if (sentimento) query.where("Sentimento", sentimento);
   if (scoreMax !== undefined) query.where("ScoreAtendente", "<=", scoreMax);
 
-  if (setor) {
+  const termo = typeof busca === "string" ? busca.trim() : "";
+
+  if (setor || termo) {
     query
       .join("atendimentos as a", "a.Id", "atendimento_avaliacoes.AtendimentoId")
-      .where("a.Setor", setor)
       .select("atendimento_avaliacoes.*");
+
+    if (setor) query.where("a.Setor", setor);
+
+    if (termo) {
+      const like = `%${termo.replace(/[\\%_]/g, (ch) => `\\${ch}`)}%`;
+      const data = parseDataBusca(termo);
+
+      query.leftJoin("atendimento_clientes as c", "c.Id", "a.ClienteId").where((qb) => {
+        qb.where("a.Codigo", "like", like)
+          .orWhere("c.Nome", "like", like)
+          // Atendentes é JSON text (["Luiz","Carla"]) — LIKE cobre busca por nome
+          .orWhere("atendimento_avaliacoes.Atendentes", "like", like);
+        if (data) qb.orWhereRaw("DATE(a.Abertura) = ?", [data]);
+      });
+    }
   }
 
   const { results, total } = await query;

+ 22 - 6
src/utils/atendimentoFormat.js

@@ -47,24 +47,26 @@ export function formatDateTime(value) {
   }).format(d);
 }
 
-export function formatMensagem(m, clienteNome) {
-  let label;
+export function estruturarMensagem(m) {
+  let papel;
+  let autor = null;
   let texto;
 
   const transcricao = typeof m.Transcricao === "string" ? m.Transcricao.trim() : "";
 
   if (m.Resposta === 1) {
     const parsed = parseAtendenteBody(m.Body);
-    label = parsed.nome ? `Atendente (${parsed.nome})` : "Atendente";
+    papel = "atendente";
+    autor = parsed.nome;
     texto = parsed.texto;
   } else if (m.Resposta === 0) {
-    label = clienteNome ? `Cliente (${clienteNome})` : "Cliente";
+    papel = "cliente";
     texto = stripHtml(m.Body);
   } else if (m.Resposta === 9) {
-    label = "Sistema";
+    papel = "sistema";
     texto = stripHtml(m.Body);
   } else {
-    label = "Desconhecido";
+    papel = "desconhecido";
     texto = stripHtml(m.Body);
   }
 
@@ -75,8 +77,22 @@ export function formatMensagem(m, clienteNome) {
   } else if (!texto && m.Tipodemidia && m.Tipodemidia !== "text") {
     texto = `[mídia: ${m.Tipodemidia}]`;
   }
+
+  return { papel, autor, texto };
+}
+
+export function formatMensagem(m, clienteNome) {
+  const { papel, autor, texto } = estruturarMensagem(m);
   if (!texto) return null;
 
+  const labels = {
+    atendente: autor ? `Atendente (${autor})` : "Atendente",
+    cliente: clienteNome ? `Cliente (${clienteNome})` : "Cliente",
+    sistema: "Sistema",
+    desconhecido: "Desconhecido"
+  };
+  const label = labels[papel];
+
   const timestamp = formatDateTime(m.Timestamp);
   return timestamp ? `[${timestamp}] ${label}: ${texto}` : `${label}: ${texto}`;
 }