168 lines
6.9 KiB
JavaScript
168 lines
6.9 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ChatRepository = void 0;
|
|
const prisma_1 = require("../../infra/database/prisma");
|
|
class ChatRepository {
|
|
async findAllByInstance(tenantId, instanceId, opts = {}) {
|
|
const { limit = 200, search, archived = false } = opts;
|
|
// ── Q1: chats (sem includes — bate o N+1) ────────────────────────────────
|
|
const chats = await prisma_1.prisma.chat.findMany({
|
|
where: {
|
|
tenantId,
|
|
instanceId,
|
|
isArchived: archived,
|
|
NOT: { OR: [
|
|
{ jid: { endsWith: '@lid' } },
|
|
{ jid: { contains: '@broadcast' } },
|
|
{ jid: { endsWith: '@newsletter' } },
|
|
] },
|
|
...(search
|
|
? {
|
|
OR: [
|
|
{ jid: { contains: search, mode: 'insensitive' } },
|
|
{ contact: { name: { contains: search, mode: 'insensitive' } } },
|
|
{ contact: { notify: { contains: search, mode: 'insensitive' } } },
|
|
{ contact: { phone: { contains: search, mode: 'insensitive' } } },
|
|
],
|
|
}
|
|
: {}),
|
|
},
|
|
orderBy: [
|
|
{ isPinned: 'desc' },
|
|
{ lastMessageAt: { sort: 'desc', nulls: 'last' } },
|
|
{ createdAt: 'desc' },
|
|
],
|
|
take: limit,
|
|
});
|
|
if (chats.length === 0) {
|
|
return [];
|
|
}
|
|
const chatIds = chats.map((c) => c.id);
|
|
const contactIds = Array.from(new Set(chats.map((c) => c.contactId).filter((x) => !!x)));
|
|
// ── Q2/Q3/Q4 em paralelo: contatos, protocols (1 por chat), last msg (1 por chat) ──
|
|
const [contacts, protocols, lastMsgs] = await Promise.all([
|
|
contactIds.length > 0
|
|
? prisma_1.prisma.contact.findMany({
|
|
where: { id: { in: contactIds } },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
notify: true,
|
|
verifiedName: true,
|
|
phone: true,
|
|
avatarUrl: true,
|
|
scoreReputacao: true,
|
|
flagRestricao: true,
|
|
},
|
|
})
|
|
: Promise.resolve([]),
|
|
// DISTINCT ON: 1 query batch — pega o protocolo aberto mais recente por chat
|
|
prisma_1.prisma.$queryRaw `
|
|
SELECT DISTINCT ON ("chatId") "chatId", number, status::text AS status
|
|
FROM protocols
|
|
WHERE "chatId" = ANY(${chatIds}::text[])
|
|
AND status IN ('OPEN','IN_PROGRESS','WAITING_CLIENT','WAITING_AGENT')
|
|
ORDER BY "chatId", "createdAt" DESC
|
|
`,
|
|
// DISTINCT ON: 1 query batch — pega a última msg por chat
|
|
prisma_1.prisma.$queryRaw `
|
|
SELECT DISTINCT ON ("chatId") "chatId", body, "fromMe", status::text AS status, timestamp, type::text AS type, "pushName"
|
|
FROM messages
|
|
WHERE "chatId" = ANY(${chatIds}::text[])
|
|
ORDER BY "chatId", timestamp DESC
|
|
`,
|
|
]);
|
|
// ── Indexa para lookup O(1) ──────────────────────────────────────────────
|
|
const contactById = new Map(contacts.map((c) => [c.id, c]));
|
|
const protocolByChat = new Map(protocols.map((p) => [p.chatId, p]));
|
|
const lastMsgByChat = new Map(lastMsgs.map((m) => [m.chatId, m]));
|
|
// ── Re-monta a estrutura no formato esperado pelo chat.routes.ts ─────────
|
|
return chats.map((c) => {
|
|
const contact = c.contactId ? contactById.get(c.contactId) ?? null : null;
|
|
const protocol = protocolByChat.get(c.id);
|
|
const lastMsg = lastMsgByChat.get(c.id);
|
|
return {
|
|
...c,
|
|
contact: contact
|
|
? {
|
|
name: contact.name,
|
|
notify: contact.notify,
|
|
verifiedName: contact.verifiedName,
|
|
phone: contact.phone,
|
|
avatarUrl: contact.avatarUrl,
|
|
scoreReputacao: contact.scoreReputacao,
|
|
flagRestricao: contact.flagRestricao,
|
|
}
|
|
: null,
|
|
protocols: protocol
|
|
? [{ number: protocol.number, status: protocol.status }]
|
|
: [],
|
|
messages: lastMsg
|
|
? [{
|
|
body: lastMsg.body,
|
|
fromMe: lastMsg.fromMe,
|
|
status: lastMsg.status,
|
|
timestamp: lastMsg.timestamp,
|
|
type: lastMsg.type,
|
|
pushName: lastMsg.pushName,
|
|
}]
|
|
: [],
|
|
};
|
|
});
|
|
}
|
|
async findById(id, tenantId) {
|
|
return prisma_1.prisma.chat.findFirst({
|
|
where: { id, tenantId },
|
|
include: {
|
|
contact: true,
|
|
protocols: {
|
|
where: { status: { in: ['OPEN', 'IN_PROGRESS', 'WAITING_CLIENT', 'WAITING_AGENT'] } },
|
|
take: 1,
|
|
orderBy: { createdAt: 'desc' },
|
|
},
|
|
},
|
|
});
|
|
}
|
|
async findByJid(tenantId, instanceId, jid) {
|
|
return prisma_1.prisma.chat.findUnique({
|
|
where: { tenantId_instanceId_jid: { tenantId, instanceId, jid } },
|
|
include: { contact: true },
|
|
});
|
|
}
|
|
async clearUnread(id, tenantId) {
|
|
return prisma_1.prisma.chat.updateMany({
|
|
where: { id, tenantId },
|
|
data: { unreadCount: 0 },
|
|
});
|
|
}
|
|
async setArchived(id, tenantId, archived) {
|
|
return prisma_1.prisma.chat.updateMany({
|
|
where: { id, tenantId },
|
|
data: { isArchived: archived },
|
|
});
|
|
}
|
|
async setPinned(id, tenantId, pinned) {
|
|
return prisma_1.prisma.chat.updateMany({
|
|
where: { id, tenantId },
|
|
data: { isPinned: pinned },
|
|
});
|
|
}
|
|
async countUnreadByInstance(tenantId, instanceId) {
|
|
const result = await prisma_1.prisma.chat.aggregate({
|
|
where: {
|
|
tenantId,
|
|
instanceId,
|
|
isArchived: false,
|
|
NOT: { OR: [
|
|
{ jid: { endsWith: '@lid' } },
|
|
{ jid: { contains: '@broadcast' } },
|
|
{ jid: { endsWith: '@newsletter' } },
|
|
] }
|
|
},
|
|
_sum: { unreadCount: true },
|
|
});
|
|
return result._sum.unreadCount ?? 0;
|
|
}
|
|
}
|
|
exports.ChatRepository = ChatRepository;
|