const express = require('express'); const QRCode = require('qrcode'); const db = require('../utils/database'); const { authenticateToken, requireAdmin } = require('../middleware/auth'); const router = express.Router(); // 生成序列号 router.post('/generate', authenticateToken, requireAdmin, async (req, res) => { try { const { companyName, quantity = 1, validDays = 365 } = req.body; if (!companyName) { return res.status(400).json({ error: '企业名称不能为空' }); } if (quantity < 1 || quantity > 100) { return res.status(400).json({ error: '生成数量必须在1-100之间' }); } // 计算有效期 const validUntil = new Date(); validUntil.setDate(validUntil.getDate() + validDays); // 确保企业存在 const existingCompany = await db.get('SELECT * FROM companies WHERE company_name = ?', [companyName]); if (!existingCompany) { await db.run('INSERT INTO companies (company_name, is_active) VALUES (?, 1)', [companyName]); } // 生成序列号 const serials = []; const prefix = 'BF'; const datePart = new Date().getFullYear().toString().substr(2); // 批量插入序列号 const insertPromises = []; for (let i = 0; i < quantity; i++) { // 使用随机数生成序列号,避免重复 const randomPart = Math.floor(Math.random() * 1000000).toString().padStart(6, '0'); const serialNumber = `${prefix}${datePart}${randomPart}`; insertPromises.push( db.run( 'INSERT INTO serials (serial_number, company_name, valid_until, created_by) VALUES (?, ?, ?, ?)', [serialNumber, companyName, validUntil.toISOString().slice(0, 19).replace('T', ' '), req.user.id] ) ); serials.push({ serialNumber, companyName, validUntil: validUntil.toISOString(), createdAt: new Date().toISOString() }); } await Promise.all(insertPromises); res.json({ message: `成功生成${quantity}个序列号`, serials }); } catch (error) { console.error('生成序列号错误:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // 生成二维码 router.post('/:serialNumber/qrcode', authenticateToken, async (req, res) => { try { const { serialNumber } = req.params; let { baseUrl } = req.body; if (!serialNumber) { return res.status(400).json({ error: '序列号不能为空' }); } // 验证序列号是否存在 const serial = await db.get( 'SELECT s.*, u.name as created_by_name FROM serials s LEFT JOIN users u ON s.created_by = u.id WHERE s.serial_number = ?', [serialNumber.toUpperCase()] ); if (!serial) { return res.status(404).json({ error: '序列号不存在' }); } if (!serial.is_active) { return res.status(400).json({ error: '序列号已被禁用' }); } // 检查是否过期 if (serial.valid_until && new Date(serial.valid_until) < new Date()) { return res.status(400).json({ error: '序列号已过期' }); } // 生成查询URL if (!baseUrl) { baseUrl = `${req.protocol}://${req.get('host')}/query.html`; } const queryUrl = baseUrl.includes('?') ? `${baseUrl}&serial=${serial.serial_number}` : `${baseUrl}?serial=${serial.serial_number}`; // 生成二维码 const qrCodeData = await QRCode.toDataURL(queryUrl, { width: 200, color: { dark: '#165DFF', light: '#ffffff' } }); res.json({ message: '二维码生成成功', qrCodeData, queryUrl, serialNumber: serial.serial_number, companyName: serial.company_name, validUntil: serial.valid_until }); } catch (error) { console.error('生成二维码错误:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // 查询序列号 router.get('/:serialNumber/query', async (req, res) => { try { const { serialNumber } = req.params; if (!serialNumber) { return res.status(400).json({ error: '序列号不能为空' }); } // 查询序列号 const serial = await db.get( 'SELECT s.*, u.name as created_by_name FROM serials s LEFT JOIN users u ON s.created_by = u.id WHERE s.serial_number = ?', [serialNumber.toUpperCase()] ); if (!serial) { return res.status(404).json({ error: '序列号不存在' }); } // 检查是否过期 if (serial.valid_until && new Date(serial.valid_until) < new Date()) { return res.status(400).json({ error: '序列号已过期' }); } res.json({ message: '查询成功', serial: { serialNumber: serial.serial_number, companyName: serial.company_name, validUntil: serial.valid_until, status: serial.is_active ? 'active' : 'disabled', isActive: serial.is_active, createdAt: serial.created_at, createdBy: serial.created_by_name } }); } catch (error) { console.error('查询序列号错误:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // 获取序列号列表 router.get('/', authenticateToken, async (req, res) => { try { const { page = 1, limit = 20, search = '' } = req.query; const offset = (page - 1) * limit; let query = ` SELECT s.*, u.name as created_by_name FROM serials s LEFT JOIN users u ON s.created_by = u.id `; let countQuery = 'SELECT COUNT(*) as total FROM serials s'; let params = []; if (search) { query += ' WHERE s.serial_number LIKE ? OR s.company_name LIKE ?'; countQuery += ' WHERE s.serial_number LIKE ? OR s.company_name LIKE ?'; const searchParam = `%${search}%`; params.push(searchParam, searchParam); } query += ' ORDER BY s.created_at DESC LIMIT ? OFFSET ?'; params.push(parseInt(limit), parseInt(offset)); const [serials, countResult] = await Promise.all([ db.all(query, params), db.get(countQuery, params.slice(0, -2)) ]); const total = countResult ? countResult.total : 0; const totalPages = Math.ceil(total / limit); res.json({ message: '获取序列号列表成功', data: serials.map(s => ({ serialNumber: s.serial_number, companyName: s.company_name, validUntil: s.valid_until, isActive: s.is_active, createdAt: s.created_at, createdBy: s.created_by_name })), pagination: { page: parseInt(page), limit: parseInt(limit), total, totalPages } }); } catch (error) { console.error('获取序列号列表错误:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // 更新序列号 router.patch('/:serialNumber', authenticateToken, requireAdmin, async (req, res) => { try { const { serialNumber } = req.params; const { companyName, validUntil, isActive } = req.body; if (!serialNumber) { return res.status(400).json({ error: '序列号不能为空' }); } // 检查序列号是否存在 const existingSerial = await db.get('SELECT * FROM serials WHERE serial_number = ?', [serialNumber.toUpperCase()]); if (!existingSerial) { return res.status(404).json({ error: '序列号不存在' }); } // 构建更新字段 const updateFields = []; const params = []; if (companyName !== undefined) { updateFields.push('company_name = ?'); params.push(companyName); } if (validUntil !== undefined) { updateFields.push('valid_until = ?'); params.push(validUntil); } if (isActive !== undefined) { updateFields.push('is_active = ?'); params.push(isActive ? 1 : 0); } if (updateFields.length === 0) { return res.status(400).json({ error: '没有提供更新字段' }); } updateFields.push('updated_at = CURRENT_TIMESTAMP'); params.push(serialNumber.toUpperCase()); await db.run( `UPDATE serials SET ${updateFields.join(', ')} WHERE serial_number = ?`, params ); // 获取更新后的序列号信息 const updatedSerial = await db.get( 'SELECT s.*, u.name as created_by_name FROM serials s LEFT JOIN users u ON s.created_by = u.id WHERE s.serial_number = ?', [serialNumber.toUpperCase()] ); res.json({ message: '序列号更新成功', serial: { serialNumber: updatedSerial.serial_number, companyName: updatedSerial.company_name, validUntil: updatedSerial.valid_until, isActive: updatedSerial.is_active, createdAt: updatedSerial.created_at, updatedAt: updatedSerial.updated_at, createdBy: updatedSerial.created_by_name } }); } catch (error) { console.error('更新序列号错误:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // 吊销序列号 router.post('/:serialNumber/revoke', authenticateToken, requireAdmin, async (req, res) => { try { const { serialNumber } = req.params; if (!serialNumber) { return res.status(400).json({ error: '序列号不能为空' }); } // 检查序列号是否存在 const existingSerial = await db.get( 'SELECT * FROM serials WHERE serial_number = ?', [serialNumber.toUpperCase()] ); if (!existingSerial) { return res.status(404).json({ error: '序列号不存在' }); } // 如果已经吊销,返回提示 if (!existingSerial.is_active) { return res.status(400).json({ error: '序列号已被吊销' }); } // 吊销序列号(将 is_active 设为 0) await db.run( 'UPDATE serials SET is_active = 0, updated_at = CURRENT_TIMESTAMP WHERE serial_number = ?', [serialNumber.toUpperCase()] ); res.json({ message: '序列号已吊销', data: { serialNumber: serialNumber.toUpperCase() } }); } catch (error) { console.error('吊销序列号错误:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // 自定义前缀生成序列号(管理员权限) router.post('/generate-with-prefix', authenticateToken, requireAdmin, async (req, res) => { try { const { companyName, quantity = 1, validDays = 365, serialPrefix } = req.body; if (!companyName) { return res.status(400).json({ error: '企业名称不能为空' }); } if (!serialPrefix || serialPrefix.length > 10) { return res.status(400).json({ error: '自定义前缀不能为空且不能超过10个字符' }); } if (quantity < 1 || quantity > 100) { return res.status(400).json({ error: '生成数量必须在1-100之间' }); } // 计算有效期 const validUntil = new Date(); validUntil.setDate(validUntil.getDate() + validDays); // 生成序列号 const serials = []; const prefix = serialPrefix.toUpperCase().replace(/[^A-Z0-9]/g, ''); if (!prefix) { return res.status(400).json({ error: '自定义前缀包含无效字符,只能包含字母和数字' }); } // 批量插入序列号 const insertPromises = []; for (let i = 0; i < quantity; i++) { // 使用随机数生成序列号,避免重复 const randomPart = Math.floor(Math.random() * 1000000).toString().padStart(6, '0'); const serialNumber = `${prefix}${randomPart}`; insertPromises.push( db.run( 'INSERT INTO serials (serial_number, company_name, valid_until, created_by) VALUES (?, ?, ?, ?)', [serialNumber, companyName, validUntil.toISOString(), req.user.id] ) ); serials.push({ serialNumber, companyName, validUntil: validUntil.toISOString(), createdAt: new Date().toISOString() }); } await Promise.all(insertPromises); res.json({ message: `成功生成${quantity}个序列号`, serials }); } catch (error) { console.error('生成序列号错误:', error); res.status(500).json({ error: '服务器内部错误' }); } }); module.exports = router;