124 lines
3.0 KiB
JavaScript
124 lines
3.0 KiB
JavaScript
const express = require('express');
|
|
const db = require('../config/database');
|
|
const { authenticate } = require('../middleware/auth');
|
|
const { asyncHandler, AppError } = require('../middleware/errorHandler');
|
|
const { isValidMonth, VALID_STATS_PERIODS, VALID_STATS_TYPES } = require('../utils/validators');
|
|
|
|
const router = express.Router();
|
|
|
|
// 获取月度统计
|
|
router.get('/', authenticate, asyncHandler(async (req, res) => {
|
|
const { month } = req.query;
|
|
|
|
if (!month) {
|
|
throw new AppError('请指定月份', 400, 'MISSING_MONTH');
|
|
}
|
|
|
|
const stmt = db.prepare(`
|
|
SELECT
|
|
SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END) as totalIncome,
|
|
SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END) as totalExpense
|
|
FROM records
|
|
WHERE user_id = ? AND date LIKE ?
|
|
`);
|
|
|
|
const result = stmt.get(req.userId, `${month}%`);
|
|
|
|
res.json({
|
|
income: result.totalIncome || 0,
|
|
expense: result.totalExpense || 0,
|
|
balance: (result.totalIncome || 0) - (result.totalExpense || 0),
|
|
});
|
|
}));
|
|
|
|
// 收支趋势统计
|
|
router.get('/trend', authenticate, asyncHandler(async (req, res) => {
|
|
const { period = 'month', month } = req.query;
|
|
|
|
if (!VALID_STATS_PERIODS.includes(period)) {
|
|
throw new AppError('统计周期错误', 400, 'INVALID_PERIOD');
|
|
}
|
|
|
|
if (month && !isValidMonth(month)) {
|
|
throw new AppError('月份格式错误', 400, 'INVALID_MONTH');
|
|
}
|
|
|
|
let dateGroup;
|
|
if (period === 'year') {
|
|
dateGroup = "strftime('%Y', date)";
|
|
} else if (period === 'week') {
|
|
dateGroup = "strftime('%Y-%W', date)";
|
|
} else {
|
|
dateGroup = "strftime('%Y-%m', date)";
|
|
}
|
|
|
|
let query = `
|
|
SELECT
|
|
${dateGroup} as period,
|
|
SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END) as income,
|
|
SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END) as expense
|
|
FROM records
|
|
WHERE user_id = ?
|
|
`;
|
|
const params = [req.userId];
|
|
|
|
if (month) {
|
|
query += ' AND date LIKE ?';
|
|
params.push(`${month}%`);
|
|
}
|
|
|
|
query += `
|
|
GROUP BY ${dateGroup}
|
|
ORDER BY period DESC
|
|
LIMIT 12
|
|
`;
|
|
|
|
const stmt = db.prepare(query);
|
|
const result = stmt.all(...params);
|
|
res.json(result.reverse());
|
|
}));
|
|
|
|
// 分类统计
|
|
router.get('/category', authenticate, asyncHandler(async (req, res) => {
|
|
const { month, type = 'all' } = req.query;
|
|
|
|
if (!VALID_STATS_TYPES.includes(type)) {
|
|
throw new AppError('统计类型错误', 400, 'INVALID_TYPE');
|
|
}
|
|
|
|
if (month && !isValidMonth(month)) {
|
|
throw new AppError('月份格式错误', 400, 'INVALID_MONTH');
|
|
}
|
|
|
|
let query = `
|
|
SELECT
|
|
category,
|
|
SUM(amount) as total,
|
|
type
|
|
FROM records
|
|
WHERE user_id = ?
|
|
`;
|
|
const params = [req.userId];
|
|
|
|
if (month) {
|
|
query += ' AND date LIKE ?';
|
|
params.push(`${month}%`);
|
|
}
|
|
|
|
if (type !== 'all') {
|
|
query += ' AND type = ?';
|
|
params.push(type);
|
|
}
|
|
|
|
query += `
|
|
GROUP BY category, type
|
|
ORDER BY total DESC
|
|
`;
|
|
|
|
const stmt = db.prepare(query);
|
|
const result = stmt.all(...params);
|
|
res.json(result);
|
|
}));
|
|
|
|
module.exports = router;
|