whatsapp-crm-common
Version:
Componentes compartidos para servicios de WhatsApp CRM - Common utilities and types for WhatsApp CRM system
248 lines • 11.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContactRepository = void 0;
const base_repository_1 = __importDefault(require("./base-repository"));
class ContactRepository extends base_repository_1.default {
/**
* Guarda múltiples contactos en la base de datos en lotes
*/
async guardarContactosBulk(client, contactos, tenantId, agentId) {
if (contactos.length === 0)
return;
// Número máximo de contactos por lote (cada contacto usa 7 parámetros incluyendo tenant_id y agent_id)
const maxContactosPerBatch = 140; // ~1000 parámetros máximo
// Procesar en lotes para evitar límites de parámetros en PostgreSQL
for (let i = 0; i < contactos.length; i += maxContactosPerBatch) {
const loteContactos = contactos.slice(i, i + maxContactosPerBatch);
let valuesSql = [];
let paramCounter = 1;
let params = [];
for (const contacto of loteContactos) {
if (!contacto.id)
continue;
valuesSql.push(`($${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++})`);
params.push(contacto.id, tenantId, agentId, contacto.name || null, contacto.notify || null, contacto.verifiedName || null, contacto.id?.split("@")[0] || null);
}
if (valuesSql.length === 0)
continue;
const query = `
INSERT INTO contacts (id, tenant_id, agent_id, name, notify, verified_name, phone_number)
VALUES ${valuesSql.join(", ")}
ON CONFLICT (id, tenant_id, agent_id) DO UPDATE
SET name = COALESCE(EXCLUDED.name, contacts.name),
notify = COALESCE(EXCLUDED.notify, contacts.notify),
verified_name = COALESCE(EXCLUDED.verified_name, contacts.verified_name),
phone_number = COALESCE(EXCLUDED.phone_number, contacts.phone_number),
last_updated = CURRENT_TIMESTAMP
`;
console.log(`Insertando lote de ${loteContactos.length} contactos (${params.length} parámetros)`);
await client.query(query, params);
}
}
/**
* Maneja relaciones entre chats y contactos
*/
async guardarChatContactRelationsBulk(client, relations, tenantId, agentId) {
if (relations.length === 0)
return;
// 1. Verificar qué contactos necesitamos y cuáles ya existen en la BD
const contactIds = [...new Set(relations.map((r) => r.contactId))];
const checkQuery = `SELECT id FROM contacts WHERE id = ANY($1::text[]) AND tenant_id = $2 AND agent_id = $3`;
const existingContactsResult = await client.query(checkQuery, [
contactIds,
tenantId,
agentId,
]);
const existingContactIds = new Set(existingContactsResult.rows.map((row) => row.id));
// 2. Identificar contactos faltantes
const missingContactIds = contactIds.filter((id) => !existingContactIds.has(id));
// 3. Crear contactos faltantes si es necesario
if (missingContactIds.length > 0) {
console.log(`ADVERTENCIA: Se detectaron ${missingContactIds.length} contactos faltantes al crear relaciones chat-contacto`);
console.log(`Contactos faltantes: ${missingContactIds.join(", ")}`);
// Crear contactos placeholder para estos IDs faltantes
const placeholderContacts = missingContactIds.map((id) => ({
id,
name: id.split("@")[0],
notify: id.split("@")[0],
verifiedName: undefined,
imgUrl: undefined,
status: undefined,
}));
await this.guardarContactosBulk(client, placeholderContacts, tenantId, agentId);
}
// 4. Proceder con la inserción de relaciones ahora que todos los contactos existen
let valuesSql = [];
let paramCounter = 1;
let params = [];
for (const relation of relations) {
valuesSql.push(`($${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++})`);
params.push(relation.chatId, tenantId, agentId, relation.contactId);
}
const query = `
INSERT INTO chat_contact_relation (chat_id, tenant_id, agent_id, contact_id)
VALUES ${valuesSql.join(", ")}
ON CONFLICT (chat_id, contact_id, tenant_id, agent_id) DO NOTHING
`;
await client.query(query, params);
}
/**
* Maneja participantes de grupos
*/
async guardarGroupParticipantsBulk(client, participants, tenantId, agentId) {
if (participants.length === 0)
return;
// Verificar qué contactos necesitamos y cuáles ya existen en la BD
const contactIds = [...new Set(participants.map((p) => p.contactId))];
const checkQuery = `SELECT id FROM contacts WHERE id = ANY($1::text[]) AND tenant_id = $2 AND agent_id = $3`;
const existingContactsResult = await client.query(checkQuery, [
contactIds,
tenantId,
agentId,
]);
const existingContactIds = new Set(existingContactsResult.rows.map((row) => row.id));
// Identificar contactos faltantes
const missingContactIds = contactIds.filter((id) => !existingContactIds.has(id));
// Crear contactos faltantes si es necesario
if (missingContactIds.length > 0) {
console.log(`ADVERTENCIA: Se detectaron ${missingContactIds.length} contactos faltantes al crear participantes de grupo`);
// Crear contactos placeholder para estos IDs faltantes
const placeholderContacts = missingContactIds.map((id) => ({
id,
name: id.split("@")[0],
notify: id.split("@")[0],
verifiedName: undefined,
imgUrl: undefined,
status: undefined,
}));
await this.guardarContactosBulk(client, placeholderContacts, tenantId, agentId);
}
// Ahora proceder con la inserción de participantes
let valuesSql = [];
let paramCounter = 1;
let params = [];
for (const participant of participants) {
valuesSql.push(`($${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++})`);
params.push(participant.groupId, tenantId, agentId, participant.contactId, false);
}
const query = `
INSERT INTO group_participants (group_id, tenant_id, agent_id, contact_id, is_admin)
VALUES ${valuesSql.join(", ")}
ON CONFLICT (group_id, contact_id, tenant_id, agent_id) DO NOTHING
`;
await client.query(query, params);
}
/**
* Crea contactos placeholder para IDs faltantes
*/
async createPlaceholderContacts(client, contactIds, tenantId, agentId) {
if (contactIds.length === 0)
return;
const placeholderContacts = contactIds.map((id) => ({
id,
name: id.split("@")[0],
notify: id.split("@")[0],
verifiedName: undefined,
imgUrl: undefined,
status: undefined,
}));
await this.guardarContactosBulk(client, placeholderContacts, tenantId, agentId);
}
/**
* Verifica si los contactos existen y crea placeholders para los faltantes
*/
async ensureContactsExist(client, contactIds, tenantId, agentId) {
if (contactIds.length === 0)
return;
const checkQuery = `SELECT id FROM contacts WHERE id = ANY($1::text[]) AND tenant_id = $2 AND agent_id = $3`;
const existingContactsResult = await client.query(checkQuery, [
contactIds,
tenantId,
agentId,
]);
const existingContactIds = new Set(existingContactsResult.rows.map((row) => row.id));
// Identificar contactos faltantes
const missingContactIds = contactIds.filter((id) => !existingContactIds.has(id));
if (missingContactIds.length > 0) {
await this.createPlaceholderContacts(client, missingContactIds, tenantId, agentId);
}
}
/**
* Maneja la inserción o actualización de contactos
*/
async handleContactUpserts(contacts, tenantId, agentId) {
if (contacts.length === 0)
return;
const client = await this.pool.connect();
try {
await client.query("BEGIN");
await this.guardarContactosBulk(client, contacts, tenantId, agentId);
await client.query("COMMIT");
}
catch (error) {
await client.query("ROLLBACK");
console.error("Error al procesar contactos:", error);
}
finally {
client.release();
}
}
/**
* Maneja actualizaciones de contactos
*/
async handleContactUpdates(tenantId, agentId, updates) {
if (updates.length === 0)
return;
const client = await this.pool.connect();
try {
await client.query("BEGIN");
for (const update of updates) {
if (!update.id)
continue;
const updateFields = {};
// Actualizar campos según lo que venga en el update
if (update.name !== undefined)
updateFields.name = update.name;
if (update.notify !== undefined)
updateFields.notify = update.notify;
if (update.verifiedName !== undefined)
updateFields.verified_name = update.verifiedName;
if (update.imgUrl !== undefined)
updateFields.img_url = update.imgUrl;
if (update.status !== undefined)
updateFields.status = update.status;
if (Object.keys(updateFields).length === 0)
continue;
// Construir la consulta de actualización
const setClause = Object.keys(updateFields)
.map((key, index) => `${key} = $${index + 4}`)
.join(", ");
const values = [
update.id,
tenantId,
agentId,
...Object.values(updateFields),
];
const updateQuery = `
UPDATE public.contacts
SET ${setClause}, last_updated = CURRENT_TIMESTAMP
WHERE id = $1 AND tenant_id = $2 AND agent_id = $3
`;
await client.query(updateQuery, values);
}
await client.query("COMMIT");
}
catch (error) {
await client.query("ROLLBACK");
console.error("Error al actualizar contactos:", error);
}
finally {
client.release();
}
}
}
exports.ContactRepository = ContactRepository;
//# sourceMappingURL=contact-repository.js.map