Atendimentos.Controller.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import { NotFoundError } from "../shared/errors/index.js";
  2. import {
  3. fetchAtendimentoJson,
  4. salvarAtendimento,
  5. sincronizarProtocolosAbertos,
  6. sincronizarTodosProtocolosAbertos,
  7. sincronizarAtendimentoPorId,
  8. listarAtendimentos,
  9. obterAtendimento
  10. } from "../services/atendimentosService.js";
  11. import {
  12. avaliarAtendimento,
  13. avaliarPendentes,
  14. listarAvaliacoes,
  15. estatisticasAvaliacoes,
  16. listarSetores,
  17. rankingAtendentes
  18. } from "../services/atendimentoAvaliacaoService.js";
  19. import { gerarRelatorioNaoResolvidosPdf } from "../services/relatorioNaoResolvidosPdfService.js";
  20. import { gerarRankingAtendentesPdf } from "../services/rankingAtendentesPdfService.js";
  21. import { listarGoldenSet, salvarGoldenLabel, aceitarGoldenLabelComoIa } from "../services/atendimentoGoldenLabelService.js";
  22. import { estruturarMensagem, montarUrlMidia } from "../utils/atendimentoFormat.js";
  23. import { parseFiltrosAvaliacoes } from "../utils/atendimentoAvaliacaoFiltros.js";
  24. import { config } from "../config/index.js";
  25. export const AtendimentosController = {
  26. Ingerir: async function (req, res, next) {
  27. try {
  28. const { atendimento } = req.body;
  29. const result = await salvarAtendimento({ payload: atendimento });
  30. res.json({ ok: true, ...result });
  31. } catch (err) {
  32. next(err);
  33. }
  34. },
  35. IngerirUrl: async function (req, res, next) {
  36. try {
  37. const { url } = req.body;
  38. const payload = await fetchAtendimentoJson(url);
  39. const result = await salvarAtendimento({ payload, sourceUrl: url });
  40. res.json({ ok: true, ...result });
  41. } catch (err) {
  42. next(err);
  43. }
  44. },
  45. Sincronizar: async function (req, res, next) {
  46. try {
  47. const result = await sincronizarProtocolosAbertos(req.body);
  48. res.json({ ok: true, ...result });
  49. } catch (err) {
  50. next(err);
  51. }
  52. },
  53. SincronizarTudo: async function (req, res, next) {
  54. try {
  55. const result = await sincronizarTodosProtocolosAbertos(req.body);
  56. res.json({ ok: true, ...result });
  57. } catch (err) {
  58. next(err);
  59. }
  60. },
  61. Listar: async function (req, res, next) {
  62. try {
  63. const page = Math.max(1, Number(req.query.page) || 1);
  64. const pageSize = Math.min(100, Math.max(1, Number(req.query.pageSize) || 20));
  65. const setor = typeof req.query.setor === "string" && req.query.setor.trim() ? req.query.setor.trim() : undefined;
  66. const status = req.query.status !== undefined ? Number(req.query.status) : undefined;
  67. const result = await listarAtendimentos({
  68. page,
  69. pageSize,
  70. setor,
  71. status: Number.isInteger(status) ? status : undefined
  72. });
  73. res.json(result);
  74. } catch (err) {
  75. next(err);
  76. }
  77. },
  78. Obter: async function (req, res, next) {
  79. try {
  80. const atendimento = await obterAtendimento(req.params.id);
  81. if (!atendimento) throw new NotFoundError("atendimento_not_found");
  82. const mensagens = (atendimento.mensagens ?? [])
  83. .map((m) => {
  84. const { papel, autor, texto } = estruturarMensagem(m);
  85. if (!texto) return null;
  86. const transcricao = typeof m.Transcricao === "string" && m.Transcricao.trim() ? m.Transcricao.trim() : null;
  87. const transcricaoWhisper =
  88. typeof m.TranscricaoWhisper === "string" && m.TranscricaoWhisper.trim() ? m.TranscricaoWhisper.trim() : null;
  89. return {
  90. Id: m.Id,
  91. Papel: papel,
  92. Autor: autor,
  93. Texto: texto,
  94. Transcricao: transcricao,
  95. TranscricaoWhisper: transcricaoWhisper,
  96. Timestamp: m.Timestamp,
  97. Tipodemidia: m.Tipodemidia,
  98. Midia: m.Midia,
  99. MidiaUrl: montarUrlMidia(m, config.ifbot.mediaBaseUrl)
  100. };
  101. })
  102. .filter(Boolean);
  103. res.json({ ...atendimento.toJSON(), mensagens });
  104. } catch (err) {
  105. next(err);
  106. }
  107. },
  108. AvaliarPendentes: async function (req, res, next) {
  109. try {
  110. const result = await avaliarPendentes(req.body);
  111. res.json({ ok: true, ...result });
  112. } catch (err) {
  113. next(err);
  114. }
  115. },
  116. AvaliarUm: async function (req, res, next) {
  117. try {
  118. const result = await avaliarAtendimento(req.params.id);
  119. res.json({ ok: true, ...result });
  120. } catch (err) {
  121. next(err);
  122. }
  123. },
  124. SincronizarUm: async function (req, res, next) {
  125. try {
  126. const result = await sincronizarAtendimentoPorId(req.params.id);
  127. res.json({ ok: true, ...result });
  128. } catch (err) {
  129. next(err);
  130. }
  131. },
  132. ListarAvaliacoes: async function (req, res, next) {
  133. try {
  134. const page = Math.max(1, Number(req.query.page) || 1);
  135. const pageSize = Math.min(100, Math.max(1, Number(req.query.pageSize) || 20));
  136. const filtros = parseFiltrosAvaliacoes(req.query);
  137. const result = await listarAvaliacoes({ page, pageSize, ...filtros });
  138. res.json(result);
  139. } catch (err) {
  140. next(err);
  141. }
  142. },
  143. EstatisticasAvaliacoes: async function (req, res, next) {
  144. try {
  145. const filtros = parseFiltrosAvaliacoes(req.query);
  146. const result = await estatisticasAvaliacoes(filtros);
  147. res.json(result);
  148. } catch (err) {
  149. next(err);
  150. }
  151. },
  152. ListarSetoresAvaliacoes: async function (req, res, next) {
  153. try {
  154. const results = await listarSetores();
  155. res.json({ results });
  156. } catch (err) {
  157. next(err);
  158. }
  159. },
  160. RankingAtendentesAvaliacoes: async function (req, res, next) {
  161. try {
  162. const { setor, dataInicio, dataFim } = parseFiltrosAvaliacoes(req.query);
  163. const { resultados, metadados } = await rankingAtendentes({ setor, dataInicio, dataFim });
  164. res.json({ results: resultados, metadados });
  165. } catch (err) {
  166. next(err);
  167. }
  168. },
  169. GerarRelatorioRankingAtendentes: async function (req, res, next) {
  170. try {
  171. const { setor, dataInicio, dataFim } = parseFiltrosAvaliacoes(req.query);
  172. const { buffer } = await gerarRankingAtendentesPdf({ setor, dataInicio, dataFim });
  173. const dataArquivo = new Date().toISOString().slice(0, 10);
  174. res.setHeader("Content-Type", "application/pdf");
  175. res.setHeader("Content-Disposition", `attachment; filename="ranking-atendentes-${dataArquivo}.pdf"`);
  176. res.setHeader("Content-Length", buffer.length);
  177. res.send(buffer);
  178. } catch (err) {
  179. next(err);
  180. }
  181. },
  182. GerarRelatorioNaoResolvidos: async function (req, res, next) {
  183. try {
  184. const filtros = parseFiltrosAvaliacoes(req.query);
  185. const { buffer } = await gerarRelatorioNaoResolvidosPdf({ ...filtros, resolvido: "nao" });
  186. const dataArquivo = new Date().toISOString().slice(0, 10);
  187. res.setHeader("Content-Type", "application/pdf");
  188. res.setHeader("Content-Disposition", `attachment; filename="atendimentos-nao-resolvidos-${dataArquivo}.pdf"`);
  189. res.setHeader("Content-Length", buffer.length);
  190. res.send(buffer);
  191. } catch (err) {
  192. next(err);
  193. }
  194. },
  195. ListarGoldenSet: async function (req, res, next) {
  196. try {
  197. const status = typeof req.query.status === "string" && req.query.status.trim() ? req.query.status.trim() : undefined;
  198. const setor = typeof req.query.setor === "string" && req.query.setor.trim() ? req.query.setor.trim() : undefined;
  199. const results = await listarGoldenSet({ status, setor });
  200. res.json({ results });
  201. } catch (err) {
  202. next(err);
  203. }
  204. },
  205. SalvarGoldenLabel: async function (req, res, next) {
  206. try {
  207. const result = await salvarGoldenLabel(req.params.id, req.body);
  208. res.json(result);
  209. } catch (err) {
  210. next(err);
  211. }
  212. },
  213. AceitarGoldenLabel: async function (req, res, next) {
  214. try {
  215. const result = await aceitarGoldenLabelComoIa(req.params.id);
  216. res.json(result);
  217. } catch (err) {
  218. next(err);
  219. }
  220. }
  221. };
  222. export default AtendimentosController;