diff --git a/SimpleBudgetTracker/README.md b/SimpleBudgetTracker/README.md new file mode 100644 index 0000000..803566e --- /dev/null +++ b/SimpleBudgetTracker/README.md @@ -0,0 +1,43 @@ +# Simple Budget Tracker + +一个适合初学者的 Java 命令行记账小项目,演示类、枚举、集合、文件读写和命令行交互等基础知识。 + +## 功能 +- 添加收入或支出记录。 +- 查看全部记录。 +- 查看统计信息(总收入、总支出、净资产)。 +- 将记录保存到本地文件并在程序启动时自动加载。 + +## 项目结构 +``` +SimpleBudgetTracker/ +├── README.md +├── data/ +│ └── transactions.txt +└── src/ + └── com/example/budget/ + ├── TransactionType.java + ├── Transaction.java + ├── BudgetManager.java + ├── StorageUtil.java + └── Main.java +``` + +## 运行步骤 +1. 安装 JDK 11 或更高版本,并确保 `java` 与 `javac` 可在终端运行。 +2. 在项目根目录执行以下命令编译: + ```bash + javac -d out src/com/example/budget/*.java + ``` +3. 运行程序: + ```bash + java -cp out com.example.budget.Main + ``` +4. 根据命令行提示进行操作。程序会自动读取和保存 `data/transactions.txt` 文件。 + +## VS Code 运行提示 +- 安装 Extension Pack for Java。 +- 打开 `Main.java` 并点击右上角的 “Run” 图标即可运行。 +- 或者在 VS Code 终端中执行上述命令。 + +祝学习愉快! diff --git a/SimpleBudgetTracker/data/transactions.txt b/SimpleBudgetTracker/data/transactions.txt new file mode 100644 index 0000000..e69de29 diff --git a/SimpleBudgetTracker/src/com/example/budget/BudgetManager.java b/SimpleBudgetTracker/src/com/example/budget/BudgetManager.java new file mode 100644 index 0000000..5ca7ae3 --- /dev/null +++ b/SimpleBudgetTracker/src/com/example/budget/BudgetManager.java @@ -0,0 +1,46 @@ +package com.example.budget; + +import java.util.ArrayList; +import java.util.List; + +public class BudgetManager { + private final List transactions; + + public BudgetManager() { + this.transactions = new ArrayList<>(); + } + + public void addTransaction(Transaction transaction) { + transactions.add(transaction); + } + + public List getAllTransactions() { + return transactions; + } + + public double getTotalIncome() { + return transactions.stream() + .filter(t -> t.getType() == TransactionType.INCOME) + .mapToDouble(Transaction::getAmount) + .sum(); + } + + public double getTotalExpense() { + return transactions.stream() + .filter(t -> t.getType() == TransactionType.EXPENSE) + .mapToDouble(Transaction::getAmount) + .sum(); + } + + public double getNetBalance() { + return getTotalIncome() - getTotalExpense(); + } + + public void printSummary() { + System.out.println("========== Summary =========="); + System.out.printf("Total Income : %.2f%n", getTotalIncome()); + System.out.printf("Total Expense: %.2f%n", getTotalExpense()); + System.out.printf("Net Balance : %.2f%n", getNetBalance()); + System.out.println("============================="); + } +} diff --git a/SimpleBudgetTracker/src/com/example/budget/Main.java b/SimpleBudgetTracker/src/com/example/budget/Main.java new file mode 100644 index 0000000..3a21ea5 --- /dev/null +++ b/SimpleBudgetTracker/src/com/example/budget/Main.java @@ -0,0 +1,95 @@ +package com.example.budget; + +import java.util.Scanner; + +public class Main { + private static final String DATA_FILE = "data/transactions.txt"; + + public static void main(String[] args) { + BudgetManager manager = new BudgetManager(); + StorageUtil.loadTransactions(manager, DATA_FILE); + + Scanner scanner = new Scanner(System.in); + boolean running = true; + + while (running) { + printMenu(); + System.out.print("请选择操作:"); + String choice = scanner.nextLine().trim(); + + switch (choice) { + case "1": + addTransaction(manager, scanner, TransactionType.INCOME); + break; + case "2": + addTransaction(manager, scanner, TransactionType.EXPENSE); + break; + case "3": + printTransactions(manager); + break; + case "4": + manager.printSummary(); + break; + case "5": + StorageUtil.saveTransactions(manager.getAllTransactions(), DATA_FILE); + break; + case "0": + running = false; + StorageUtil.saveTransactions(manager.getAllTransactions(), DATA_FILE); + break; + default: + System.out.println("无效选择,请重新输入。"); + } + } + + scanner.close(); + System.out.println("谢谢使用记账系统,已退出。"); + } + + private static void printMenu() { + System.out.println("\n===== 简易记账系统 ====="); + System.out.println("1. 记录收入"); + System.out.println("2. 记录支出"); + System.out.println("3. 查看全部记录"); + System.out.println("4. 查看统计信息"); + System.out.println("5. 手动保存数据"); + System.out.println("0. 退出程序"); + System.out.println("======================="); + } + + private static void addTransaction(BudgetManager manager, Scanner scanner, TransactionType type) { + System.out.print("请输入金额:"); + String amountInput = scanner.nextLine().trim(); + double amount; + try { + amount = Double.parseDouble(amountInput); + } catch (NumberFormatException e) { + System.out.println("金额格式不正确,请重新输入。"); + return; + } + + if (amount <= 0) { + System.out.println("金额必须大于0。"); + return; + } + + System.out.print("请输入描述:"); + String description = scanner.nextLine().trim(); + if (description.isEmpty()) { + description = type == TransactionType.INCOME ? "收入" : "支出"; + } + + Transaction transaction = new Transaction(amount, type, description); + manager.addTransaction(transaction); + System.out.println("成功添加记录:" + transaction.format()); + } + + private static void printTransactions(BudgetManager manager) { + System.out.println("\n===== 当前记录 ====="); + if (manager.getAllTransactions().isEmpty()) { + System.out.println("暂无记录。"); + } else { + manager.getAllTransactions().forEach(t -> System.out.println(t.format())); + } + } +} diff --git a/SimpleBudgetTracker/src/com/example/budget/StorageUtil.java b/SimpleBudgetTracker/src/com/example/budget/StorageUtil.java new file mode 100644 index 0000000..a9964ae --- /dev/null +++ b/SimpleBudgetTracker/src/com/example/budget/StorageUtil.java @@ -0,0 +1,44 @@ +package com.example.budget; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class StorageUtil { + public static void saveTransactions(List transactions, String filePath) { + try (BufferedWriter writer = Files.newBufferedWriter(Path.of(filePath))) { + for (Transaction t : transactions) { + writer.write(t.toFileFormat()); + writer.newLine(); + } + System.out.println("记录已成功保存到 " + filePath); + } catch (IOException e) { + System.err.println("保存记录失败: " + e.getMessage()); + } + } + + public static void loadTransactions(BudgetManager manager, String filePath) { + Path path = Path.of(filePath); + if (!Files.exists(path)) { + System.out.println("未找到交易记录文件,将创建新文件。"); + return; + } + try (BufferedReader reader = Files.newBufferedReader(path)) { + String line; + while ((line = reader.readLine()) != null) { + try { + Transaction transaction = Transaction.fromFileFormat(line); + manager.addTransaction(transaction); + } catch (IllegalArgumentException ex) { + System.err.println("跳过无效记录: " + line); + } + } + System.out.println("已从文件加载记录。"); + } catch (IOException e) { + System.err.println("加载记录失败: " + e.getMessage()); + } + } +} diff --git a/SimpleBudgetTracker/src/com/example/budget/Transaction.java b/SimpleBudgetTracker/src/com/example/budget/Transaction.java new file mode 100644 index 0000000..eab29e9 --- /dev/null +++ b/SimpleBudgetTracker/src/com/example/budget/Transaction.java @@ -0,0 +1,65 @@ +package com.example.budget; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Transaction { + private final double amount; + private final TransactionType type; + private final String description; + private final LocalDateTime timestamp; + + public Transaction(double amount, TransactionType type, String description) { + this.amount = amount; + this.type = type; + this.description = description; + this.timestamp = LocalDateTime.now(); + } + + public Transaction(double amount, TransactionType type, String description, LocalDateTime timestamp) { + this.amount = amount; + this.type = type; + this.description = description; + this.timestamp = timestamp; + } + + public double getAmount() { + return amount; + } + + public TransactionType getType() { + return type; + } + + public String getDescription() { + return description; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public String format() { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + return String.format("%s | %s | %.2f | %s", + formatter.format(timestamp), type, amount, description); + } + + public String toFileFormat() { + DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + return String.format("%s|%s|%.2f|%s", + formatter.format(timestamp), type, amount, description.replace("|", "/")); + } + + public static Transaction fromFileFormat(String line) { + String[] parts = line.split("\\|"); + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid line: " + line); + } + LocalDateTime time = LocalDateTime.parse(parts[0]); + TransactionType type = TransactionType.valueOf(parts[1]); + double amount = Double.parseDouble(parts[2]); + String description = parts[3]; + return new Transaction(amount, type, description, time); + } +} diff --git a/SimpleBudgetTracker/src/com/example/budget/TransactionType.java b/SimpleBudgetTracker/src/com/example/budget/TransactionType.java new file mode 100644 index 0000000..6d704aa --- /dev/null +++ b/SimpleBudgetTracker/src/com/example/budget/TransactionType.java @@ -0,0 +1,6 @@ +package com.example.budget; + +public enum TransactionType { + INCOME, + EXPENSE +}