From 5431fe118e7b93bb334ab12ff58fb8e02a8837c7 Mon Sep 17 00:00:00 2001 From: Surya Paolo Date: Mon, 31 Mar 2025 23:56:01 +0200 Subject: [PATCH] - Ricerca Titolo per nome o autore o ISBN o codice articolo --- src/server/controllers/articleController.js | 2 +- src/server/models/catalog.js | 97 +++++++++- src/server/models/myscheda.js | 1 + src/server/models/product.js | 99 +++++----- src/server/router/admin_router.js | 200 ++++++++++++++------ src/server/router/index_router.js | 4 +- src/server/server.js | 2 +- src/server/tools/general.js | 53 +++++- src/server/tools/server_constants.js | 1 + 9 files changed, 345 insertions(+), 114 deletions(-) diff --git a/src/server/controllers/articleController.js b/src/server/controllers/articleController.js index ac295af..db34661 100644 --- a/src/server/controllers/articleController.js +++ b/src/server/controllers/articleController.js @@ -243,7 +243,7 @@ const getTableContent = async (options) => { } // Esegue la query per recuperare i dati - console.log('dataQuery', dataQuery); + // console.log('dataQuery', dataQuery); const dataResponse = await axios.post(SERVER_A_URL + '/query', { query: dataQuery }, { headers: { 'x-api-key': API_KEY } }); diff --git a/src/server/models/catalog.js b/src/server/models/catalog.js index 3468694..641ddbe 100755 --- a/src/server/models/catalog.js +++ b/src/server/models/catalog.js @@ -29,7 +29,8 @@ const CatalogSchema = new Schema({ }, foto_collana: IImg, idCollane: [{ - type: Number, + type: Schema.Types.ObjectId, + ref: 'Collana', }], argomenti: [{ type: String, @@ -72,6 +73,10 @@ const CatalogSchema = new Schema({ date_updated: { type: Date, }, + lista_prodotti: [{ + type: Schema.Types.ObjectId, + ref: 'Product', + }], }); /* @@ -94,7 +99,7 @@ CatalogSchema.statics.executeQueryTable = function (idapp, params, user) { return tools.executeQueryTable(this, idapp, params, user); }; -CatalogSchema.statics.findAllIdApp = async function (idapp) { +CatalogSchema.statics.OLD_findAllIdApp = async function (idapp) { const Catalog = this; const arrrec = await Catalog.aggregate([ @@ -119,6 +124,94 @@ CatalogSchema.statics.findAllIdApp = async function (idapp) { return arrrec; }; +CatalogSchema.statics.findAllIdApp = async function (idapp) { + const Catalog = this; + + let arrrec = await Catalog.find({ idapp }) + .sort({ title: 1 }) // Ordina i risultati per titolo + .populate({ + path: "idCollane", // Popola il campo idCollane + model: "Collana" // Specifica il modello della collezione Collana + }) + .populate({ + path: "lista_prodotti", // Popola il campo lista_prodotti + populate: { + path: "idProductInfo", // Popola il campo idProductInfo dentro ogni prodotto + model: "ProductInfo", // Specifica il modello della collezione ProductInfo + populate: [ + { + path: "idCatProds", + model: "CatProd" + }, + { + path: "idSubCatProds", + model: "SubCatProd" + }, + { + path: "idAuthors", + model: "Author" + } + ], + }, + }) + .populate({ + path: "lista_prodotti", + populate: { + path: "idProducer", + model: "Producer" + } + }) + .populate({ + path: "lista_prodotti", + populate: { + path: "idProvider", + model: "Provider" + } + }) + .populate({ + path: "lista_prodotti", + populate: { + path: "idStorehouses", + model: "Storehouse" + } + }) + .populate({ + path: "lista_prodotti", + populate: { + path: "idScontisticas", + model: "Scontistica" + } + }) + .populate({ + path: "lista_prodotti", + populate: { + path: "idGasordine", + model: "Gasordine" + } + }) + ; + + const transformedArrRec = arrrec.map(catalog => ({ + ...catalog.toObject(), // Converte il documento Mongoose in un oggetto JavaScript puro + lista_prodotti: catalog.lista_prodotti.map(product => ({ + ...product.toObject(), + productInfo: { + ...product.idProductInfo.toObject(), // Copia tutti i campi di idProductInfo + catprods: product.idProductInfo.idCatProds, // Rinomina idCatProds in catprods + subcatprods: product.idProductInfo.idSubCatProds, + collana: product.idProductInfo.idCollana, + authors: product.idProductInfo.idAuthors, + }, + producer: product.idProducer, + storehouse: product.idStorehouses, + scontisticas: product.idScontisticas, + gasordine: product.idGasordine, + })), + })); + + return transformedArrRec; +}; + const Catalog = mongoose.model('Catalog', CatalogSchema); Catalog.createIndexes() diff --git a/src/server/models/myscheda.js b/src/server/models/myscheda.js index cca2a46..92c16db 100755 --- a/src/server/models/myscheda.js +++ b/src/server/models/myscheda.js @@ -56,6 +56,7 @@ const IDimensioni = new Schema({ }); const IPagina = new Schema({ dimensioni: IDimensioni, + testo_title: IText, testo_up: IText, testo_down: IText, }); diff --git a/src/server/models/product.js b/src/server/models/product.js index 2af3560..224c33f 100755 --- a/src/server/models/product.js +++ b/src/server/models/product.js @@ -254,55 +254,44 @@ module.exports.executeQueryPickup = async function (idapp, params) { filterfindexact = { comune: strfind }; } - let limit = 10; + let limit = 20; let risexact = []; let filterfind = { idapp, - 'productInfo.name': { - $regex: `\\b${strfind}`, // Usa \\b per trovare solo le parole che iniziano con strfind - $options: 'i' // Rendi la ricerca case-insensitive - } - }; - - /* - let aggr1 = [ - { - $lookup: { - from: 'productinfos', - localField: 'idProductInfo', - foreignField: '_id', - as: 'productInfo' - } - }, - { - $lookup: { - from: 'authors', - localField: 'idAuthors', - foreignField: '_id', - as: 'authors' - } - }, - { - $match: { 'productInfo.name': strfind }, - }, - { $limit: 1 }, - { - $project: { - name: { $concat: ["$productInfo.name", " (", "$authors", ")"] }, + $or: [ + { + 'productInfo.name': { + $regex: `\\b${strfind}`, // Cerca parole che iniziano con strfind + $options: 'i' // Rende la ricerca case-insensitive + } }, - }, - ]; - - - if (params.filter) { - filterfind = { ...params.filter, ...filterfind }; - limit = 200; - } else { - // risexact = await City.find(filterfindexact, {comune: 1, prov: 1, reg: 1}).lean(); - risexact = await City.aggregate(aggr1); - } - */ + { + 'productInfo.code': { + $regex: `\\b${strfind}`, // Cerca parole che iniziano con strfind + $options: 'i' // Rende la ricerca case-insensitive + } + }, + { + 'productInfo.sku': { + $regex: `\\b${strfind}`, // Cerca parole che iniziano con strfind + $options: 'i' // Rende la ricerca case-insensitive + } + }, + { + 'productInfo.authors.name': { + $regex: `\\b${strfind}`, // Cerca parole che iniziano con strfind nel nome dell'autore + $options: 'i' // Rende la ricerca case-insensitive + } + }, + { + 'productInfo.authors.surname': { + $regex: `\\b${strfind}`, // Cerca parole che iniziano con strfind nel cognome dell'autore + $options: 'i' // Rende la ricerca case-insensitive + } + }, + ] + }; if (params.filter) { filterfind = { ...params.filter, ...filterfind }; @@ -327,24 +316,34 @@ module.exports.executeQueryPickup = async function (idapp, params) { { $lookup: { from: 'authors', - localField: 'idAuthors', + localField: 'productInfo.idAuthors', foreignField: '_id', - as: 'authors' + as: 'productInfo.authors' } }, + { + $unwind: { + path: '$authors', + preserveNullAndEmptyArrays: true, + }, + }, { $match: filterfind, }, { $limit: limit }, { $project: { - name: '$productInfo.name', - }, - }, + name: '$productInfo.name', // Nome del prodotto + authors: '$productInfo.authors', + productInfo: { + name: '$productInfo.name', // Nome del prodotto + authors: '$productInfo.authors', + }, + } + } ]; - // let ris = await City.find(filterfind, {comune: 1, prov: 1, reg: 1}).lean().limit(limit); let ris = await this.aggregate(aggr2).limit(limit); return [...risexact, ...ris]; diff --git a/src/server/router/admin_router.js b/src/server/router/admin_router.js index 2aef7f7..436a64c 100755 --- a/src/server/router/admin_router.js +++ b/src/server/router/admin_router.js @@ -115,16 +115,19 @@ async function findOrCreateCatProd(idapp, idArgomento, DescrArgomento) { function updateProductInfoCatProds(productInfo, reccatprod) { if (productInfo) { - const isChanged = (!productInfo.idCatProds || productInfo.idCatProds.length < 1) || - (productInfo.idCatProds[0].toString() !== reccatprod._id.toString()); + // Controllo che nell'array productInfo.idCatProds ci sia esattamente 1 record e che sia uguale a reccatprod._id + const cond3 = Array.isArray(productInfo.idCatProds) && productInfo.idCatProds[0].toString() !== reccatprod._id.toString(); + const isChanged = + !Array.isArray(productInfo.idCatProds) || // Assicurati che sia un array + productInfo.idCatProds.length !== 1 || // L'array deve contenere esattamente 1 elemento + cond3; // Il primo elemento deve essere uguale a reccatprod._id if (isChanged) { productInfo.idCatProds = [reccatprod._id]; - console.log('ARGOMENTO VARIATO!', reccatprod.name); + console.log('ARGOMENTO VARIATO!', reccatprod.name, ' Libro:', productInfo.name); } } } - async function compressPdf(inputFile, outputFile, compressione) { try { const tempFolder = path.join(cwd, "temp"); @@ -406,63 +409,138 @@ function fixURL(url) { async function downloadImgIfMissing(productInfo) { - if (!productInfo.image_link) - return { prodInfo: null, aggiornatoimg: false }; - - const relativeimg = productInfo.image_link.split('/').pop(); - const img = 'upload/products/' + productInfo.image_link.split('/').pop(); - const savePath = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine - - let scaricaimg = !productInfo.imagefile || !fs.existsSync(savePath); - - if (!productInfo.imagefile && fs.existsSync(savePath)) { - // esiste il file, ma sul DB non è corretto - const stats = fs.statSync(savePath); // Ottieni informazioni sul file - - if (stats.size > 0) { // Controlla se la dimensione del file è maggiore di zero - // Esiste il file ed è non vuoto, ma sul DB non è corretto - productInfo.imagefile = relativeimg; - return { prodInfo: productInfo, aggiornatoimg: true }; + try { + if (tools.sulServer()) { + dirmain = ''; } else { - scaricaimg = true; + dirmain = server_constants.DIR_PUBLIC_LOCALE; } - } - if (productInfo.imagefile && fs.existsSync(savePath)) { - // esiste il file, ma sul DB non è corretto - const stats = fs.statSync(savePath); // Ottieni informazioni sul file + const vecchiomodo = false; - if (stats.size <= 0) { // Controlla se la dimensione del file è maggiore di zero - scaricaimg = true; - } - } + if (productInfo.image_link && vecchiomodo) { - if (scaricaimg) { - // Download image from the URL productInfo.image_link - productInfo.imagefile = relativeimg; + const relativeimg = productInfo.image_link.split('/').pop(); + const img = tools.getdirByIdApp(productInfo.idapp) + dirmain + + server_constants.DIR_UPLOAD + '/products/' + productInfo.image_link.split('/').pop(); + const savePath = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine - const downloader = new ImageDownloader(); + let scaricaimg = !productInfo.imagefile || !fs.existsSync(savePath); - const aggiornatoimg = await downloader.downloadImage(productInfo.image_link, savePath, - { - maxRetries: 3, - initialDelay: 300, - timeout: 15000, - }).then(result => { - if (result) { - // console.log('Download completato con successo!'); + if (!productInfo.imagefile && fs.existsSync(savePath)) { + // esiste il file, ma sul DB non è corretto + const stats = fs.statSync(savePath); // Ottieni informazioni sul file + + if (stats.size > 0) { // Controlla se la dimensione del file è maggiore di zero + // Esiste il file ed è non vuoto, ma sul DB non è corretto + productInfo.imagefile = relativeimg; + return { prodInfo: productInfo, aggiornatoimg: true }; } else { - console.log('Download non riuscito.'); + scaricaimg = true; + } + } + + if (productInfo.imagefile && fs.existsSync(savePath)) { + // esiste il file, ma sul DB non è corretto + const stats = fs.statSync(savePath); // Ottieni informazioni sul file + + if (stats.size <= 0) { // Controlla se la dimensione del file è maggiore di zero + scaricaimg = true; + } + } + + + if (scaricaimg && vecchiomodo) { + // Download image from the URL productInfo.image_link + productInfo.imagefile = relativeimg; + + const downloader = new ImageDownloader(); + + const aggiornatoimg = await downloader.downloadImage(productInfo.image_link, savePath, + { + maxRetries: 3, + initialDelay: 300, + timeout: 15000, + }).then(result => { + if (result) { + // console.log('Download completato con successo!'); + } else { + console.log('Download non riuscito.'); + } + + return result; + + }); + return { prodInfo: productInfo, aggiornatoimg: aggiornatoimg.ris }; + } + } + + let fileesistente = false; + if (productInfo.imagefile) { + // controlla se esiste il file + const img = tools.getdirByIdApp(productInfo.idapp) + dirmain + + server_constants.DIR_UPLOAD + '/products/' + productInfo.imagefile.split('/').pop(); + const filecompleto = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine + + // Se non esiste lo scarico ! + fileesistente = fs.existsSync(filecompleto); + } + + if (!vecchiomodo && (!productInfo.image_link || !fileesistente)) { + + let scarica_da_sito = !productInfo.imagefile; + + if (!scarica_da_sito && productInfo.imagefile) { + scarica_da_sito = !fileesistente; // Se non esiste lo scarico ! + } + + if (scarica_da_sito) { + // date and time + productInfo.imagefile = 'img_' + new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''); + const img = tools.getdirByIdApp(productInfo.idapp) + dirmain + + server_constants.DIR_UPLOAD + '/products/' + productInfo.imagefile.split('/').pop(); + let savePath = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine + + + let link = 'https://www.gruppomacro.com/copertine.php?id_gm=' + productInfo.sku + + const downloader = new ImageDownloader(); + + + const aggiornatoimg = await downloader.downloadImage(link, savePath, + { + maxRetries: 3, + initialDelay: 300, + timeout: 15000, + nomefileoriginale: true, + }).then(result => { + if (result) { + // console.log('Download completato con successo!'); + } else { + console.log('Download non riuscito.'); + } + + return result; + + }); + if (aggiornatoimg.filepath) { + const filenamebase = path.basename(aggiornatoimg.filepath); + // const img = '/upload/products/' + filenamebase; + productInfo.imagefile = filenamebase; } - return result; + return { prodInfo: productInfo, aggiornatoimg: aggiornatoimg.ris }; + } else { + return { prodInfo: null, aggiornatoimg: false }; + } + } - }); - return { prodInfo: productInfo, aggiornatoimg }; + } catch (e) { + console.error('downloadImgIfMissing', e.message); } - return { prodInfo: null, aggiornatoimg: false }; + } async function completaSettaggioProduct_AndProductInfo(arrcampi_productInfo, arrcampi_product, rec, product, productInfo) { @@ -1042,8 +1120,7 @@ router.post('/import', authenticate, async (req, res) => { let imported = 0; let errors = 0; - const ripopola = true; - + const ripopola = false; //++MODIFICARE! if (ripopola) { dataObjects = null; @@ -1131,6 +1208,8 @@ router.post('/import', authenticate, async (req, res) => { let importa = true; + + if (!product.title || !product.sku) importa = false; @@ -1145,12 +1224,15 @@ router.post('/import', authenticate, async (req, res) => { if (!product.isbn) { const rectrovare = await ImportaIsbn.findOne({ sku: product.sku }).lean(); if (rectrovare) { - product.isbn = rectrovare.isbn; + // se l'isbn non inizia con 'field' allora è buono + if (rectrovare.isbn && !rectrovare.isbn.startsWith('field')) { + product.isbn = rectrovare.isbn; + } } else { nontrovati++; - console.log(`${nontrovati} - ISBN non trovato [sku=${product.sku} title=${product.title}]`) + // console.log(`${nontrovati} - ISBN non trovato [sku=${product.sku} title=${product.title}]`) } - + } let productInfo = { @@ -1169,12 +1251,21 @@ router.post('/import', authenticate, async (req, res) => { idCatProds: [], idSubCatProds: [], //img: 'upload/products/' + product.code + '.jpg', - image_link: product.image_link ? product.image_link : '', + // image_link: product.image_link ? product.image_link : '', img2: product.img2 ? product.img2 : '', img3: product.img3 ? product.img3 : '', img4: product.img4 ? product.img4 : '', checkout_link: product.checkout_link ? product.checkout_link : '', } + const test = false; + + if (test) { + productInfo.imagefile = '' + productInfo.image_link = ''; + let { prodInfo, aggiornatoimg } = await downloadImgIfMissing(productInfo); + console.log('imagefile=', prodInfo.imagefile) + + } let esisteindb = await ProductInfo.findOne({ code: productInfo.code }).lean(); if (esisteindb) { @@ -1250,6 +1341,7 @@ router.post('/import', authenticate, async (req, res) => { } else { arrcat = product.categories.trim().split(','); + productInfo.idCatProds = []; for (const mycat of arrcat) { let mycatstr = mycat.trim(); @@ -1336,6 +1428,7 @@ router.post('/import', authenticate, async (req, res) => { if (product.subcat_name) { arrsubcat = product.subcat_name.trim().split(','); + productInfo.idSubCatProds = []; for (const mysubcat of arrsubcat) { let mysubcatstr = mysubcat.trim(); @@ -1662,6 +1755,7 @@ router.post('/import', authenticate, async (req, res) => { if (product.subcat_name) { arrsubcat = product.subcat_name.trim().split(','); + productInfo.idSubCatProds = []; for (const mysubcat of arrsubcat) { let mysubcatstr = mysubcat.trim(); diff --git a/src/server/router/index_router.js b/src/server/router/index_router.js index c76baa3..a4eb5f4 100755 --- a/src/server/router/index_router.js +++ b/src/server/router/index_router.js @@ -2304,7 +2304,7 @@ function uploadFile(req, res, version) { if (tools.sulServer()) { dirmain = ''; } else { - dirmain = '/public'; + dirmain = server_constants.DIR_PUBLIC_LOCALE; } } @@ -2503,7 +2503,7 @@ function deleteFile(req, res, version) { let dirmain = ''; if (version > 0) { if (!tools.sulServer()) { - dirmain = '/public'; + dirmain = server_constants.DIR_PUBLIC_LOCALE; } } diff --git a/src/server/server.js b/src/server/server.js index 4a352b0..29e4855 100755 --- a/src/server/server.js +++ b/src/server/server.js @@ -540,7 +540,7 @@ connectToDatabase(connectionUrl, options) try { if (!tools.sulServer()) { - dirmain = '/public'; + dirmain = server_constants.DIR_PUBLIC_LOCALE; } for (const rec of arrlist) { diff --git a/src/server/tools/general.js b/src/server/tools/general.js index 8d0b255..6e3a353 100755 --- a/src/server/tools/general.js +++ b/src/server/tools/general.js @@ -433,7 +433,8 @@ class ImageDownloader { initialDelay = 1000, // Ritardo iniziale maxDelay = 10000, // Ritardo massimo timeout = 30000, // Timeout della richiesta - validateContentType = true // Verifica del tipo di contenuto + validateContentType = true, // Verifica del tipo di contenuto + nomefileoriginale = true, } = options; // Funzione per il backoff esponenziale @@ -448,7 +449,6 @@ class ImageDownloader { fs.unlinkSync(filepath); } - const writer = fs.createWriteStream(filepath); if (attempt > 1) console.log(`📥 Tentativo ${attempt}/${maxRetries} - Scaricamento: ${url}`); @@ -487,6 +487,28 @@ class ImageDownloader { downloadedBytes += chunk.length; }); + let writer = null; + + if (nomefileoriginale) { + // Estrai il nome del file dall'URL o da Content-Disposition + //let fileName = this.extractFileNameFromUrl(url) || this.extractFileNameFromHeaders(response.headers); + let fileName = path.basename(response.data.responseUrl); + + // Se il nome del file non è specificato, genera un nome predefinito + if (!fileName) { + fileName = `image_${Date.now()}.jpg`; + } + + // Genera il percorso completo del file + const fullPath = path.join(path.dirname(filepath), fileName); + + filepath = fullPath; + + } + + // Scrivi il file sul disco + writer = fs.createWriteStream(filepath); + response.data.pipe(writer); await new Promise((resolve, reject) => { @@ -501,8 +523,11 @@ class ImageDownloader { }); }); + console.info(`✅ Immagine scaricata con successo in ${filepath}`); - return true; + + + return { ris: true, filepath }; } catch (error) { console.error(`❌ Errore nel tentativo ${attempt}/${maxRetries}:`, error.message); @@ -514,7 +539,7 @@ class ImageDownloader { if (attempt === maxRetries) { console.error(`Download fallito dopo ${maxRetries} tentativi: ${error.message}`); - return false; + return { ris: false }; } const delay = getDelay(attempt); @@ -523,6 +548,24 @@ class ImageDownloader { } } } + + // Funzione per estrarre il nome del file dall'URL + extractFileNameFromUrl(url) { + const match = url.match(/\/([^/?#]+)(?:[?#]|$)/); + return match ? decodeURIComponent(match[1]) : null; + } + + // Funzione per estrarre il nome del file da Content-Disposition + extractFileNameFromHeaders(headers) { + const contentDisposition = headers['content-disposition']; + if (contentDisposition) { + const match = contentDisposition.match(/filename="([^"]+)"/); + if (match) { + return decodeURIComponent(match[1]); + } + } + return null; + } } @@ -3380,7 +3423,7 @@ module.exports = { try { console.time(name); } catch (e) { - + } } }, diff --git a/src/server/tools/server_constants.js b/src/server/tools/server_constants.js index 1605807..ac4575d 100755 --- a/src/server/tools/server_constants.js +++ b/src/server/tools/server_constants.js @@ -101,6 +101,7 @@ module.exports = Object.freeze({ PREFIX_IMG_SMALL: 'sm_', DIR_UPLOAD: '/upload', //'/upload' + DIR_PUBLIC_LOCALE: '/public', Privacy: { all: 'all',