Actualizar server.js
This commit is contained in:
113
server.js
113
server.js
@@ -904,6 +904,119 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 🌐 EMBUDO PÚBLICO DE ENTRADA DE CLIENTES
|
||||
// ==========================================
|
||||
app.post("/public/new-request", async (req, res) => {
|
||||
const clientDb = await pool.connect();
|
||||
try {
|
||||
const { phone, name, address, guild_id, description, is_urgent, owner_id } = req.body;
|
||||
|
||||
if (!phone || !guild_id) return res.status(400).json({ ok: false, error: "Faltan datos clave" });
|
||||
|
||||
// Por ahora, como es público, asumiremos que entra a la instancia principal (owner_id = 1),
|
||||
// a menos que uses subdominios por empresa. Lo forzamos a 1 para este ejemplo.
|
||||
const targetOwnerId = owner_id || 1;
|
||||
const cleanPhone = phone.replace(/\D/g, "");
|
||||
if (cleanPhone.length < 9) return res.status(400).json({ ok: false, error: "Teléfono inválido" });
|
||||
|
||||
await clientDb.query('BEGIN');
|
||||
|
||||
// 1. BUSCAR O CREAR CLIENTE
|
||||
let clientId, clientToken;
|
||||
const qClient = await clientDb.query("SELECT id, portal_token FROM clients WHERE phone LIKE $1 AND owner_id = $2 LIMIT 1", [`%${cleanPhone}%`, targetOwnerId]);
|
||||
|
||||
if (qClient.rowCount > 0) {
|
||||
clientId = qClient.rows[0].id;
|
||||
clientToken = qClient.rows[0].portal_token;
|
||||
} else {
|
||||
clientToken = crypto.randomBytes(6).toString('hex');
|
||||
const newClient = await clientDb.query(
|
||||
"INSERT INTO clients (owner_id, full_name, phone, addresses, portal_token) VALUES ($1, $2, $3, $4, $5) RETURNING id",
|
||||
[targetOwnerId, name || "Cliente Web", phone, JSON.stringify([address || ""]), clientToken]
|
||||
);
|
||||
clientId = newClient.rows[0].id;
|
||||
}
|
||||
|
||||
// 2. BUSCAR OPERARIO AL AZAR (Si es NORMAL)
|
||||
let assignedWorkerId = null;
|
||||
if (!is_urgent) {
|
||||
// Buscamos un operario activo de ese gremio al azar
|
||||
const qWorker = await clientDb.query(`
|
||||
SELECT u.id FROM users u
|
||||
JOIN user_guilds ug ON u.id = ug.user_id
|
||||
WHERE u.owner_id = $1 AND u.role = 'operario' AND u.status = 'active' AND ug.guild_id = $2
|
||||
ORDER BY RANDOM() LIMIT 1
|
||||
`, [targetOwnerId, guild_id]);
|
||||
|
||||
if (qWorker.rowCount > 0) {
|
||||
assignedWorkerId = qWorker.rows[0].id;
|
||||
} else {
|
||||
// Si no hay operarios para asignar calendario, lo forzamos a manual/bolsa
|
||||
return res.status(400).json({ ok: false, error: "No hay técnicos disponibles para cita normal en este gremio. Llama a la oficina." });
|
||||
}
|
||||
}
|
||||
|
||||
// 3. CREAR EL SERVICIO
|
||||
const rawData = {
|
||||
"Nombre Cliente": name || "Cliente Web",
|
||||
"Teléfono": phone,
|
||||
"Dirección": address || "",
|
||||
"Descripción": description || "Aviso desde Web",
|
||||
"guild_id": guild_id,
|
||||
"Compañía": "Particular",
|
||||
"Origen": "Web Público"
|
||||
};
|
||||
|
||||
const ref = `WEB-${Date.now().toString().slice(-6)}`;
|
||||
|
||||
const qSvc = await clientDb.query(`
|
||||
INSERT INTO scraped_services (owner_id, provider, service_ref, status, automation_status, assigned_to, is_urgent, raw_data)
|
||||
VALUES ($1, 'particular', $2, 'pending', $3, $4, $5, $6) RETURNING id
|
||||
`, [
|
||||
targetOwnerId,
|
||||
ref,
|
||||
is_urgent ? 'in_progress' : 'manual', // Si es urgente, va a la bolsa
|
||||
assignedWorkerId, // Solo tendrá id si NO es urgente
|
||||
is_urgent,
|
||||
JSON.stringify(rawData)
|
||||
]);
|
||||
|
||||
const newServiceId = qSvc.rows[0].id;
|
||||
await clientDb.query('COMMIT');
|
||||
|
||||
// 4. BIFURCACIÓN DE RUTAS
|
||||
if (is_urgent) {
|
||||
// Mandamos a la cola automáticamente para que suene a los técnicos
|
||||
// Lo ejecutamos en segundo plano llamando a tu propio endpoint
|
||||
const port = process.env.PORT || 3000;
|
||||
fetch(`http://127.0.0.1:${port}/providers/automate/${newServiceId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ADMIN_TOKEN || 'hack_interno'}` }, // Necesitarás un token interno para esto
|
||||
body: JSON.stringify({ guild_id, cp: "00000", useDelay: false })
|
||||
}).catch(e => console.error("Error lanzando bolsa urgente:", e));
|
||||
|
||||
res.json({ ok: true, action: 'queued', message: "Aviso urgente en cola." });
|
||||
} else {
|
||||
// Devolvemos el token del cliente y el ID del servicio para redirigirlo al calendario
|
||||
res.json({
|
||||
ok: true,
|
||||
action: 'calendar',
|
||||
redirectUrl: `https://portal.integrarepara.es/?token=${clientToken}&service=${newServiceId}`
|
||||
});
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
await clientDb.query('ROLLBACK');
|
||||
console.error("Error en Web Pública:", e);
|
||||
res.status(500).json({ ok: false, error: "Error procesando solicitud" });
|
||||
} finally {
|
||||
clientDb.release();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// ==========================================
|
||||
// 🔗 PORTAL PÚBLICO DEL CLIENTE
|
||||
// ==========================================
|
||||
|
||||
Reference in New Issue
Block a user