Skip to content

TodoApp

文件信息

  • 📄 原文件:TodoApp.java
  • 🔤 语言:java

Todo 命令行应用 一个简单的命令行 Todo 应用,演示 Java 实战开发。 功能:

  • 添加任务
  • 列出任务
  • 完成任务
  • 删除任务
  • 持久化存储

完整代码

java
import java.io.*;
import java.nio.file.*;
import java.time.*;
import java.time.format.*;
import java.util.*;

/**
 * ============================================================
 *                    Todo 命令行应用
 * ============================================================
 * 一个简单的命令行 Todo 应用,演示 Java 实战开发。
 *
 * 功能:
 * - 添加任务
 * - 列出任务
 * - 完成任务
 * - 删除任务
 * - 持久化存储
 * ============================================================
 */
public class TodoApp {

    private static final String DATA_FILE = "todos.txt";
    private static final DateTimeFormatter FORMATTER =
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private final List<TodoItem> todos = new ArrayList<>();
    private final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        TodoApp app = new TodoApp();
        app.run();
    }

    /**
     * 主运行循环
     */
    public void run() {
        loadTodos();
        printWelcome();

        boolean running = true;
        while (running) {
            printMenu();
            String choice = scanner.nextLine().trim();

            switch (choice) {
                case "1" -> addTodo();
                case "2" -> listTodos();
                case "3" -> completeTodo();
                case "4" -> deleteTodo();
                case "5" -> {
                    saveTodos();
                    running = false;
                    System.out.println("再见!");
                }
                default -> System.out.println("无效选项,请重试。");
            }
        }
    }

    private void printWelcome() {
        System.out.println("""

            ╔════════════════════════════════════╗
            ║       Todo 命令行应用 v1.0        ║
            ╚════════════════════════════════════╝
            """);
    }

    private void printMenu() {
        System.out.println("""

            ━━━━━━━━━━ 菜单 ━━━━━━━━━━
            1. 添加任务
            2. 查看任务
            3. 完成任务
            4. 删除任务
            5. 退出
            ━━━━━━━━━━━━━━━━━━━━━━━━━━
            """);
        System.out.print("请选择: ");
    }

    /**
     * 添加新任务
     */
    private void addTodo() {
        System.out.print("\n输入任务内容: ");
        String content = scanner.nextLine().trim();

        if (content.isEmpty()) {
            System.out.println("任务内容不能为空!");
            return;
        }

        TodoItem item = new TodoItem(
            generateId(),
            content,
            false,
            LocalDateTime.now(),
            null
        );

        todos.add(item);
        saveTodos();
        System.out.println("✓ 任务已添加: " + content);
    }

    /**
     * 列出所有任务
     */
    private void listTodos() {
        System.out.println("\n━━━━━━━━━━ 任务列表 ━━━━━━━━━━");

        if (todos.isEmpty()) {
            System.out.println("  (暂无任务)");
            return;
        }

        // 分离已完成和未完成
        List<TodoItem> pending = new ArrayList<>();
        List<TodoItem> completed = new ArrayList<>();

        for (TodoItem item : todos) {
            if (item.completed()) {
                completed.add(item);
            } else {
                pending.add(item);
            }
        }

        // 显示未完成任务
        if (!pending.isEmpty()) {
            System.out.println("\n【待办】");
            for (TodoItem item : pending) {
                System.out.printf("  [%d] ○ %s%n", item.id(), item.content());
                System.out.printf("      创建于: %s%n", item.createdAt().format(FORMATTER));
            }
        }

        // 显示已完成任务
        if (!completed.isEmpty()) {
            System.out.println("\n【已完成】");
            for (TodoItem item : completed) {
                System.out.printf("  [%d] ● %s%n", item.id(), item.content());
                System.out.printf("      完成于: %s%n",
                    item.completedAt() != null ? item.completedAt().format(FORMATTER) : "");
            }
        }

        System.out.printf("%n共 %d 个任务,%d 个待办,%d 个已完成%n",
            todos.size(), pending.size(), completed.size());
    }

    /**
     * 完成任务
     */
    private void completeTodo() {
        List<TodoItem> pending = todos.stream()
            .filter(t -> !t.completed())
            .toList();

        if (pending.isEmpty()) {
            System.out.println("\n没有待办任务!");
            return;
        }

        System.out.println("\n待办任务:");
        for (TodoItem item : pending) {
            System.out.printf("  [%d] %s%n", item.id(), item.content());
        }

        System.out.print("\n输入要完成的任务 ID: ");
        try {
            int id = Integer.parseInt(scanner.nextLine().trim());
            boolean found = false;

            for (int i = 0; i < todos.size(); i++) {
                if (todos.get(i).id() == id && !todos.get(i).completed()) {
                    TodoItem old = todos.get(i);
                    todos.set(i, new TodoItem(
                        old.id(),
                        old.content(),
                        true,
                        old.createdAt(),
                        LocalDateTime.now()
                    ));
                    saveTodos();
                    System.out.println("✓ 已完成: " + old.content());
                    found = true;
                    break;
                }
            }

            if (!found) {
                System.out.println("未找到该任务或已完成!");
            }
        } catch (NumberFormatException e) {
            System.out.println("无效的 ID!");
        }
    }

    /**
     * 删除任务
     */
    private void deleteTodo() {
        if (todos.isEmpty()) {
            System.out.println("\n没有任务可删除!");
            return;
        }

        System.out.println("\n所有任务:");
        for (TodoItem item : todos) {
            String status = item.completed() ? "●" : "○";
            System.out.printf("  [%d] %s %s%n", item.id(), status, item.content());
        }

        System.out.print("\n输入要删除的任务 ID: ");
        try {
            int id = Integer.parseInt(scanner.nextLine().trim());
            boolean removed = todos.removeIf(t -> t.id() == id);

            if (removed) {
                saveTodos();
                System.out.println("✓ 任务已删除");
            } else {
                System.out.println("未找到该任务!");
            }
        } catch (NumberFormatException e) {
            System.out.println("无效的 ID!");
        }
    }

    /**
     * 生成唯一 ID
     */
    private int generateId() {
        return todos.stream()
            .mapToInt(TodoItem::id)
            .max()
            .orElse(0) + 1;
    }

    /**
     * 加载任务数据
     */
    private void loadTodos() {
        Path path = Path.of(DATA_FILE);
        if (!Files.exists(path)) {
            return;
        }

        try {
            List<String> lines = Files.readAllLines(path);
            for (String line : lines) {
                String[] parts = line.split("\\|");
                if (parts.length >= 4) {
                    int id = Integer.parseInt(parts[0]);
                    String content = parts[1];
                    boolean completed = Boolean.parseBoolean(parts[2]);
                    LocalDateTime createdAt = LocalDateTime.parse(parts[3], FORMATTER);
                    LocalDateTime completedAt = parts.length > 4 && !parts[4].isEmpty()
                        ? LocalDateTime.parse(parts[4], FORMATTER)
                        : null;

                    todos.add(new TodoItem(id, content, completed, createdAt, completedAt));
                }
            }
            System.out.println("已加载 " + todos.size() + " 个任务");
        } catch (IOException e) {
            System.out.println("加载数据失败: " + e.getMessage());
        }
    }

    /**
     * 保存任务数据
     */
    private void saveTodos() {
        try {
            List<String> lines = new ArrayList<>();
            for (TodoItem item : todos) {
                String line = String.join("|",
                    String.valueOf(item.id()),
                    item.content(),
                    String.valueOf(item.completed()),
                    item.createdAt().format(FORMATTER),
                    item.completedAt() != null ? item.completedAt().format(FORMATTER) : ""
                );
                lines.add(line);
            }
            Files.write(Path.of(DATA_FILE), lines);
        } catch (IOException e) {
            System.out.println("保存数据失败: " + e.getMessage());
        }
    }
}

/**
 * Todo 项目记录类
 */
record TodoItem(
    int id,
    String content,
    boolean completed,
    LocalDateTime createdAt,
    LocalDateTime completedAt
) {}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布