|
|
|
@ -104,7 +104,7 @@ setInterval(async () => { |
|
|
|
// 批量更新商品状态为sold_out
|
|
|
|
for (const product of productsToOffline) { |
|
|
|
await connection.query( |
|
|
|
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?', |
|
|
|
'UPDATE products SET status = ?, label = 1, updated_at = NOW() WHERE id = ?', |
|
|
|
['sold_out', product.id] |
|
|
|
); |
|
|
|
|
|
|
|
@ -1320,11 +1320,18 @@ app.put('/api/supplies/:id', async (req, res) => { |
|
|
|
return sendResponse(res, false, null, '货源不存在'); |
|
|
|
} |
|
|
|
|
|
|
|
// 更新状态
|
|
|
|
await connection.query( |
|
|
|
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?', |
|
|
|
[status, productId] |
|
|
|
); |
|
|
|
// 更新状态,当状态为sold_out时同时锁定label
|
|
|
|
if (status === 'sold_out') { |
|
|
|
await connection.query( |
|
|
|
'UPDATE products SET status = ?, label = 1, updated_at = NOW() WHERE id = ?', |
|
|
|
[status, productId] |
|
|
|
); |
|
|
|
} else { |
|
|
|
await connection.query( |
|
|
|
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?', |
|
|
|
[status, productId] |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// 提交事务
|
|
|
|
await connection.commit(); |
|
|
|
@ -1631,9 +1638,183 @@ app.get('/api/suppliers', async (req, res) => { |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 首页路由
|
|
|
|
// 管理员统计 - 获取货源创建统计数据
|
|
|
|
app.get('/api/admin/stats/supplies', async (req, res) => { |
|
|
|
try { |
|
|
|
const { filter } = req.query; |
|
|
|
const connection = await pool.getConnection(); |
|
|
|
|
|
|
|
// 计算时间范围
|
|
|
|
let timeCondition = ''; |
|
|
|
const now = new Date(); |
|
|
|
|
|
|
|
if (filter === 'today') { |
|
|
|
// 今天
|
|
|
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); |
|
|
|
timeCondition = `AND created_at >= '${today.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'yesterday') { |
|
|
|
// 昨天
|
|
|
|
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); |
|
|
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); |
|
|
|
timeCondition = `AND created_at >= '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${today.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'beforeYesterday') { |
|
|
|
// 前天
|
|
|
|
const beforeYesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 2); |
|
|
|
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); |
|
|
|
timeCondition = `AND created_at >= '${beforeYesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'week') { |
|
|
|
// 本周
|
|
|
|
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); |
|
|
|
timeCondition = `AND created_at >= '${weekAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'month') { |
|
|
|
// 本月
|
|
|
|
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); |
|
|
|
timeCondition = `AND created_at >= '${monthAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'custom') { |
|
|
|
// 自定义时间范围
|
|
|
|
const { startDate, endDate } = req.query; |
|
|
|
if (startDate) { |
|
|
|
// 开始日期,格式化为YYYY-MM-DD 00:00:00
|
|
|
|
const start = new Date(startDate); |
|
|
|
start.setHours(0, 0, 0, 0); |
|
|
|
timeCondition += `AND created_at >= '${start.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} |
|
|
|
if (endDate) { |
|
|
|
// 结束日期,格式化为YYYY-MM-DD 23:59:59
|
|
|
|
const end = new Date(endDate); |
|
|
|
end.setHours(23, 59, 59, 999); |
|
|
|
timeCondition += `AND created_at <= '${end.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取每个卖家创建的货源数量,关联users表获取nickName
|
|
|
|
// 替换created_at为p.created_at,避免歧义
|
|
|
|
const chartTimeCondition = timeCondition.replace(/created_at/g, 'p.created_at'); |
|
|
|
const [chartData] = await connection.query(` |
|
|
|
SELECT p.sellerId, u.nickName, COUNT(*) as count |
|
|
|
FROM products p |
|
|
|
LEFT JOIN users u ON p.sellerId = u.userId |
|
|
|
WHERE 1=1 ${chartTimeCondition} |
|
|
|
GROUP BY p.sellerId, u.nickName |
|
|
|
ORDER BY count DESC |
|
|
|
`);
|
|
|
|
|
|
|
|
// 获取总体统计信息
|
|
|
|
const [totalSuppliesResult] = await connection.query(` |
|
|
|
SELECT COUNT(*) as total FROM products WHERE 1=1 ${timeCondition} |
|
|
|
`);
|
|
|
|
const totalSupplies = totalSuppliesResult[0].total; |
|
|
|
|
|
|
|
const [totalUsersResult] = await connection.query(` |
|
|
|
SELECT COUNT(DISTINCT sellerId) as total FROM products WHERE 1=1 ${timeCondition} |
|
|
|
`);
|
|
|
|
const totalUsers = totalUsersResult[0].total; |
|
|
|
|
|
|
|
const avgPerUser = totalUsers > 0 ? totalSupplies / totalUsers : 0; |
|
|
|
|
|
|
|
connection.release(); |
|
|
|
|
|
|
|
sendResponse(res, true, { |
|
|
|
chartData, |
|
|
|
stats: { |
|
|
|
totalSupplies, |
|
|
|
totalUsers, |
|
|
|
avgPerUser |
|
|
|
} |
|
|
|
}, '获取统计数据成功'); |
|
|
|
} catch (error) { |
|
|
|
console.error('获取统计数据失败:', error.message); |
|
|
|
console.error('错误详情:', error); |
|
|
|
sendResponse(res, false, null, '获取统计数据失败'); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 管理员统计 - 获取指定卖家的货源列表
|
|
|
|
app.get('/api/admin/supplies', async (req, res) => { |
|
|
|
try { |
|
|
|
const { sellerId, filter } = req.query; |
|
|
|
const connection = await pool.getConnection(); |
|
|
|
|
|
|
|
// 计算时间范围
|
|
|
|
let timeCondition = ''; |
|
|
|
const now = new Date(); |
|
|
|
|
|
|
|
if (filter === 'today') { |
|
|
|
// 今天
|
|
|
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); |
|
|
|
timeCondition = `AND created_at >= '${today.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'yesterday') { |
|
|
|
// 昨天
|
|
|
|
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); |
|
|
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); |
|
|
|
timeCondition = `AND created_at >= '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${today.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'beforeYesterday') { |
|
|
|
// 前天
|
|
|
|
const beforeYesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 2); |
|
|
|
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); |
|
|
|
timeCondition = `AND created_at >= '${beforeYesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'week') { |
|
|
|
// 本周
|
|
|
|
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); |
|
|
|
timeCondition = `AND created_at >= '${weekAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'month') { |
|
|
|
// 本月
|
|
|
|
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); |
|
|
|
timeCondition = `AND created_at >= '${monthAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} else if (filter === 'custom') { |
|
|
|
// 自定义时间范围
|
|
|
|
const { startDate, endDate } = req.query; |
|
|
|
if (startDate) { |
|
|
|
// 开始日期,格式化为YYYY-MM-DD 00:00:00
|
|
|
|
const start = new Date(startDate); |
|
|
|
start.setHours(0, 0, 0, 0); |
|
|
|
timeCondition += `AND created_at >= '${start.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} |
|
|
|
if (endDate) { |
|
|
|
// 结束日期,格式化为YYYY-MM-DD 23:59:59
|
|
|
|
const end = new Date(endDate); |
|
|
|
end.setHours(23, 59, 59, 999); |
|
|
|
timeCondition += `AND created_at <= '${end.toISOString().slice(0, 19).replace('T', ' ')}'`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取指定卖家的货源列表,关联users表获取创建人姓名
|
|
|
|
// 替换created_at为p.created_at,避免歧义
|
|
|
|
const suppliesTimeCondition = timeCondition.replace(/created_at/g, 'p.created_at'); |
|
|
|
const [supplies] = await connection.query(` |
|
|
|
SELECT p.*, u.nickName |
|
|
|
FROM products p |
|
|
|
LEFT JOIN users u ON p.sellerId = u.userId |
|
|
|
WHERE p.sellerId = ? ${suppliesTimeCondition} |
|
|
|
ORDER BY p.created_at DESC |
|
|
|
`, [sellerId]);
|
|
|
|
|
|
|
|
connection.release(); |
|
|
|
|
|
|
|
sendResponse(res, true, { supplies }, '获取货源列表成功'); |
|
|
|
} catch (error) { |
|
|
|
console.error('获取货源列表失败:', error.message); |
|
|
|
console.error('错误详情:', error); |
|
|
|
sendResponse(res, false, null, '获取货源列表失败'); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 首页路由 - 根据角色重定向
|
|
|
|
app.get('/', (req, res) => { |
|
|
|
res.sendFile(path.join(__dirname, 'Reject.html')); |
|
|
|
// 注意:这里无法直接获取localStorage中的用户信息,因为localStorage是客户端的
|
|
|
|
// 实际生产环境中,应该通过cookie或token来获取用户角色信息
|
|
|
|
// 这里我们默认跳转到登录页面,让客户端处理角色跳转
|
|
|
|
res.sendFile(path.join(__dirname, 'login.html')); |
|
|
|
}); |
|
|
|
|
|
|
|
// 管理员统计页面路由
|
|
|
|
app.get('/admin-stats', (req, res) => { |
|
|
|
res.sendFile(path.join(__dirname, 'Management.html')); |
|
|
|
}); |
|
|
|
|
|
|
|
// 货源管理页面路由
|
|
|
|
app.get('/management', (req, res) => { |
|
|
|
res.sendFile(path.join(__dirname, 'Management.html')); |
|
|
|
}); |
|
|
|
|
|
|
|
// 错误处理中间件
|
|
|
|
|