function parseJsonSafe(value) { if (!value) { return null; } if (typeof value === 'object') { return value; } try { return JSON.parse(value); } catch (_error) { return null; } } function readFormArray(body, key) { if (Array.isArray(body[key])) { return body[key]; } if (body[key] === undefined) { return []; } return [body[key]]; } function normalizePageNumber(value) { const pageNumber = Math.floor(Number(value) || 1); return Math.max(1, pageNumber); } async function fetchPagedRows(pool, options) { const selectSql = String(options && options.selectSql || '').trim(); const countSql = String(options && options.countSql || '').trim(); const params = Array.isArray(options && options.params) ? options.params : []; const pageSize = Math.max(1, Number(options && options.pageSize) || 10); const currentPage = normalizePageNumber(options && options.page); if (!selectSql || !countSql) { throw new Error('fetchPagedRows requires selectSql and countSql.'); } const [countRows] = await pool.query(countSql, params); const totalItems = Number(countRows && countRows[0] && countRows[0].count) || 0; const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); const safeCurrentPage = Math.min(currentPage, totalPages); const offset = (safeCurrentPage - 1) * pageSize; const [rows] = await pool.query(`${selectSql} LIMIT ? OFFSET ?`, params.concat([pageSize, offset])); return { rows: rows || [], totalItems: totalItems, totalPages: totalPages, currentPage: safeCurrentPage, pageSize: pageSize }; } async function fetchDuplicateName(pool, tableName, name, excludeId, columnName) { const normalizedName = String(name || '').trim(); if (!normalizedName) { return null; } const normalizedColumnName = String(columnName || 'name').trim() || 'name'; const params = [normalizedName]; let sql = `SELECT id, \`${normalizedColumnName}\` AS name FROM \`${tableName}\` WHERE LOWER(TRIM(\`${normalizedColumnName}\`)) = LOWER(TRIM(?))`; if (excludeId !== undefined && excludeId !== null) { sql += ' AND id <> ?'; params.push(excludeId); } sql += ' LIMIT 1'; const [rows] = await pool.query(sql, params); return rows[0] || null; } module.exports = { parseJsonSafe, readFormArray, normalizePageNumber, fetchPagedRows, fetchDuplicateName };