99 lines
3.2 KiB
JavaScript
99 lines
3.2 KiB
JavaScript
const express = require('express');
|
|||
|
|
const db = require('../config/database');
|
||
|
|
const { authenticate } = require('../middleware/auth');
|
||
|
|
const { asyncHandler, AppError } = require('../middleware/errorHandler');
|
||
|
|
const { validateCategoryPayload } = require('../utils/validators');
|
||
|
|
|
||
|
|
const router = express.Router();
|
||
|
|
|
||
|
|
const defaultCategories = {
|
||
|
|
expense: [
|
||
|
|
{ name: '餐饮', icon: '🍜' },
|
||
|
|
{ name: '买菜', icon: '🥬' },
|
||
|
|
{ name: '交通', icon: '🚗' },
|
||
|
|
{ name: '购物', icon: '🛒' },
|
||
|
|
{ name: '娱乐', icon: '🎮' },
|
||
|
|
{ name: '房租', icon: '🏠' },
|
||
|
|
{ name: '房贷', icon: '🏦' },
|
||
|
|
{ name: '水电', icon: '💧' },
|
||
|
|
{ name: '衣服', icon: '👔' },
|
||
|
|
{ name: '通讯', icon: '📱' },
|
||
|
|
{ name: '日用品', icon: '🧴' },
|
||
|
|
{ name: '美妆', icon: '💄' },
|
||
|
|
{ name: '医疗', icon: '🏥' },
|
||
|
|
{ name: '养娃', icon: '👶' },
|
||
|
|
{ name: '家电', icon: '📺' },
|
||
|
|
{ name: '家具', icon: '🛋️' },
|
||
|
|
{ name: '装修', icon: '🔨' },
|
||
|
|
{ name: '宠物', icon: '🐕' },
|
||
|
|
{ name: '其他', icon: '📝' },
|
||
|
|
],
|
||
|
|
income: [
|
||
|
|
{ name: '工资', icon: '💰' },
|
||
|
|
{ name: '奖金', icon: '🎁' },
|
||
|
|
{ name: '理财', icon: '📈' },
|
||
|
|
{ name: '红包', icon: '🧧' },
|
||
|
|
{ name: '其他', icon: '📝' },
|
||
|
|
],
|
||
|
|
};
|
||
|
|
|
||
|
|
// 获取分类列表
|
||
|
|
router.get('/', authenticate, asyncHandler(async (req, res) => {
|
||
|
|
const stmt = db.prepare('SELECT * FROM categories WHERE user_id = ? ORDER BY type, id');
|
||
|
|
const customCategories = stmt.all(req.userId);
|
||
|
|
|
||
|
|
// 合并默认分类和自定义分类
|
||
|
|
const categories = {
|
||
|
|
expense: [
|
||
|
|
...defaultCategories.expense.map((c) => ({ ...c, is_default: true })),
|
||
|
|
...customCategories.filter((c) => c.type === 'expense'),
|
||
|
|
],
|
||
|
|
income: [
|
||
|
|
...defaultCategories.income.map((c) => ({ ...c, is_default: true })),
|
||
|
|
...customCategories.filter((c) => c.type === 'income'),
|
||
|
|
],
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json(categories);
|
||
|
|
}));
|
||
|
|
|
||
|
|
// 新增自定义分类
|
||
|
|
router.post('/', authenticate, asyncHandler(async (req, res) => {
|
||
|
|
const { type, name, icon } = req.body;
|
||
|
|
|
||
|
|
const validationError = validateCategoryPayload(req.body);
|
||
|
|
if (validationError) {
|
||
|
|
throw new AppError(validationError, 400, 'VALIDATION_ERROR');
|
||
|
|
}
|
||
|
|
|
||
|
|
const stmt = db.prepare(`
|
||
|
|
INSERT INTO categories (user_id, type, name, icon, is_default)
|
||
|
|
VALUES (?, ?, ?, ?, 0)
|
||
|
|
`);
|
||
|
|
|
||
|
|
const result = stmt.run(req.userId, type, name.trim(), icon.trim());
|
||
|
|
console.log(`Category created: user=${req.userId}, id=${result.lastInsertRowid}`);
|
||
|
|
|
||
|
|
res.json({ id: result.lastInsertRowid, message: '添加成功' });
|
||
|
|
}));
|
||
|
|
|
||
|
|
// 删除自定义分类
|
||
|
|
router.delete('/:id', authenticate, asyncHandler(async (req, res) => {
|
||
|
|
const { id } = req.params;
|
||
|
|
|
||
|
|
const checkStmt = db.prepare('SELECT * FROM categories WHERE id = ? AND user_id = ? AND is_default = 0');
|
||
|
|
const category = checkStmt.get(id, req.userId);
|
||
|
|
|
||
|
|
if (!category) {
|
||
|
|
throw new AppError('分类不存在或无法删除', 404, 'NOT_FOUND');
|
||
|
|
}
|
||
|
|
|
||
|
|
const stmt = db.prepare('DELETE FROM categories WHERE id = ? AND user_id = ?');
|
||
|
|
stmt.run(id, req.userId);
|
||
|
|
console.log(`Category deleted: user=${req.userId}, id=${id}`);
|
||
|
|
|
||
|
|
res.json({ message: '删除成功' });
|
||
|
|
}));
|
||
|
|
|
||
|
|
module.exports = router;
|