From d7bf701d0e1dc75825cbaf486297863c66c39a49 Mon Sep 17 00:00:00 2001 From: Truong Vo <41848815+vmtruong301296@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:53:48 +0700 Subject: [PATCH] =?UTF-8?q?Hi=E1=BB=87u=20ch=E1=BB=89nh=20x=C3=B3a=20batch?= =?UTF-8?q?=20m=E1=BB=81m?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 132 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/server.js b/server.js index 9a29578..ccd62c9 100644 --- a/server.js +++ b/server.js @@ -24,17 +24,41 @@ const db = new sqlite3.Database('./products.db', (err) => { console.log('Connected to products database.'); }); -// Create tables -db.serialize(() => { - // Batches table - db.run(`CREATE TABLE IF NOT EXISTS batches ( +function runInitAsync(sql, params = []) { + return new Promise((resolve, reject) => { + db.run(sql, params, (err) => { + if (err) reject(err); + else resolve(); + }); + }); +} + +function allInitAsync(sql, params = []) { + return new Promise((resolve, reject) => { + db.all(sql, params, (err, rows) => { + if (err) reject(err); + else resolve(rows); + }); + }); +} + +async function initializeDatabase() { + await runInitAsync(`CREATE TABLE IF NOT EXISTS batches ( id INTEGER PRIMARY KEY AUTOINCREMENT, batch_name TEXT NOT NULL UNIQUE, - createdAt DATETIME DEFAULT CURRENT_TIMESTAMP + createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, + status INTEGER NOT NULL DEFAULT 0 )`); - // Items table (for valid items) - db.run(`CREATE TABLE IF NOT EXISTS items ( + // Migration for existing databases: add status column if missing + const columns = await allInitAsync(`PRAGMA table_info(batches)`); + const hasStatusColumn = columns.some((col) => col.name === 'status'); + if (!hasStatusColumn) { + await runInitAsync(`ALTER TABLE batches ADD COLUMN status INTEGER NOT NULL DEFAULT 0`); + console.log('Added status column to batches table.'); + } + + await runInitAsync(`CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, batch_id INTEGER NOT NULL, brand TEXT, @@ -45,8 +69,7 @@ db.serialize(() => { FOREIGN KEY (batch_id) REFERENCES batches(id) ON DELETE CASCADE )`); - // Items_mix table (for mixed MPN items) - db.run(`CREATE TABLE IF NOT EXISTS items_mix ( + await runInitAsync(`CREATE TABLE IF NOT EXISTS items_mix ( id INTEGER PRIMARY KEY AUTOINCREMENT, batch_id INTEGER NOT NULL, brand TEXT, @@ -58,13 +81,14 @@ db.serialize(() => { )`); // Create indexes for better performance - db.run(`CREATE INDEX IF NOT EXISTS idx_items_batch_id ON items(batch_id)`); - db.run(`CREATE INDEX IF NOT EXISTS idx_items_mix_batch_id ON items_mix(batch_id)`); - db.run(`CREATE INDEX IF NOT EXISTS idx_items_mpn ON items(mpn)`); - db.run(`CREATE INDEX IF NOT EXISTS idx_items_sn ON items(sn)`); - db.run(`CREATE INDEX IF NOT EXISTS idx_items_brand ON items(brand)`); - db.run(`CREATE INDEX IF NOT EXISTS idx_items_mix_brand ON items_mix(brand)`); -}); + await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_batch_id ON items(batch_id)`); + await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mix_batch_id ON items_mix(batch_id)`); + await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_batches_status ON batches(status)`); + await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mpn ON items(mpn)`); + await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_sn ON items(sn)`); + await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_brand ON items(brand)`); + await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mix_brand ON items_mix(brand)`); +} // ==================== BATCH API ROUTES ==================== @@ -220,43 +244,44 @@ app.get('/api/batch/get-all', (req, res) => { const column = validColumns.includes(sortBy) ? sortBy : 'id'; const order = sortOrder.toUpperCase() === 'ASC' ? 'ASC' : 'DESC'; - let query = 'SELECT * FROM batches'; - let countQuery = 'SELECT COUNT(*) as total FROM batches'; - let params = []; + const whereConditions = ['status = 0']; + let searchParams = []; if (search) { - const searchCondition = ` - WHERE - batch_name LIKE ? - OR id LIKE ? - OR EXISTS ( - SELECT 1 FROM items - WHERE items.batch_id = batches.id - AND sn LIKE ? + const searchCondition = ` + ( + batch_name LIKE ? + OR id LIKE ? + OR EXISTS ( + SELECT 1 FROM items + WHERE items.batch_id = batches.id + AND sn LIKE ? + ) + OR EXISTS ( + SELECT 1 FROM items_mix + WHERE items_mix.batch_id = batches.id + AND sn LIKE ? + ) ) - OR EXISTS ( - SELECT 1 FROM items_mix - WHERE items_mix.batch_id = batches.id - AND sn LIKE ? - ) - `; + `; + whereConditions.push(searchCondition); - query += searchCondition; - countQuery += searchCondition; + const searchParam = `%${search}%`; + searchParams = [searchParam, searchParam, searchParam, searchParam]; + } - const searchParam = `%${search}%`; - params = [searchParam, searchParam, searchParam, searchParam]; -} + const whereClause = ` WHERE ${whereConditions.join(' AND ')}`; + const countQuery = `SELECT COUNT(*) as total FROM batches${whereClause}`; + const query = `SELECT * FROM batches${whereClause} ORDER BY ${column} ${order} LIMIT ? OFFSET ?`; + const countParams = [...searchParams]; + const queryParams = [...searchParams, limit, offset]; - query += ` ORDER BY ${column} ${order} LIMIT ? OFFSET ?`; - params.push(limit, offset); - - db.get(countQuery, search ? params.slice(0, 2) : [], (err, countRow) => { + db.get(countQuery, countParams, (err, countRow) => { if (err) { return res.status(500).json({ error: err.message }); } - db.all(query, params, (err, batches) => { + db.all(query, queryParams, (err, batches) => { if (err) { return res.status(500).json({ error: err.message }); } @@ -320,7 +345,7 @@ app.get('/api/batch/get-all', (req, res) => { app.get('/api/batch/get/:id', (req, res) => { const id = req.params.id; - db.get('SELECT * FROM batches WHERE id = ?', [id], (err, batch) => { + db.get('SELECT * FROM batches WHERE id = ? AND status = 0', [id], (err, batch) => { if (err) { return res.status(500).json({ error: err.message }); } @@ -353,11 +378,11 @@ app.get('/api/batch/get/:id', (req, res) => { }); }); -// Delete batch (cascade delete items and items_mix) +// Soft delete batch (update status from 0 to 1) app.delete('/api/batch/delete/:id', (req, res) => { const id = req.params.id; - db.run('DELETE FROM batches WHERE id = ?', id, function (err) { + db.run('UPDATE batches SET status = 1 WHERE id = ? AND status = 0', [id], function (err) { if (err) { return res.status(500).json({ error: err.message }); } @@ -366,7 +391,7 @@ app.delete('/api/batch/delete/:id', (req, res) => { return res.status(404).json({ error: 'Batch not found' }); } - res.json({ success: true, deleted: this.changes }); + res.json({ success: true, updated: this.changes, deleted: this.changes }); }); }); @@ -428,6 +453,13 @@ app.get('/', (req, res) => { }); const PORT = process.env.PORT || 4444; -app.listen(PORT, () => { - console.log(`Server is running on http://localhost:${PORT}`); -}); \ No newline at end of file +initializeDatabase() + .then(() => { + app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); + }); + }) + .catch((err) => { + console.error('Database initialization failed:', err.message); + process.exit(1); + }); \ No newline at end of file