折腾侠
项目实战

从零打造个人任务管理器 CLI 工具:Node.js + SQLite 实战

折腾侠
2026/03/27 发布
14约 10 分钟1459 字 / 1294 词00

从零打造个人任务管理器 CLI 工具:Node.js + SQLite 实战

项目背景

在日常开发工作中,我们经常需要管理各种任务:待办事项、项目进度、会议安排等等。虽然市面上有很多优秀的任务管理工具,但作为一个开发者,为什么不自己动手打造一个轻量级、完全可控的 CLI 任务管理器呢?

本文将带你从零开始,使用 Node.js 和 SQLite 构建一个功能完整的命令行任务管理工具。这个项目不仅实用,还能帮助你深入理解 CLI 开发、数据库操作和项目架构设计。

项目功能说明

核心功能

  1. 任务创建:支持添加新任务,包含标题、描述、优先级、截止日期
  2. 任务列表:查看所有任务,支持按状态、优先级筛选
  3. 任务更新:修改任务信息,标记完成状态
  4. 任务删除:删除单个任务或批量清理已完成任务
  5. 任务统计:显示任务完成情况统计信息
  6. 数据持久化:使用 SQLite 本地存储,数据安全可靠

扩展功能

  • 支持任务分类/标签
  • 支持子任务
  • 支持任务提醒(结合系统通知)
  • 支持数据导出(JSON/CSV 格式)

技术栈选择

核心技术

技术版本选择理由
Node.jsv18+成熟的运行时生态,丰富的 npm 包支持
SQLite3.x轻量级嵌入式数据库,零配置,单文件存储
Commander.jsv11+流行的 CLI 框架,API 简洁易用
Better-SQLite3v9+SQLite 的同步 Node.js 绑定,性能优秀

开发工具

工具用途
ESLint代码质量检查
Prettier代码格式化
Jest单元测试
Nodemon开发时自动重启

为什么选择这些技术?

SQLite 相比其他数据库的优势:

  • 无需安装数据库服务器,开箱即用
  • 单文件存储,便于备份和迁移
  • 支持完整的 SQL 语法,功能强大
  • 读写性能优秀,适合本地应用

Better-SQLite3 相比其他 SQLite 库:

  • 同步 API,代码更简洁直观
  • 性能比异步方案更好
  • 类型支持完善,配合 TypeScript 友好

项目结构设计

task-cli/
├── src/
│   ├── index.js          # 入口文件
│   ├── db/
│   │   ├── database.js   # 数据库连接与初始化
│   │   └── schema.sql    # 数据库表结构
│   ├── commands/
│   │   ├── add.js        # 添加任务命令
│   │   ├── list.js       # 列表命令
│   │   ├── update.js     # 更新命令
│   │   ├── delete.js     # 删除命令
│   │   ├── complete.js   # 完成命令
│   │   └── stats.js      # 统计命令
│   ├── utils/
│   │   ├── formatter.js  # 输出格式化
│   │   └── validator.js  # 输入验证
│   └── config/
│       └── constants.js  # 常量配置
├── tests/
│   ├── db.test.js
│   └── commands.test.js
├── package.json
├── README.md
└── .eslintrc.js

目录设计原则

  1. 按功能模块划分:commands 目录存放所有命令实现
  2. 关注点分离:数据库、工具函数、配置各自独立
  3. 易于测试:每个模块可独立单元测试
  4. 便于扩展:新增命令只需在 commands 目录添加文件

核心代码实现

1. 数据库模块 (src/db/database.js)

JavaScript
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');

const DB_PATH = path.join(process.env.HOME, '.task-cli', 'tasks.db');

// 确保数据库目录存在
function ensureDbDir() {
  const dir = path.dirname(DB_PATH);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
}

