348 lines
10 KiB
JavaScript
348 lines
10 KiB
JavaScript
|
|
const express = require('express');
|
||
|
|
const router = express.Router();
|
||
|
|
const { getDB } = require('../database');
|
||
|
|
const { auth, adminAuth } = require('../middleware/auth');
|
||
|
|
|
||
|
|
// 获取用户当前积分
|
||
|
|
router.get('/balance', auth, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const userId = req.user.id;
|
||
|
|
|
||
|
|
const [users] = await getDB().execute(
|
||
|
|
'SELECT points FROM users WHERE id = ?',
|
||
|
|
[userId]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (users.length === 0) {
|
||
|
|
return res.status(404).json({ success: false, message: '用户不存在' });
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: {
|
||
|
|
points: users[0].points
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('获取积分余额失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '获取积分余额失败' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// 获取用户积分历史记录
|
||
|
|
router.get('/history', auth, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { page = 1, limit = 10, type, username, change, startDate, endDate } = req.query;
|
||
|
|
|
||
|
|
// 确保参数为有效数字
|
||
|
|
const pageNum = parseInt(page) || 1;
|
||
|
|
const limitNum = parseInt(limit) || 10;
|
||
|
|
const offset = (pageNum - 1) * limitNum;
|
||
|
|
|
||
|
|
let whereClause = '';
|
||
|
|
let queryParams = [];
|
||
|
|
|
||
|
|
// 如果是管理员,可以查看所有用户的积分历史
|
||
|
|
if (req.user.role === 'admin') {
|
||
|
|
whereClause = 'WHERE 1=1';
|
||
|
|
|
||
|
|
// 按用户名筛选
|
||
|
|
if (username) {
|
||
|
|
whereClause += ' AND u.username LIKE ?';
|
||
|
|
queryParams.push(`%${username}%`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 按类型筛选
|
||
|
|
if (type) {
|
||
|
|
whereClause += ' AND ph.type = ?';
|
||
|
|
queryParams.push(type);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 按积分变化筛选
|
||
|
|
if (change === 'positive') {
|
||
|
|
whereClause += ' AND ph.amount > 0';
|
||
|
|
} else if (change === 'negative') {
|
||
|
|
whereClause += ' AND ph.amount < 0';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 按时间范围筛选
|
||
|
|
if (startDate) {
|
||
|
|
whereClause += ' AND DATE(ph.created_at) >= ?';
|
||
|
|
queryParams.push(startDate);
|
||
|
|
}
|
||
|
|
if (endDate) {
|
||
|
|
whereClause += ' AND DATE(ph.created_at) <= ?';
|
||
|
|
queryParams.push(endDate);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// 普通用户只能查看自己的积分历史
|
||
|
|
whereClause = 'WHERE ph.user_id = ?';
|
||
|
|
queryParams.push(req.user.id);
|
||
|
|
|
||
|
|
if (type && ['earn', 'spend'].includes(type)) {
|
||
|
|
whereClause += ' AND ph.type = ?';
|
||
|
|
queryParams.push(type);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取总数
|
||
|
|
const countQuery = req.user.role === 'admin'
|
||
|
|
? `SELECT COUNT(*) as total FROM points_history ph JOIN users u ON ph.user_id = u.id ${whereClause}`
|
||
|
|
: `SELECT COUNT(*) as total FROM points_history ph ${whereClause}`;
|
||
|
|
|
||
|
|
const [countResult] = await getDB().execute(countQuery, queryParams);
|
||
|
|
|
||
|
|
// 获取历史记录
|
||
|
|
const historyQuery = req.user.role === 'admin'
|
||
|
|
? `SELECT ph.id, ph.amount as points, ph.type, ph.description, ph.created_at,
|
||
|
|
u.username,
|
||
|
|
(SELECT points FROM users WHERE id = ph.user_id) as balance_after
|
||
|
|
FROM points_history ph
|
||
|
|
JOIN users u ON ph.user_id = u.id
|
||
|
|
${whereClause}
|
||
|
|
ORDER BY ph.created_at DESC
|
||
|
|
LIMIT ${limitNum} OFFSET ${offset}`
|
||
|
|
: `SELECT id, amount as points_change, type, description, created_at
|
||
|
|
FROM points_history ph
|
||
|
|
${whereClause}
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
LIMIT ${limitNum} OFFSET ${offset}`;
|
||
|
|
|
||
|
|
const [records] = await getDB().execute(historyQuery, queryParams);
|
||
|
|
|
||
|
|
const responseData = req.user.role === 'admin'
|
||
|
|
? {
|
||
|
|
history: records,
|
||
|
|
total: countResult[0].total
|
||
|
|
}
|
||
|
|
: {
|
||
|
|
records,
|
||
|
|
pagination: {
|
||
|
|
page: pageNum,
|
||
|
|
limit: limitNum,
|
||
|
|
total: countResult[0].total,
|
||
|
|
totalPages: Math.ceil(countResult[0].total / limitNum)
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: responseData
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('获取积分历史失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '获取积分历史失败' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// 管理员调整用户积分
|
||
|
|
router.post('/adjust', auth, adminAuth, async (req, res) => {
|
||
|
|
const connection = await getDB().getConnection();
|
||
|
|
|
||
|
|
try {
|
||
|
|
await connection.beginTransaction();
|
||
|
|
|
||
|
|
const { userId, points, reason } = req.body;
|
||
|
|
|
||
|
|
if (!userId || points === undefined || points === null || !reason) {
|
||
|
|
await connection.rollback();
|
||
|
|
return res.status(400).json({ success: false, message: '请提供有效的用户ID、积分数量和调整原因' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查用户是否存在
|
||
|
|
const [users] = await connection.execute(
|
||
|
|
'SELECT id, username, points FROM users WHERE id = ?',
|
||
|
|
[userId]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (users.length === 0) {
|
||
|
|
await connection.rollback();
|
||
|
|
return res.status(404).json({ success: false, message: '用户不存在' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const currentPoints = users[0].points;
|
||
|
|
const newPoints = currentPoints + points;
|
||
|
|
|
||
|
|
// 检查积分是否会变为负数
|
||
|
|
if (newPoints < 0) {
|
||
|
|
await connection.rollback();
|
||
|
|
return res.status(400).json({ success: false, message: '用户积分不足,无法扣除' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 更新用户积分
|
||
|
|
await connection.execute(
|
||
|
|
'UPDATE users SET points = ? WHERE id = ?',
|
||
|
|
[newPoints, userId]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 记录积分历史
|
||
|
|
await connection.execute(
|
||
|
|
`INSERT INTO points_history (user_id, amount, type, description, created_at)
|
||
|
|
VALUES (?, ?, 'admin_adjust', ?, NOW())`,
|
||
|
|
[userId, points, reason]
|
||
|
|
);
|
||
|
|
|
||
|
|
await connection.commit();
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
message: '积分调整成功',
|
||
|
|
data: {
|
||
|
|
userId: userId,
|
||
|
|
pointsChanged: points,
|
||
|
|
newBalance: newPoints
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
await connection.rollback();
|
||
|
|
console.error('积分调整失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '积分调整失败' });
|
||
|
|
} finally {
|
||
|
|
connection.release();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// 管理员给用户充值积分
|
||
|
|
router.post('/recharge', auth, adminAuth, async (req, res) => {
|
||
|
|
const connection = await getDB().getConnection();
|
||
|
|
|
||
|
|
try {
|
||
|
|
await connection.beginTransaction();
|
||
|
|
|
||
|
|
const { user_id, points, description = '管理员充值' } = req.body;
|
||
|
|
|
||
|
|
if (!user_id || !points || points <= 0) {
|
||
|
|
await connection.rollback();
|
||
|
|
return res.status(400).json({ success: false, message: '请提供有效的用户ID和积分数量' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查用户是否存在
|
||
|
|
const [users] = await connection.execute(
|
||
|
|
'SELECT id, username FROM users WHERE id = ?',
|
||
|
|
[user_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (users.length === 0) {
|
||
|
|
await connection.rollback();
|
||
|
|
return res.status(404).json({ success: false, message: '用户不存在' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 增加用户积分
|
||
|
|
await connection.execute(
|
||
|
|
'UPDATE users SET points = points + ? WHERE id = ?',
|
||
|
|
[points, user_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 记录积分历史
|
||
|
|
await connection.execute(
|
||
|
|
`INSERT INTO points_history (user_id, amount, type, description, created_at)
|
||
|
|
VALUES (?, ?, 'earn', ?, NOW())`,
|
||
|
|
[user_id, points, description]
|
||
|
|
);
|
||
|
|
|
||
|
|
await connection.commit();
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
message: '积分充值成功',
|
||
|
|
data: {
|
||
|
|
userId: user_id,
|
||
|
|
pointsAdded: points
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
await connection.rollback();
|
||
|
|
console.error('积分充值失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '积分充值失败' });
|
||
|
|
} finally {
|
||
|
|
connection.release();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
// 获取积分排行榜
|
||
|
|
router.get('/leaderboard', auth, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { limit = 10 } = req.query;
|
||
|
|
|
||
|
|
const [users] = await getDB().execute(
|
||
|
|
`SELECT id, username, points
|
||
|
|
FROM users
|
||
|
|
WHERE points > 0
|
||
|
|
ORDER BY points DESC
|
||
|
|
LIMIT ?`,
|
||
|
|
[parseInt(limit)]
|
||
|
|
);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: {
|
||
|
|
leaderboard: users.map((user, index) => ({
|
||
|
|
rank: index + 1,
|
||
|
|
userId: user.id,
|
||
|
|
username: user.username,
|
||
|
|
points: user.points
|
||
|
|
}))
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('获取积分排行榜失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '获取积分排行榜失败' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// 获取积分统计信息(管理员权限)
|
||
|
|
router.get('/stats', auth, adminAuth, async (req, res) => {
|
||
|
|
try {
|
||
|
|
// 总积分发放量
|
||
|
|
const [totalEarned] = await getDB().execute(
|
||
|
|
'SELECT SUM(amount) as total FROM points_history WHERE type = "earn"'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 总积分消费量
|
||
|
|
const [totalConsumed] = await getDB().execute(
|
||
|
|
'SELECT SUM(ABS(amount)) as total FROM points_history WHERE type = "spend"'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 本月积分发放
|
||
|
|
const [monthEarned] = await getDB().execute(
|
||
|
|
'SELECT SUM(amount) as total FROM points_history WHERE type = "earn" AND YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 上月积分发放(用于计算增长率)
|
||
|
|
const [lastMonthEarned] = await getDB().execute(
|
||
|
|
'SELECT SUM(amount) as total FROM points_history WHERE type = "earn" AND YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 计算月增长率
|
||
|
|
const lastMonthTotal = lastMonthEarned[0].total || 0;
|
||
|
|
const currentMonthTotal = monthEarned[0].total || 0;
|
||
|
|
let monthGrowthRate = 0;
|
||
|
|
if (lastMonthTotal > 0) {
|
||
|
|
monthGrowthRate = ((currentMonthTotal - lastMonthTotal) / lastMonthTotal * 100).toFixed(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 活跃用户数(有积分记录的用户)
|
||
|
|
const [activeUsers] = await getDB().execute(
|
||
|
|
'SELECT COUNT(DISTINCT user_id) as count FROM points_history'
|
||
|
|
);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: {
|
||
|
|
stats: {
|
||
|
|
totalPoints: totalEarned[0].total || 0,
|
||
|
|
totalEarned: totalEarned[0].total || 0,
|
||
|
|
totalSpent: totalConsumed[0].total || 0,
|
||
|
|
activeUsers: activeUsers[0].count
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('获取积分统计失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '获取积分统计失败' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
module.exports = router;
|