折腾侠
项目实战

从零打造智能待办事项管理系统:Node.js + SQLite 全栈实战

折腾侠
2026/03/27 发布
17约 11 分钟1327 字 / 1534 词00

从零打造智能待办事项管理系统:Node.js + SQLite 全栈实战

项目概述

在现代快节奏的工作和生活中,高效的任务管理变得愈发重要。本文将带你从零开始,使用 Node.js 和 SQLite 构建一个功能完整的智能待办事项管理系统。这个项目不仅适合初学者学习全栈开发,也适合作为个人日常使用的效率工具。

项目亮点

  • 🎯 智能优先级排序:根据截止日期和重要性自动计算任务优先级
  • 📊 数据统计面板:可视化展示任务完成情况和效率分析
  • 🔔 智能提醒系统:支持多种通知方式,确保重要任务不被遗漏
  • 💾 轻量级存储:使用 SQLite,无需额外数据库服务
  • 🚀 RESTful API:规范的接口设计,便于扩展和集成

技术栈选择

后端技术

技术版本选择理由
Node.jsv20+高性能异步运行时,生态丰富
Expressv4.x轻量级 Web 框架,易于上手
SQLite3v5.x零配置数据库,适合小型项目
Better-sqlite3v9.x同步 API,代码更简洁

前端技术

技术版本选择理由
HTML5/CSS3-基础网页结构
JavaScript (ES6+)-现代语法,更好的开发体验
Chart.jsv4.x轻量级图表库,数据可视化
Axiosv1.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 即可查看应用界面。


项目扩展建议

短期扩展

  1. 用户认证系统:添加 JWT 认证,支持多用户
  2. 邮件提醒:集成 Nodemailer,到期前发送邮件通知
  3. 任务子项:支持将大任务拆分为多个子任务
  4. 标签系统:完善标签功能,支持多标签筛选

长期扩展

  1. 移动端应用:使用 React Native 开发跨平台移动应用
  2. 团队协作:支持任务分配和团队看板
  3. AI 智能建议:基于历史数据推荐最佳执行时间
  4. 第三方集成:对接日历、Slack、钉钉等工具

总结

通过这个项目,我们完成了一个功能完整的智能待办事项管理系统。项目涵盖了:

  • ✅ RESTful API 设计规范
  • ✅ SQLite 数据库操作
  • ✅ 优先级算法实现
  • ✅ 前后端分离架构
  • ✅ 数据可视化展示

这个项目可以作为学习全栈开发的实践案例,也可以作为个人效率工具使用。代码结构清晰,易于扩展和维护。

项目源码已上传至 GitHub,欢迎 Star 和 Fork!


作者:折腾虾 | 发布时间:2026 年 3 月 | 分类:<a href="/categories/projects" title="项目实战" class="auto-link">项目实战

分享到:

如果这篇文章对你有帮助,欢迎请作者喝杯咖啡 ☕

加载评论中...