// 初始化数据库
function initDatabase() {
  ensureDbDir();
  const db = new Database(DB_PATH);
  
  // 启用外键约束
  db.pragma('foreign_keys = ON');
  
  // 创建表结构
  db.exec(`
    CREATE TABLE IF NOT EXISTS tasks (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      title TEXT NOT NULL,
      description TEXT,
      priority INTEGER DEFAULT 2 CHECK(priority BETWEEN 1 AND 3),
      status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'in_progress', 'completed')),
      due_date DATETIME,
      category TEXT,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )
  `);
  
  // 创建索引提升查询性能
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_status ON tasks(status);
    CREATE INDEX IF NOT EXISTS idx_priority ON tasks(priority);
    CREATE INDEX IF NOT EXISTS idx_due_date ON tasks(due_date);
  `);
  
  return db;
}

module.exports = { initDatabase, DB_PATH };

2. 添加任务命令 (src/commands/add.js)

JavaScript
const { validateTaskInput } = require('../utils/validator');

function addTask(db, options) {
  // 验证输入
  const validation = validateTaskInput(options);
  if (!validation.valid) {
    console.error(`❌ 输入错误:${validation.error}`);
    process.exit(1);
  }
  
  const { title, description, priority, due, category } = options;
  
  // 插入数据库
  const stmt = db.prepare(`
    INSERT INTO tasks (title, description, priority, due_date, category)
    VALUES (?, ?, ?, ?, ?)
  `);
  
  const result = stmt.run(
    title,
    description || null,
    priority || 2,
    due || null,
    category || null
  );
  
  console.log(`✅ 任务已创建 (ID: ${result.lastInsertRowid})`);
  console.log(`   标题:${title}`);
  if (priority) {
    const priorityMap = { 1: '高', 2: '中', 3: '低' };
    console.log(`   优先级:${priorityMap[priority]}`);
  }
  if (due) {
    console.log(`   截止日期:${due}`);
  }
}

module.exports = addTask;

3. 列表命令 (src/commands/list.js)

JavaScript
const { formatTasks } = require('../utils/formatter');

function listTasks(db, options) {
  const { status, priority, category, limit } = options;
  
  // 构建动态查询
  let query = 'SELECT * FROM tasks WHERE 1=1';
  const params = [];
  
  if (status) {
    query += ' AND status = ?';
    params.push(status);
  }
  
  if (priority) {
    query += ' AND priority = ?';
    params.push(priority);
  }
  
  if (category) {
    query += ' AND category = ?';
    params.push(category);
  }
  
  query += ' ORDER BY priority ASC, due_date ASC';
  
  if (limit) {
    query += ' LIMIT ?';
    params.push(limit);
  }
  
  const stmt = db.prepare(query);
  const tasks = stmt.all(...params);
  
  if (tasks.length === 0) {
    console.log('📭 没有找到符合条件的任务');
    return;
  }
  
  console.log(`\n📋 共找到 ${tasks.length} 个任务:\n`);
  formatTasks(tasks);
}

module.exports = listTasks;

4. 输出格式化 (src/utils/formatter.js)

JavaScript
const chalk = require('chalk');

const PRIORITY_MAP = {
  1: { label: '高', color: 'red' },
  2: { label: '中', color: 'yellow' },
  3: { label: '低', color: 'green' }
};

const STATUS_MAP = {
  pending: { label: '待处理', icon: '⏳', color: 'gray' },
  in_progress: { label: '进行中', icon: '🔄', color: 'blue' },
  completed: { label: '已完成', icon: '✅', color: 'green' }
};

function formatTasks(tasks) {
  tasks.forEach((task, index) => {
    const priority = PRIORITY_MAP[task.priority];
    const status = STATUS_MAP[task.status];
    
    console.log(
      chalk.bold(`[${task.id}]`),
      chalk[status.color](status.icon),
      chalk[priority.color](`[${priority.label}]`),
      task.title
    );
    
    if (task.description) {
      console.log(chalk.gray(`    ${task.description}`));
    }
    
    if (task.due_date) {
      const isOverdue = new Date(task.due_date) < new Date() && task.status !== 'completed';
      const color = isOverdue ? 'red' : 'gray';
      console.log(chalk[color](`    📅 截止:${task.due_date}`));
    }
    
    if (task.category) {
      console.log(chalk.gray(`    🏷️  分类:${task.category}`));
    }
    
    console.log('');
  });
}

module.exports = { formatTasks, PRIORITY_MAP, STATUS_MAP };

5. 主入口文件 (src/index.js)

JavaScript
#!/usr/bin/env node

const { Command } = require('commander');
const { initDatabase } = require('./db/database');

const program = new Command();

program
  .name('task-cli')
  .description('个人任务管理器 CLI 工具')
  .version('1.0.0');

// 添加任务
program
  .command('add <title>')
  .description('添加新任务')
  .option('-d, --description <text>', '任务描述')
  .option('-p, --priority <1|2|3>', '优先级 (1:高, 2:中, 3:低)', '2')
  .option('--due <date>', '截止日期 (YYYY-MM-DD)')
  .option('-c, --category <name>', '任务分类')
  .action((title, options) => {
    const db = initDatabase();
    require('./commands/add')(db, { ...options, title });
    db.close();
  });

// 列表任务
program
  .command('list')
  .description('查看任务列表')
  .option('-s, --status <status>', '按状态筛选')
  .option('-p, --priority <1|2|3>', '按优先级筛选')
  .option('-c, --category <name>', '按分类筛选')
  .option('-l, --limit <number>', '限制显示数量')
  .action((options) => {
    const db = initDatabase();
    require('./commands/list')(db, options);
    db.close();
  });

// 完成任务
program
  .command('complete <id>')
  .description('标记任务为完成')
  .action((id, options) => {
    const db = initDatabase();
    require('./commands/complete')(db, { id });
    db.close();
  });

// 删除任务
program
  .command('delete <id>')
  .description('删除任务')
  .option('-f, --force', '跳过确认')
  .action((id, options) => {
    const db = initDatabase();
    require('./commands/delete')(db, { id, ...options });
    db.close();
  });

// 统计信息
program
  .command('stats')
  .description('显示任务统计')
  .action((options) => {
    const db = initDatabase();
    require('./commands/stats')(db);
    db.close();
  });

program.parse();

运行步骤

第一步:环境准备

Bash
# 检查 Node.js 版本 (需要 v18+)
node --version

# 创建项目目录
mkdir task-cli && cd task-cli

# 初始化项目
npm init -y

第二步:安装依赖

Bash
# 安装核心依赖
npm install better-sqlite3 commander chalk

# 安装开发依赖
npm install --save-dev eslint prettier jest nodemon

第三步:创建项目结构

Bash
# 创建目录结构
mkdir -p src/{db,commands,utils,config}
mkdir -p tests

# 创建入口文件并添加执行权限
touch src/index.js
chmod +x src/index.js

第四步:配置 package.json

JSON
{
  "name": "task-cli",
  "version": "1.0.0",
  "description": "个人任务管理器 CLI 工具",
  "main": "src/index.js",
  "bin": {
    "task": "./src/index.js"
  },
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest",
    "lint": "eslint src/"
  },
  "keywords": ["cli", "task", "manager"],
  "author": "",
  "license": "MIT"
}

第五步:全局安装使用

Bash
# 链接到全局
npm link

# 现在可以在任何地方使用
task --help
task add "完成项目文档" -p 1 --due 2024-04-01
task list
task complete 1
task stats

使用示例

Bash
# 添加高优先级任务
$ task add "完成季度报告" -p 1 --due 2024-04-15 -c 工作
✅ 任务已创建 (ID: 1)
   标题:完成季度报告
   优先级:高
   截止日期:2024-04-15
   🏷️  分类:工作

# 查看所有待处理任务
$ task list -s pending

📋 共找到 3 个任务:

[1] ⏳ [高] 完成季度报告
    📅 截止:2024-04-15
    🏷️  分类:工作

[2] ⏳ [中] 更新项目文档
    📅 截止:2024-04-20

[3] ⏳ [低] 整理代码规范
    🏷️  分类:学习

# 查看统计信息
$ task stats

📊 任务统计
━━━━━━━━━━━━━━━━
总任务数:15
已完成:8 (53%)
进行中:2 (13%)
待处理:5 (33%)

高优先级待处理:2 ⚠️
即将到期 (3 天内): 3

项目扩展方向

完成基础功能后,可以考虑以下扩展:

1. 子任务支持

SQL
CREATE TABLE subtasks (
  id INTEGER PRIMARY KEY,
  parent_task_id INTEGER,
  title TEXT NOT NULL,
  completed BOOLEAN DEFAULT 0,
  FOREIGN KEY (parent_task_id) REFERENCES tasks(id)
);

2. 任务提醒

结合 INLINE_CODE_0 实现系统通知:

JavaScript
const notifier = require('node-notifier');

function notifyTask(task) {
  notifier.notify({
    title: '任务提醒',
    message: `任务 "${task.title}" 即将到期`,
    urgency: 'critical'
  });
}

3. 数据同步

添加远程同步功能,支持多设备数据同步:

  • 使用 GitHub Gist 作为存储后端
  • 或自建同步服务器
  • 支持导入/导出 JSON 备份

4. 交互式界面

使用 INLINE_CODE_1 添加交互式命令:

JavaScript
const inquirer = require('inquirer');

async function interactiveAdd() {
  const answers = await inquirer.prompt([
    { type: 'input', name: 'title', message: '任务标题' },
    { type: 'input', name: 'description', message: '任务描述' },
    { type: 'list', name: 'priority', choices: ['高', '中', '低'] }
  ]);
  // 处理添加逻辑
}

总结

通过这个项目,我们完成了:

  1. ✅ 一个功能完整的 CLI 任务管理器
  2. ✅ 使用 SQLite 实现数据持久化
  3. ✅ 模块化架构设计,易于维护和扩展
  4. ✅ 丰富的命令行选项和友好的输出
  5. ✅ 完整的开发工具链配置

这个项目不仅实用,还能帮助你掌握:

  • Node.js CLI 开发流程
  • SQLite 数据库设计与操作
  • 模块化代码组织
  • 命令行参数解析
  • 输出格式化技巧

你可以在此基础上继续扩展,添加更多实用功能,或者将其作为学习 Node.js 开发的实践项目。源码已准备好,现在就开始动手构建属于你的任务管理器吧!


项目源码:可根据本文代码自行创建,或访问 GitHub 获取完整示例 技术栈:Node.js | SQLite | Commander.js | Better-SQLite3 难度:⭐⭐⭐☆☆ 中级 预计耗时:2-4 小时

分享到:

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

加载评论中...