From f2a5dc4ca371b8080e1d3ae651e92c6218e1d560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoni=20Nu=C3=B1ez=20Romeu?= Date: Mon, 13 Jul 2026 15:18:20 +0200 Subject: [PATCH] feat(backend): add CIMA OTC search function with Redis caching --- apps/backend/cima-service.js | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/apps/backend/cima-service.js b/apps/backend/cima-service.js index ad185fd..76364ac 100644 --- a/apps/backend/cima-service.js +++ b/apps/backend/cima-service.js @@ -231,6 +231,86 @@ export async function getMedicineDetails(nregistro) { } } +/** + * Busca medicamentos OTC (Sin Receta) en la API de CIMA con caché de Redis + * @param {string} query - Término de búsqueda + * @returns {Promise} - Lista de medicamentos OTC encontrados + */ +export async function searchOTC(query) { + if (!query || query.trim().length < 2) { + return []; + } + + const searchTerm = query.trim().toLowerCase(); + const cacheKey = `cima:otc:${searchTerm}`; + + try { + // Intentar obtener del caché + const cachedData = await redisClient.get(cacheKey); + + if (cachedData) { + console.log(`📦 Cache hit for OTC search: ${searchTerm}`); + return JSON.parse(cachedData); + } + + const parsed = parseSearchQuery(searchTerm); + const apiSearchTerm = parsed.nameQuery || searchTerm; + + console.log(`🌐 Fetching OTC from CIMA API: ${apiSearchTerm}`); + const response = await axios.get(`${CIMA_API_BASE_URL}/medicamentos`, { + params: { + nombre: apiSearchTerm, + cpresc: 'Sin Receta' + }, + timeout: 5000 + }); + + if (response.data && response.data.resultados) { + // Transformar los datos de CIMA al modelo unificado Product + const medicines = response.data.resultados.map(med => ({ + id: med.nregistro, + source: 'cima', + name: med.nombre, + brand: med.labtitular || '', + category: 'otc', + image_url: med.fotos?.[0]?.url || null, + active_ingredient: med.vtm?.nombre || null, + dosage: med.dosis || null, + form: med.formaFarmaceutica?.nombre || null, + prescription: med.cpresc, + commercialized: med.comerc, + photos: med.fotos || [], + docs: med.docs || [] + })); + + const filtered = filterMedicinesByFullQuery(medicines, searchTerm); + + // Guardar en caché (1h TTL) + await redisClient.setEx(cacheKey, CACHE_TTL, JSON.stringify(filtered)); + + console.log(`✅ Cached ${filtered.length} OTC medicines for: ${searchTerm}`); + return filtered; + } + + return []; + } catch (error) { + console.error('Error searching OTC medicines from CIMA:', error.message); + + // Si falla, intentar devolver datos cacheados aunque hayan expirado + try { + const staleData = await redisClient.get(cacheKey); + if (staleData) { + console.log('⚠️ Returning stale OTC cache data due to API error'); + return JSON.parse(staleData); + } + } catch (cacheError) { + console.error('OTC cache fallback also failed:', cacheError); + } + + return []; + } +} + /** * Limpia el caché de búsquedas (útil para testing o mantenimiento) * @param {string} pattern - Patrón de claves a eliminar (ej: 'medicines:search:*')