const express = require('express'); const mysql = require('mysql2/promise'); const cors = require('cors'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 5000; const JWT_SECRET = 'weiMonkey2024_secret_key'; // 中间件 app.use(cors()); app.use(express.json()); // 数据库连接配置 const dbConfig = { host: 'localhost', port: 3306, user: 'root', password: '123456', database: 'myuser' }; // 创建数据库连接池 const pool = mysql.createPool(dbConfig); // 初始化数据库表 async function initDatabase() { try { const connection = await pool.getConnection(); // 创建用户表 await connection.execute(` CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(64) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, is_admin BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // 创建待办事项表 await connection.execute(` CREATE TABLE IF NOT EXISTS todos ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, description TEXT, priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium', completed BOOLEAN DEFAULT FALSE, suspended BOOLEAN DEFAULT FALSE, date DATE NOT NULL, suspended_date DATE NULL, user_id INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) `); // 添加user_id字段(如果表已存在但没有该字段) try { await connection.execute(` ALTER TABLE todos ADD COLUMN user_id INT NOT NULL, ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE `); } catch (error) { // 字段可能已存在,忽略错误 console.log('User_id column may already exist'); } // 添加suspended字段(如果表已存在但没有该字段) try { await connection.execute(` ALTER TABLE todos ADD COLUMN suspended BOOLEAN DEFAULT FALSE, ADD COLUMN suspended_date DATE NULL `); } catch (error) { // 字段可能已存在,忽略错误 console.log('Suspended columns may already exist'); } // 添加description字段(如果表已存在但没有该字段) try { await connection.execute(` ALTER TABLE todos ADD COLUMN description TEXT `); } catch (error) { // 字段可能已存在,忽略错误 console.log('Description column may already exist'); } // 创建默认管理员用户 const adminPassword = await bcrypt.hash('weiMonkey2024', 10); try { await connection.execute(` INSERT INTO users (username, password, is_admin) VALUES ('admin', ?, TRUE) `, [adminPassword]); console.log('默认管理员用户创建成功'); } catch (error) { // 用户可能已存在 console.log('默认管理员用户可能已存在'); } connection.release(); console.log('数据库表初始化成功'); } catch (error) { console.error('数据库初始化失败:', error); } } // 验证密码的中间件 const authenticatePassword = (req, res, next) => { const { password } = req.body; const correctPassword = 'weiMonkey2024'; if (password !== correctPassword) { return res.status(401).json({ error: '密码错误' }); } next(); }; // 验证token的中间件 const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.status(401).json({ error: '需要认证' }); } jwt.verify(token, JWT_SECRET, (err, user) => { if (err) { return res.status(403).json({ error: '无效的token' }); } req.user = user; next(); }); }; // 验证管理员权限的中间件 const authenticateAdmin = (req, res, next) => { if (!req.user.is_admin) { return res.status(403).json({ error: '需要管理员权限' }); } next(); }; // 路由 // 用户登录 app.post('/api/auth/login', async (req, res) => { try { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: '用户名和密码是必需的' }); } const connection = await pool.getConnection(); // 查找用户 const [users] = await connection.execute( 'SELECT * FROM users WHERE username = ?', [username] ); connection.release(); if (users.length === 0) { return res.status(401).json({ error: '用户名或密码错误' }); } const user = users[0]; // 验证密码 const isValidPassword = await bcrypt.compare(password, user.password); if (!isValidPassword) { return res.status(401).json({ error: '用户名或密码错误' }); } // 生成token const token = jwt.sign( { userId: user.id, username: user.username, is_admin: user.is_admin }, JWT_SECRET, { expiresIn: '24h' } ); res.json({ success: true, token, user: { id: user.id, username: user.username, is_admin: user.is_admin } }); } catch (error) { console.error('登录失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 管理员添加用户 app.post('/api/auth/register', authenticateToken, authenticateAdmin, async (req, res) => { try { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: '用户名和密码是必需的' }); } if (password.length < 6) { return res.status(400).json({ error: '密码长度至少6位' }); } const connection = await pool.getConnection(); // 检查用户名是否已存在 const [existingUsers] = await connection.execute( 'SELECT id FROM users WHERE username = ?', [username] ); if (existingUsers.length > 0) { connection.release(); return res.status(400).json({ error: '用户名已存在' }); } // 创建新用户 const hashedPassword = await bcrypt.hash(password, 10); const [result] = await connection.execute( 'INSERT INTO users (username, password, is_admin) VALUES (?, ?, FALSE)', [username, hashedPassword] ); connection.release(); res.json({ success: true, message: '用户创建成功', userId: result.insertId }); } catch (error) { console.error('注册用户失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 获取所有用户(仅管理员) app.get('/api/users', authenticateToken, authenticateAdmin, async (req, res) => { try { const connection = await pool.getConnection(); const [users] = await connection.execute( 'SELECT id, username, is_admin, created_at FROM users ORDER BY created_at DESC' ); connection.release(); res.json(users); } catch (error) { console.error('获取用户列表失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 获取历史待办事项(一周之前的) app.get('/api/todos/history', authenticateToken, async (req, res) => { try { const connection = await pool.getConnection(); // 获取一周之前的历史数据 const today = new Date(); const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); // 管理员可以查看所有用户的数据,普通用户只能查看自己的 let query = 'SELECT * FROM todos WHERE date < ? AND user_id = ? ORDER BY date DESC, priority DESC, created_at DESC'; let params = [oneWeekAgo.toISOString().split('T')[0]]; params.push(req.user.userId); const [rows] = await connection.execute(query, params); connection.release(); res.json(rows); } catch (error) { console.error('获取历史待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 获取最近一周的待办事项(排除挂起的) app.get('/api/todos', authenticateToken, async (req, res) => { try { const connection = await pool.getConnection(); // 获取最近一周的日期范围 const today = new Date(); const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); // 管理员可以查看所有用户的数据,普通用户只能查看自己的 let query = 'SELECT * FROM todos WHERE date >= ? AND date <= ? AND suspended = FALSE AND user_id = ? ORDER BY date DESC, priority DESC, created_at DESC'; let params = [oneWeekAgo.toISOString().split('T')[0], today.toISOString().split('T')[0]]; params.push(req.user.userId); const [rows] = await connection.execute(query, params); connection.release(); res.json(rows); } catch (error) { console.error('获取待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 创建新的待办事项 app.post('/api/todos', authenticateToken, async (req, res) => { try { const { title, priority, date, description } = req.body; if (!title || !date) { return res.status(400).json({ error: '标题和日期是必需的' }); } const connection = await pool.getConnection(); const [result] = await connection.execute( 'INSERT INTO todos (title, description, priority, date, user_id) VALUES (?, ?, ?, ?, ?)', [title, description || null, priority || 'medium', date, req.user.userId] ); const [newTodo] = await connection.execute( 'SELECT * FROM todos WHERE id = ?', [result.insertId] ); connection.release(); res.status(201).json(newTodo[0]); } catch (error) { console.error('创建待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 更新待办事项状态 app.put('/api/todos/:id', authenticateToken, async (req, res) => { try { const { id } = req.params; const { completed, title, priority, date, description } = req.body; const connection = await pool.getConnection(); // 检查待办事项是否存在且属于当前用户(管理员可以修改所有) let checkQuery = 'SELECT * FROM todos WHERE id = ?'; let checkParams = [id]; if (!req.user.is_admin) { checkQuery = 'SELECT * FROM todos WHERE id = ? AND user_id = ?'; checkParams.push(req.user.userId); } const [existingTodos] = await connection.execute(checkQuery, checkParams); if (existingTodos.length === 0) { connection.release(); return res.status(404).json({ error: '待办事项不存在或无权限访问' }); } // 构建更新字段和值 const updates = []; const values = []; if (completed !== undefined) { updates.push('completed = ?'); values.push(completed); } if (title !== undefined) { updates.push('title = ?'); values.push(title); } if (priority !== undefined) { updates.push('priority = ?'); values.push(priority); } if (date !== undefined) { updates.push('date = ?'); values.push(date); } if (description !== undefined) { updates.push('description = ?'); values.push(description || null); } if (updates.length === 0) { connection.release(); return res.status(400).json({ error: '没有提供要更新的字段' }); } // 添加 id 到值数组 values.push(id); // 执行更新 await connection.execute( `UPDATE todos SET ${updates.join(', ')} WHERE id = ?`, values ); // 获取更新后的待办事项 const [updatedTodo] = await connection.execute( 'SELECT * FROM todos WHERE id = ?', [id] ); connection.release(); res.json(updatedTodo[0]); } catch (error) { console.error('更新待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 删除待办事项 app.delete('/api/todos/:id', authenticateToken, async (req, res) => { try { const { id } = req.params; const connection = await pool.getConnection(); // 检查待办事项是否存在且属于当前用户(管理员可以删除所有) let checkQuery = 'SELECT * FROM todos WHERE id = ?'; let checkParams = [id]; if (!req.user.is_admin) { checkQuery = 'SELECT * FROM todos WHERE id = ? AND user_id = ?'; checkParams.push(req.user.userId); } const [existingTodos] = await connection.execute(checkQuery, checkParams); if (existingTodos.length === 0) { connection.release(); return res.status(404).json({ error: '待办事项不存在或无权限访问' }); } await connection.execute('DELETE FROM todos WHERE id = ?', [id]); connection.release(); res.json({ success: true }); } catch (error) { console.error('删除待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 挂起今天的待办事项 app.put('/api/todos/:id/suspend', authenticateToken, async (req, res) => { try { const { id } = req.params; const today = new Date().toISOString().split('T')[0]; const connection = await pool.getConnection(); // 检查是否是今天的待办事项且属于当前用户 let checkQuery = 'SELECT * FROM todos WHERE id = ? AND date = ? AND user_id = ?'; let checkParams = [id, today]; checkParams.push(req.user.userId); const [todoCheck] = await connection.execute(checkQuery, checkParams); if (todoCheck.length === 0) { connection.release(); return res.status(400).json({ error: '只能挂起今天的待办事项或无权限访问' }); } // 更新挂起状态 await connection.execute( 'UPDATE todos SET suspended = TRUE, suspended_date = ? WHERE id = ?', [today, id] ); const [updatedTodo] = await connection.execute( 'SELECT * FROM todos WHERE id = ?', [id] ); connection.release(); res.json(updatedTodo[0]); } catch (error) { console.error('挂起待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 获取所有挂起的待办事项 app.get('/api/todos/suspended', authenticateToken, async (req, res) => { try { const connection = await pool.getConnection(); // 管理员可以查看所有用户的挂起待办,普通用户只能查看自己的 let query = 'SELECT * FROM todos WHERE suspended = TRUE AND user_id = ? ORDER BY suspended_date DESC, priority DESC, created_at DESC'; let params = []; params.push(req.user.userId); const [rows] = await connection.execute(query, params); connection.release(); res.json(rows); } catch (error) { console.error('获取挂起待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 恢复挂起的待办事项到今天 app.put('/api/todos/:id/resume', authenticateToken, async (req, res) => { try { const { id } = req.params; const today = new Date().toISOString().split('T')[0]; const connection = await pool.getConnection(); // 检查是否是挂起的待办事项且属于当前用户 let checkQuery = 'SELECT * FROM todos WHERE id = ? AND suspended = TRUE AND user_id = ?'; let checkParams = [id]; checkParams.push(req.user.userId); const [todoCheck] = await connection.execute(checkQuery, checkParams); if (todoCheck.length === 0) { connection.release(); return res.status(400).json({ error: '待办事项未处于挂起状态或无权限访问' }); } // 恢复待办事项到今天 await connection.execute( 'UPDATE todos SET suspended = FALSE, suspended_date = NULL, date = ?, completed = FALSE WHERE id = ?', [today, id] ); const [updatedTodo] = await connection.execute( 'SELECT * FROM todos WHERE id = ?', [id] ); connection.release(); res.json(updatedTodo[0]); } catch (error) { console.error('恢复待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 将历史未完成待办迁移到今天 app.post('/api/todos/migrate-pending', authenticateToken, async (req, res) => { try { const connection = await pool.getConnection(); // 获取今天的日期 const today = new Date().toISOString().split('T')[0]; // 查找今天之前所有未完成且未挂起的待办事项 let query = 'SELECT * FROM todos WHERE date < ? AND completed = FALSE AND suspended = FALSE AND user_id = ? ORDER BY date DESC, priority DESC'; let params = [today]; params.push(req.user.userId); const [pendingTodos] = await connection.execute(query, params); let migratedCount = 0; // 将每个未完成的待办事项迁移到今天 for (const todo of pendingTodos) { await connection.execute( 'UPDATE todos SET date = ? WHERE id = ?', [today, todo.id] ); migratedCount++; } connection.release(); res.json({ success: true, migratedCount, message: `已迁移 ${migratedCount} 个未完成的待办事项到今天` }); } catch (error) { console.error('迁移待办事项失败:', error); res.status(500).json({ error: '服务器错误' }); } }); // 启动服务器 app.listen(PORT, async () => { console.log(`服务器运行在端口 ${PORT}`); await initDatabase(); });