从零打造智能待办事项管理系统:Node.js + SQLite 全栈实战
折
折腾侠
2026/03/27 发布
17约 11 分钟1327 字 / 1534 词00
从零打造智能待办事项管理系统:Node.js + SQLite 全栈实战
项目概述
在现代快节奏的工作和生活中,高效的任务管理变得愈发重要。本文将带你从零开始,使用 Node.js 和 SQLite 构建一个功能完整的智能待办事项管理系统。这个项目不仅适合初学者学习全栈开发,也适合作为个人日常使用的效率工具。
项目亮点
- 🎯 智能优先级排序:根据截止日期和重要性自动计算任务优先级
- 📊 数据统计面板:可视化展示任务完成情况和效率分析
- 🔔 智能提醒系统:支持多种通知方式,确保重要任务不被遗漏
- 💾 轻量级存储:使用 SQLite,无需额外数据库服务
- 🚀 RESTful API:规范的接口设计,便于扩展和集成
技术栈选择
后端技术
| 技术 | 版本 | 选择理由 |
|---|---|---|
| Node.js | v20+ | 高性能异步运行时,生态丰富 |
| Express | v4.x | 轻量级 Web 框架,易于上手 |
| SQLite3 | v5.x | 零配置数据库,适合小型项目 |
| Better-sqlite3 | v9.x | 同步 API,代码更简洁 |
前端技术
| 技术 | 版本 | 选择理由 |
|---|---|---|
| HTML5/CSS3 | - | 基础网页结构 |
| JavaScript (ES6+) | - | 现代语法,更好的开发体验 |
| Chart.js | v4.x | 轻量级图表库,数据可视化 |
| Axios | v1.x | 简洁的 HTTP 客户端 |
开发工具
- 包管理器:npm 或 pnpm
- 代码编辑器:VS Code
- API 测试:Postman 或 Insomnia
- 版本控制:Git
项目结构设计
smart-todo/
├── src/
│ ├── controllers/
│ │ └── taskController.js # 任务控制器
│ ├── models/
│ │ └── database.js # 数据库配置
│ ├── routes/
│ │ └── tasks.js # 路由定义
│ ├── middleware/
│ │ └── validation.js # 请求验证
│ ├── utils/
│ │ └── priorityCalculator.js # 优先级计算工具
│ └── app.js # 应用入口
├── public/
│ ├── index.html # 主页面
│ ├── css/
│ │ └── style.css # 样式文件
│ └── js/
│ └── app.js # 前端逻辑
├── tests/
│ └── api.test.js # API 测试
├── .env # 环境变量
├── .gitignore # Git 忽略文件
└── package.json # 项目配置
核心功能实现
1. 数据库设计
首先,我们设计 SQLite 数据库的表结构。任务表需要包含以下字段:
JavaScript
// src/models/database.js
const Database = require('better-sqlite3');
const path = require('path');
const db = new Database(path.join(__dirname, '../../data/todo.db'));
// 创建任务表
db.exec(`
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
priority INTEGER DEFAULT 3,
status TEXT DEFAULT 'pending',
due_date DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
completed_at DATETIME,
category TEXT DEFAULT 'general',
tags TEXT
)
`);
// 创建索引以优化查询
db.exec(`
CREATE INDEX IF NOT EXISTS idx_status ON tasks(status);
CREATE INDEX IF NOT EXISTS idx_due_date ON tasks(due_date);
CREATE INDEX IF NOT EXISTS idx_priority ON tasks(priority);
`);
module.exports = db;
2. 优先级计算算法
智能优先级的核心在于根据多个因素动态计算任务的重要程度:
JavaScript
// src/utils/priorityCalculator.js
class PriorityCalculator {
// 优先级权重配置
static WEIGHTS = {
urgency: 0.4, // 紧急程度权重
importance: 0.4, // 重要程度权重
effort: 0.2 // 所需精力权重
};
/**
* 计算任务优先级分数 (1-10)
* @param {Object} task - 任务对象
* @returns {number} 优先级分数
*/
static calculate(task) {
const urgencyScore = this.calculateUrgency(task.due_date);
const importanceScore = this.calculateImportance(task.category);
const effortScore = this.calculateEffort(task.description);
const finalScore =
urgencyScore * this.WEIGHTS.urgency +
importanceScore * this.WEIGHTS.importance +
effortScore * this.WEIGHTS.effort;
return Math.round(finalScore * 10) / 10;
}
/**
* 计算紧急程度分数
*/
static calculateUrgency(dueDate) {
if (!dueDate) return 5; // 无截止日期默认为中等
const now = new Date();
const due = new Date(dueDate);
const hoursRemaining = (due - now) / (1000 * 60 * 60);
if (hoursRemaining < 0) return 10; // 已过期
if (hoursRemaining < 24) return 9; // 24 小时内
if (hoursRemaining < 48) return 7; // 48 小时内
if (hoursRemaining < 168) return 5; // 1 周内
if (hoursRemaining < 336) return 3; // 2 周内
return 2; // 2 周以上
}
/**
* 计算重要程度分数
*/
static calculateImportance(category) {
const importanceMap = {
'work': 8,
'health': 9,
'family': 8,
'finance': 7,
'learning': 6,
'general': 5
};
return importanceMap[category] || 5;
}
/**
* 计算所需精力分数(反向:精力越少优先级越高)
*/
static calculateEffort(description) {
if (!description) return 5;
const wordCount = description.split(' ').length;
if (wordCount < 10) return 8; // 简单任务
if (wordCount < 30) return 6; // 中等任务
if (wordCount < 50) return 4; // 复杂任务
return 2; // 非常复杂
}
}
module.exports = PriorityCalculator;
3. 任务控制器
控制器负责处理业务逻辑,协调模型和视图:
JavaScript
// src/controllers/taskController.js
const db = require('../models/database');
const PriorityCalculator = require('../utils/priorityCalculator');
class TaskController {
/**
* 获取所有任务
*/
static getAll(req, res) {
const { status, category, sortBy = 'priority' } = req.query;
let query = 'SELECT * FROM tasks WHERE 1=1';
const params = [];
if (status) {
query += ' AND status = ?';
params.push(status);
}
if (category) {
query += ' AND category = ?';
params.push(category);
}
// 排序选项
const validSorts = ['priority', 'due_date', 'created_at'];
const order = validSorts.includes(sortBy) ? sortBy : 'priority';
query += ` ORDER BY ${order} DESC`;
const tasks = db.prepare(query).all(...params);
res.json({
success: true,
count: tasks.length,
data: tasks
});
}
/**
* 创建新任务
*/
static create(req, res) {
const { title, description, due_date, category, tags } = req.body;
// 验证必填字段
if (!title || title.trim().length === 0) {
return res.status(400).json({
success: false,
message: '任务标题不能为空'
});
}
// 计算初始优先级
const priority = PriorityCalculator.calculate({
due_date,
category,
description
});
const stmt = db.prepare(`
INSERT INTO tasks (title, description, priority, due_date, category, tags)
VALUES (?, ?, ?, ?, ?, ?)
`);
const result = stmt.run(
title.trim(),
description || null,
priority,
due_date || null,
category || 'general',
tags ? JSON.stringify(tags) : null
);
const newTask = db.prepare('SELECT * FROM tasks WHERE id = ?').get(result.lastInsertRowid);
res.status(201).json({
success: true,
message: '任务创建成功',
data: newTask
});
}
/**
* 更新任务状态
*/
static updateStatus(req, res) {
const { id } = req.params;
const { status } = req.body;
const validStatuses = ['pending', 'in_progress', 'completed', 'cancelled'];
if (!validStatuses.includes(status)) {
return res.status(400).json({
success: false,
message: '无效的状态值'
});
}
const completed_at = status === 'completed' ? new Date().toISOString() : null;
const stmt = db.prepare(`
UPDATE tasks
SET status = ?, completed_at = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const result = stmt.run(status, completed_at, id);
if (result.changes === 0) {
return res.status(404).json({
success: false,
message: '任务不存在'
});
}
const updatedTask = db.prepare('SELECT * FROM tasks WHERE id = ?').get(id);
res.json({
success: true,
message: '任务状态已更新',
data: updatedTask
});
}
/**
* 删除任务
*/
static delete(req, res) {
const { id } = req.params;
const stmt = db.prepare('DELETE FROM tasks WHERE id = ?');
const result = stmt.run(id);
if (result.changes === 0) {
return res.status(404).json({
success: false,
message: '任务不存在'
});
}
res.json({
success: true,
message: '任务已删除'
});
}
/**
* 获取统计信息
*/
static getStats(req, res) {
const stats = {};
// 按状态统计
stats.byStatus = db.prepare(`
SELECT status, COUNT(*) as count
FROM tasks
GROUP BY status
`).all();
// 按分类统计
stats.byCategory = db.prepare(`
SELECT category, COUNT(*) as count
FROM tasks
GROUP BY category
`).all();
// 完成情况
const total = db.prepare('SELECT COUNT(*) as count FROM tasks').get();
const completed = db.prepare(
'SELECT COUNT(*) as count FROM tasks WHERE status = "completed"'
).get();
stats.completionRate = total.count > 0
? Math.round((completed.count / total.count) * 100)
: 0;
// 即将到期任务
stats.upcomingDue = db.prepare(`
SELECT * FROM tasks
WHERE status != 'completed'
AND due_date IS NOT NULL
AND due_date <= datetime('now', '+7 days')
ORDER BY due_date ASC
LIMIT 5
`).all();
res.json({
success: true,
data: stats
});
}
}
module.exports = TaskController;
4. API 路由定义
JavaScript
// src/routes/tasks.js
const express = require('express');
const router = express.Router();
const TaskController = require('../controllers/taskController');
// 获取所有任务
router.get('/', TaskController.getAll);
// 获取统计信息
router.get('/stats', TaskController.getStats);
// 创建新任务
router.post('/', TaskController.create);
// 获取单个任务
router.get('/:id', (req, res) => {
const task = require('../models/database')
.prepare('SELECT * FROM tasks WHERE id = ?')
.get(req.params.id);
if (!task) {
return res.status(404).json({ success: false, message: '任务不存在' });
}
res.json({ success: true, data: task });
});
// 更新任务
router.put('/:id', (req, res) => {
// 实现更新逻辑
});
// 更新任务状态
router.patch('/:id/status', TaskController.updateStatus);
// 删除任务
router.delete('/:id', TaskController.delete);
module.exports = router;
5. 主应用入口
JavaScript
// src/app.js
const express = require('express');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const taskRoutes = require('./routes/tasks');
const app = express();
const PORT = process.env.PORT || 3000;
// 确保数据目录存在
const dataDir = path.join(__dirname, '../data');
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
// 中间件
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, '../public')));
// API 路由
app.use('/api/tasks', taskRoutes);
// 健康检查
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// 404 处理
app.use((req, res) => {
res.status(404).json({ success: false, message: '接口不存在' });
});
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
});
app.listen(PORT, () => {
console.log(`🚀 智能待办系统运行在 http://localhost:${PORT}`);
});
module.exports = app;
前端界面实现
HTML 主页面
HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能待办事项管理系统</title>
<link rel="stylesheet" href="/css/style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="container">
<header>
<h1>📋 智能待办事项管理系统</h1>
<div class="stats-bar" id="statsBar"></div>
</header>
<main>
<!-- 添加任务表单 -->
<section class="add-task-section">
<h2>添加新任务</h2>
<form id="taskForm">
<input type="text" id="title" placeholder="任务标题" required>
<textarea id="description" placeholder="任务描述(可选)"></textarea>
<select id="category">
<option value="general">一般</option>
<option value="work">工作</option>
<option value="health">健康</option>
<option value="family">家庭</option>
<option value="finance">财务</option>
<option value="learning">学习</option>
</select>
<input type="datetime-local" id="dueDate">
<button type="submit">添加任务</button>
</form>
</section>
<!-- 任务列表 -->
<section class="task-list-section">
<div class="filters">
<select id="statusFilter">
<option value="">全部状态</option>
<option value="pending">待处理</option>
<option value="in_progress">进行中</option>
<option value="completed">已完成</option>
</select>
<select id="sortFilter">
<option value="priority">按优先级</option>
<option value="due_date">按截止日期</option>
<option value="created_at">按创建时间</option>
</select>
</div>
<div id="taskList"></div>
</section>
<!-- 统计图表 -->
<section class="stats-section">
<h2>数据统计</h2>
<div class="charts">
<canvas id="statusChart"></canvas>
<canvas id="categoryChart"></canvas>
</div>
</section>
</main>
</div>
<script src="/js/app.js"></script>
</body>
</html>
运行步骤
第一步:环境准备
确保已安装 Node.js (v20+) 和 npm:
Bash
# 检查 Node.js 版本
node -v
# 检查 npm 版本
npm -v
第二步:项目初始化
Bash
# 创建项目目录
mkdir smart-todo
cd smart-todo
# 初始化 npm 项目
npm init -y
# 安装依赖
npm install express better-sqlite3 cors dotenv
npm install --save-dev nodemon jest supertest
第三步:创建项目结构
Bash
# 创建目录结构
mkdir -p src/{controllers,models,routes,middleware,utils}
mkdir -p public/{css,js}
mkdir -p tests
touch data/.gitkeep
第四步:配置文件
创建 INLINE_CODE_0 文件:
.env
PORT=3000
NODE_ENV=development
DB_PATH=./data/todo.db
第五步:启动应用
Bash
# 开发模式(支持热重载)
npm run dev
# 生产模式
npm start
访问 INLINE_CODE_1 即可查看应用界面。
项目扩展建议
短期扩展
- 用户认证系统:添加 JWT 认证,支持多用户
- 邮件提醒:集成 Nodemailer,到期前发送邮件通知
- 任务子项:支持将大任务拆分为多个子任务
- 标签系统:完善标签功能,支持多标签筛选
长期扩展
- 移动端应用:使用 React Native 开发跨平台移动应用
- 团队协作:支持任务分配和团队看板
- AI 智能建议:基于历史数据推荐最佳执行时间
- 第三方集成:对接日历、Slack、钉钉等工具
总结
通过这个项目,我们完成了一个功能完整的智能待办事项管理系统。项目涵盖了:
- ✅ RESTful API 设计规范
- ✅ SQLite 数据库操作
- ✅ 优先级算法实现
- ✅ 前后端分离架构
- ✅ 数据可视化展示
这个项目可以作为学习全栈开发的实践案例,也可以作为个人效率工具使用。代码结构清晰,易于扩展和维护。
项目源码已上传至 GitHub,欢迎 Star 和 Fork!
作者:折腾虾 | 发布时间:2026 年 3 月 | 分类:<a href="/categories/projects" title="项目实战" class="auto-link">项目实战