This repository has been archived on 2026-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
todo/server/index.js

355 lines
10 KiB
JavaScript
Raw Normal View History

2025-06-13 06:04:40 +00:00
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 todos (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium',
completed BOOLEAN DEFAULT FALSE,
suspended BOOLEAN DEFAULT FALSE,
date DATE NOT NULL,
suspended_date DATE NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
`);
// 添加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');
}
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();
};
// 路由
// 验证密码
app.post('/api/auth', authenticatePassword, (req, res) => {
const token = jwt.sign({ authenticated: true }, JWT_SECRET, { expiresIn: '24h' });
res.json({ success: true, token });
});
// 验证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();
});
};
// 获取历史待办事项(一周之前的)
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);
const [rows] = await connection.execute(
'SELECT * FROM todos WHERE date < ? ORDER BY date DESC, priority DESC, created_at DESC',
[oneWeekAgo.toISOString().split('T')[0]]
);
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);
const [rows] = await connection.execute(
'SELECT * FROM todos WHERE date >= ? AND date <= ? AND suspended = FALSE ORDER BY date DESC, priority DESC, created_at DESC',
[oneWeekAgo.toISOString().split('T')[0], today.toISOString().split('T')[0]]
);
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 } = 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, priority, date) VALUES (?, ?, ?)',
[title, priority || 'medium', date]
);
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 } = req.body;
const connection = await pool.getConnection();
await connection.execute(
'UPDATE todos SET completed = ? WHERE id = ?',
[completed, 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.delete('/api/todos/:id', authenticateToken, async (req, res) => {
try {
const { id } = req.params;
const connection = await pool.getConnection();
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();
// 检查是否是今天的待办事项
const [todoCheck] = await connection.execute(
'SELECT * FROM todos WHERE id = ? AND date = ?',
[id, today]
);
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();
const [rows] = await connection.execute(
'SELECT * FROM todos WHERE suspended = TRUE ORDER BY suspended_date DESC, priority DESC, created_at DESC'
);
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();
// 检查是否是挂起的待办事项
const [todoCheck] = await connection.execute(
'SELECT * FROM todos WHERE id = ? AND suspended = TRUE',
[id]
);
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];
// 查找今天之前所有未完成且未挂起的待办事项
const [pendingTodos] = await connection.execute(
'SELECT * FROM todos WHERE date < ? AND completed = FALSE AND suspended = FALSE ORDER BY date DESC, priority DESC',
[today]
);
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();
});