Files
backend-node/routes/companies.js
2026-02-06 14:29:29 +08:00

407 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const express = require('express');
const db = require('../utils/database');
const { authenticateToken, requireAdmin } = require('../middleware/auth');
const router = express.Router();
// 获取企业列表
router.get('/', authenticateToken, requireAdmin, async (req, res) => {
try {
const { page = 1, limit = 20, search = '' } = req.query;
const offset = (page - 1) * limit;
let query = `
SELECT c.company_name, c.created_at as first_created, c.updated_at as last_created, c.is_active,
(SELECT COUNT(*) FROM serials s WHERE s.company_name = c.company_name) as serial_count,
(SELECT COUNT(*) FROM serials s WHERE s.company_name = c.company_name AND s.is_active = 1) as active_count
FROM companies c
`;
let countQuery = 'SELECT COUNT(*) as total FROM companies';
let params = [];
if (search) {
query += ' WHERE c.company_name LIKE ?';
countQuery += ' WHERE company_name LIKE ?';
params.push(`%${search}%`);
}
query += ' ORDER BY c.updated_at DESC LIMIT ? OFFSET ?';
params.push(parseInt(limit), parseInt(offset));
const [companies, 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: companies.map(company => ({
companyName: company.company_name,
firstCreated: company.first_created,
lastCreated: company.last_created,
serialCount: company.serial_count,
activeCount: company.active_count,
status: company.is_active ? 'active' : 'disabled'
})),
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
totalPages
}
});
} catch (error) {
console.error('获取企业列表错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 获取企业详情
router.get('/:companyName', authenticateToken, requireAdmin, async (req, res) => {
try {
const { companyName } = req.params;
const decodedCompanyName = decodeURIComponent(companyName);
const { page = 1, limit = 20 } = req.query;
const offset = (page - 1) * limit;
// 获取企业基本信息
const companyInfo = await db.get('SELECT * FROM companies WHERE company_name = ?', [decodedCompanyName]);
if (!companyInfo) {
return res.status(404).json({ error: '企业不存在' });
}
// 获取序列号统计信息
const serialStats = await db.get(`
SELECT COUNT(*) as serial_count,
SUM(CASE WHEN is_active = 1 THEN 1 ELSE 0 END) as active_count,
SUM(CASE WHEN is_active = 0 THEN 1 ELSE 0 END) as disabled_count,
SUM(CASE WHEN valid_until IS NOT NULL AND valid_until <= datetime('now') THEN 1 ELSE 0 END) as expired_count
FROM serials
WHERE company_name = ?
`, [decodedCompanyName]);
// 获取企业的序列号列表
const serials = await db.all(`
SELECT s.*, u.name as created_by_name
FROM serials s
LEFT JOIN users u ON s.created_by = u.id
WHERE s.company_name = ?
ORDER BY s.created_at DESC
LIMIT ? OFFSET ?
`, [decodedCompanyName, parseInt(limit), parseInt(offset)]);
// 获取统计数据
const stats = await db.all(`
SELECT strftime('%Y-%m', created_at) as month,
COUNT(*) as count
FROM serials
WHERE company_name = ?
GROUP BY strftime('%Y-%m', created_at)
ORDER BY month DESC
LIMIT 12
`, [decodedCompanyName]);
res.json({
message: '获取企业详情成功',
data: {
companyName: decodedCompanyName,
serialCount: serialStats?.serial_count || 0,
activeCount: serialStats?.active_count || 0,
disabledCount: serialStats?.disabled_count || 0,
expiredCount: serialStats?.expired_count || 0,
firstCreated: companyInfo.created_at,
lastCreated: companyInfo.updated_at,
status: companyInfo.is_active ? 'active' : 'disabled',
serials: serials.map(s => ({
serialNumber: s.serial_number,
validUntil: s.valid_until,
isActive: s.is_active,
createdAt: s.created_at,
createdBy: s.created_by_name
})),
monthlyStats: stats.map(stat => ({
month: stat.month,
count: stat.count
}))
}
});
} catch (error) {
console.error('获取企业详情错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 更新企业信息
router.patch('/:companyName', authenticateToken, requireAdmin, async (req, res) => {
try {
const { companyName } = req.params;
const decodedCompanyName = decodeURIComponent(companyName);
const { newCompanyName } = req.body;
if (!newCompanyName || newCompanyName.trim() === '') {
return res.status(400).json({ error: '新企业名称不能为空' });
}
// 检查企业是否存在
const existingCompany = db.get(
'SELECT COUNT(*) as count FROM serials WHERE company_name = ?',
[decodedCompanyName]
);
if (!existingCompany || existingCompany.count === 0) {
return res.status(404).json({ error: '企业不存在' });
}
// 检查新企业名称是否已存在
const duplicateCompany = db.get(
'SELECT COUNT(*) as count FROM serials WHERE company_name = ?',
[newCompanyName]
);
if (duplicateCompany && duplicateCompany.count > 0) {
return res.status(400).json({ error: '企业名称已存在' });
}
// 更新企业名称
db.run(
'UPDATE serials SET company_name = ?, updated_at = CURRENT_TIMESTAMP WHERE company_name = ?',
[newCompanyName, decodedCompanyName]
);
res.json({
message: '企业名称更新成功',
data: {
oldCompanyName: decodedCompanyName,
newCompanyName
}
});
} catch (error) {
console.error('更新企业信息错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 删除企业(物理删除,完全删除企业和所有序列号)
router.delete('/:companyName', authenticateToken, requireAdmin, async (req, res) => {
try {
const { companyName } = req.params;
console.log('原始参数:', companyName);
const decodedCompanyName = decodeURIComponent(companyName);
console.log('解码后参数:', decodedCompanyName);
// 检查企业是否存在
const existingCompany = await db.get(
'SELECT * FROM companies WHERE company_name = ?',
[decodedCompanyName]
);
if (!existingCompany) {
return res.status(404).json({ error: '企业不存在' });
}
// 开始事务
db.run('BEGIN TRANSACTION');
try {
// 删除该企业的所有序列号
const serialDeleteResult = db.run(
'DELETE FROM serials WHERE company_name = ?',
[decodedCompanyName]
);
console.log('删除序列号结果:', serialDeleteResult);
// 删除企业记录
const companyDeleteResult = db.run(
'DELETE FROM companies WHERE company_name = ?',
[decodedCompanyName]
);
console.log('删除企业结果:', companyDeleteResult);
if (companyDeleteResult.changes === 0) {
db.run('ROLLBACK');
return res.status(404).json({ error: '企业不存在' });
}
db.run('COMMIT');
res.json({
message: '企业已完全删除,所有相关序列号已删除',
data: {
companyName: decodedCompanyName,
deletedSerialCount: serialDeleteResult.changes,
deletedCompanyCount: companyDeleteResult.changes
}
});
} catch (error) {
console.error('删除企业过程中错误:', error);
db.run('ROLLBACK');
throw error;
}
} catch (error) {
console.error('删除企业错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 获取企业统计数据
router.get('/stats/overview', authenticateToken, requireAdmin, async (req, res) => {
try {
// 获取总企业数
const companyCount = await db.get('SELECT COUNT(*) as count FROM companies');
// 获取总序列号数
const serialCount = await db.get('SELECT COUNT(*) as count FROM serials');
// 获取活跃序列号数
const activeCount = await db.get(`
SELECT COUNT(*) as count FROM serials
WHERE is_active = 1 AND (valid_until IS NULL OR valid_until > datetime('now'))
`);
// 按月份统计 - 使用正确的日期格式
const monthlyStats = await db.all(`
SELECT strftime('%Y-%m', created_at) as month,
COUNT(DISTINCT company_name) as company_count,
COUNT(*) as serial_count
FROM serials
WHERE created_at >= strftime('%Y-%m-%d', datetime('now', '-12 months'))
GROUP BY strftime('%Y-%m', created_at)
ORDER BY month ASC
`);
// 获取最新添加的企业
const recentCompanies = await db.all(`
SELECT c.company_name, c.created_at as last_created, c.is_active
FROM companies c
ORDER BY c.updated_at DESC
LIMIT 10
`);
// 获取最近生成的序列号
const recentSerials = await db.all(`
SELECT s.serial_number, s.company_name, s.is_active, s.created_at
FROM serials s
ORDER BY s.created_at DESC
LIMIT 10
`);
// 如果没有数据生成过去12个月的空数据
if (monthlyStats.length === 0) {
const now = new Date();
for (let i = 11; i >=0; i--) {
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
const month = date.toISOString().substr(0, 7);
monthlyStats.push({
month,
company_count: 0,
serial_count: 0
});
}
}
res.json({
message: '获取统计数据成功',
data: {
overview: {
totalCompanies: companyCount.count || 0,
totalSerials: serialCount.count || 0,
activeSerials: activeCount.count || 0,
inactiveSerials: (serialCount.count || 0) - (activeCount.count || 0)
},
monthlyStats: monthlyStats.map(stat => ({
month: stat.month,
company_count: stat.company_count,
serial_count: stat.serial_count
})),
recentCompanies: recentCompanies.map(c => ({
companyName: c.company_name,
lastCreated: c.last_created,
status: c.is_active ? 'active' : 'disabled'
})),
recentSerials: recentSerials.map(s => ({
serialNumber: s.serial_number,
companyName: s.company_name,
isActive: s.is_active,
createdAt: s.created_at
}))
}
});
} catch (error) {
console.error('获取统计数据错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 吊销单个序列号
router.delete('/:companyName/serials/:serialNumber', authenticateToken, requireAdmin, async (req, res) => {
try {
const { companyName, serialNumber } = req.params;
// 检查序列号是否存在且属于该企业
const serial = await db.get(
'SELECT * FROM serials WHERE serial_number = ? AND company_name = ?',
[serialNumber.toUpperCase(), companyName]
);
if (!serial) {
return res.status(404).json({ error: '序列号不存在或不属于该企业' });
}
// 物理删除序列号
await db.run(
'DELETE FROM serials WHERE serial_number = ? AND company_name = ?',
[serialNumber.toUpperCase(), companyName]
);
res.json({
message: '序列号已成功吊删除',
data: {
serialNumber: serial.serial_number,
companyName
}
});
} catch (error) {
console.error('吊销序列号错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 吊销企业
router.post('/:companyName/revoke', authenticateToken, requireAdmin, async (req, res) => {
try {
const { companyName } = req.params;
const decodedCompanyName = decodeURIComponent(companyName);
// 检查企业是否存在
const existingCompany = await db.get(
'SELECT COUNT(*) as count FROM serials WHERE company_name = ?',
[decodedCompanyName]
);
if (!existingCompany || existingCompany.count === 0) {
return res.status(404).json({ error: '企业不存在' });
}
// 吊销该企业的所有序列号(将 is_active 设为 0
await db.run(
'UPDATE serials SET is_active = 0, updated_at = CURRENT_TIMESTAMP WHERE company_name = ?',
[decodedCompanyName]
);
res.json({
message: '企业已吊销,所有序列号已失效',
data: {
companyName: decodedCompanyName
}
});
} catch (error) {
console.error('吊销企业错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
module.exports = router;