# java.program3 **Repository Path**: zhedaotizenmezuo/java.program3 ## Basic Information - **Project Name**: java.program3 - **Description**: No description available - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-11-14 - **Last Updated**: 2026-01-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 《Java 程序设计》团队项目作业报告书 **课程名称**:Java 程序设计 **学期**:2025-2026 学年第一学期 **团队信息**: * 班级:24 数据本1 * 组号:第 1 组 * 组长:张聪(学号:2414110737) * 组员:刘新亮(学号:2414110715)、徐浩然(学号:2414110739)、鞠承瑞(学号:2414110708)、任怡颖(学号:241411023)、齐雪晴(学号:2414110722) * 项目名称:管家婆财务管理工具 * 提交日期:2026年1月4日 ## 目录 1. [项目概](#一-项目概述)[述](#一-项目概述) 2. [需](#二-需求分析)[求分析](#二-需求分析) 3. [系统设计](#三-系统设计) 4. [实现过程](#四-实现过程) 5. [测试报](#五-测试报告)[告](#五-测试报告) 6. [团队](#六-团队协作与ai应用)[协作与 AI](#六-团队协作与ai应用)[ 应用](#六-团队协作与ai应用) 7. [总](#七-总结与反思)[结与反思](#七-总结与反思) 8. [附录](#八-附录) ## 一、项目概述 ### 1.1 项目背景与意义 在日常生活和个人财务管理场景中,人工记录收支信息存在效率低、数据统计繁琐、历史数据追溯困难等问题,且难以直观呈现收支趋势和月度结余情况。针对个人及小家庭的财务管理需求,本项目开发了 “管家婆收支管理系统”,通过可视化的图形界面实现收支记录的添加、查看、删除、汇总统计及历史趋势分析,解决了传统人工记账的痛点。 从技术应用角度,本项目践行 “技术服务生活” 的理念,将 Java Swing 图形界面开发、数据持久化存储、时间序列数据处理、可视化图表展示等技术融合应用,既满足了实际的财务管理需求,也培养了工程化的软件开发思维和问题解决能力 ### 1.2 项目目标 **功能目标** 实现收支记录全生命周期管理,核心功能包括: 1.收支记录管理:支持收入 / 支出记录的添加(含严格的日期、金额校验)、按月份筛选查看、批量删除; 2.收支汇总统计:按月份统计收入 / 支出明细、当月结余、累计结余及总资产; 3.历史趋势分析:通过折线图可视化展示月度收入、支出、结余趋势,支持多维度对比查看; 4.数据持久化:实现记录、总资产、月度结余数据的本地文件存储,保障数据不丢失。 **技术目标** 1.核心技术:应用 Java Swing 进行 GUI 开发,掌握布局管理(CardLayout、BorderLayout 等)、组件事件绑定、自定义单元格渲染器等; 2.数据处理:使用集合(List、Map、SortedSet)实现数据分组、排序、统计,基于 SimpleDateFormat 实现严格的日期校验; 3.可视化:集成 JFreeChart 实现时间序列折线图展示,自定义图表样式适配财务数据展示场景; 4.数据存储:通过 IO 流(BufferedReader/BufferedWriter)实现文本文件的读写,保障数据持久化。 **AI 应用目标** 1.借助 AI 代码助手(如通义灵码、GitHub Copilot)辅助完成重复且标准化的代码编写(如组件样式设置、事件绑定模板、文件读写工具类); 2.利用 AI 智能诊断工具(如 IDEA AI 辅助调试)定位日期格式化、集合遍历中的异常问题,提升调试效率; 3.通过 AI 生成 JFreeChart 图表配置的初始代码,结合财务数据展示需求调整样式和数据映射逻辑。 ### 1.3 AI 生成内容标注表 | AI 工具名称 | 应用模块 | 生成内容(初稿) | 人工调整内容 | 调整说明(为何调整) | |------------|----------|------------------|--------------|----------------------| | 通义灵码 | GUI布局 | CardLayout切换卡片、按钮样式设置代码模板 | 1. 调整按钮配色适配财务软件视觉风格;2. 补充批量操作按钮(全选/反选/删除)的布局逻辑;3. 优化标签字体和间距提升可读性 | 原模板仅实现基础布局,未结合财务系统的视觉需求和批量操作的业务逻辑,需适配实际功能场景 | | 通义灵码 | JFreeChart图表 | 时间序列折线图基础配置代码 | 1. 自定义X轴日期格式为“yyyy-MM”;2. 新增数值标签显示(保留2位小数);3. 区分收入/支出/结余的图表配色;4. 增加图表缩放、提示框功能 | 原代码仅实现基础折线图,未适配月度财务数据的展示需求,需优化视觉效果和交互体验 | | GitHub Copilot | 数据统计 | 月度收支统计的Map遍历代码模板 | 1. 补充“无记录月份按0统计”的逻辑;2. 增加从最早记录月到当前月的全月份遍历;3. 优化累计结余的计算规则 | 原模板仅统计有记录的月份,未覆盖“无记录月份”的场景,不符合财务统计的完整性要求 | | IDEA AI辅助调试 | 日期校验 | 日期格式校验的异常捕获代码 | 1. 增加“严格模式”(拒绝无效日期如2025-02-30);2. 补充日期范围校验(2000-01-01至当前日期);3. 优化错误提示文案的可读性 | 原代码仅捕获格式异常,未校验日期的实际合法性和范围,易导致无效数据录入 | | 通义灵码 | 文件读写 | 文本文件读写的IO流代码模板 | 1. 补充数据解析时的异常处理(如记录格式错误);2. 优化记录分隔符(避免备注含“|”导致解析失败);3. 增加文件不存在时的自动创建逻辑 | 原模板未考虑业务数据的特殊性(如备注含特殊字符),需保障数据读写的鲁棒性 | ## 二、需求分析 ### 2.1 功能需求 ![输入图片说明](.gitee/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202026-01-03%20210851.png) ### 2.2 非功能需求 1. 性能需求 核心操作(增删/筛选/图表生成)响应时间≤2秒;支持1000条以上记录存储,100个连续月份统计;内存占用≤200MB,磁盘占用≤10MB。 2. 易用性需求 核心功能操作≤3步;关键数据颜色区分,分组展示提升可读性;输入默认填充+错误精准提示,降低操作成本。 3. 兼容性需求 支持Windows7+/macOS10.14+/Linux Ubuntu18.04+;兼容JDK8+及JFreeChart1.5.0+;适配主流文件系统。 4. 可靠性需求 严格数据校验(日期/金额);操作后数据自动同步一致;缺失文件自动创建,异常时友好容错。 5. 可维护性需求 模块化代码结构,常量集中定义;关键操作日志可追溯;收支类型、图表类型支持扩展。 ## 三、系统设计 ### 3.1 总体架构设计 * 用架构图(如分层架构图)展示系统整体结构,标注各层职责: * 采用三层架构 + 模块化设计:表现层(UI 层,负责用户交互,展示数据和接收操作,包含查看记录卡片、添加记录卡片、收支汇总卡片、历史结余卡片)、业务逻辑层(处理核心业务规则,衔接 UI 与数据存储,包含记录管理、月度统计、数据校验、图表数据转换)、数据访问层(负责数据持久化,实现文件读写,包含记录存储、总资产存储、月度结余存储); * 附架构图(本图由 DrawIO 绘制,结合 AI 绘图助手提供的分层架构模板调整而成,人工补充了模块间的数据流关系,确保符合财务工具的业务场景)。 * 架构特点:采用 “自上而下” 的数据流设计,用户操作(如添加记录)通过 UI 层触发,业务逻辑层处理数据校验和计算,最终由数据访问层完成持久化;查询类操作则从数据访问层加载数据,经业务层处理后通过 UI 层展示,确保数据流转的一致性。 ### 3.2 技术选型 * 按 “开发工具、编程语言、数据库、AI 工具” 分类说明: | 类别 | 具体选型 | 选型理由 | | ----- | ------------------ | ------------------------------ | | 开发工具 | IntelliJ IDEA 2023 | 支持 Java Swing 可视化开发、AI 辅助调试、代码补全,符合课程推荐工具链,团队成员均熟悉操作 | | 编程语言 | Java 8+ | 课程核心教学语言,支持 Swing 图形界面、IO 流、集合框架等核心技术,兼容性强,团队掌握度高 | | 图形界面 | Java Swing | 课程要求掌握的 GUI 开发技术,支持多种布局管理器和组件定制,能满足多卡片页面、表格、按钮等交互需求 | | 可视化图表 | JFreeChart 1.5.6 | 轻量级 Java 图表库,支持时间序列折线图,适配月度财务数据展示,API 文档完善,易与 Swing 集成 | | 数据存储 | 本地文本文件 | 无需数据库环境,适配 “个人财务管理” 的轻量场景,便于数据迁移和备份,契合课程 IO 流知识点应用要求 | | AI 辅助工具 | 通义灵码、GitHub Copilot、IDEA AI 助手 | 辅助生成重复代码、定位调试问题、提供技术方案模板,提升开发效率,同时不脱离课程要求的核心技术实现 | ### 3.3 核心模块设计 * 按模块说明设计细节,重点体现面向对象思想与 AI 辅助设计: **1.类设计** :附核心类的 UML 类图(主类 GuiAccountBook、实体类 Record、辅助类 AddCardComponents),标注类的属性、方法及关联关系(继承 / 聚合); | 类名 | 类型 | 核心属性 | 核心方法 | 关联关系 | |---|---|---|---|---| | GuiAccountBook | JFrame 子类 | recordList、monthlyBalanceMap、各 UI 组件引用 | initCardLayout、calculateAllMonthlyBalance、load/save 系列方法 | 包含 Record、AddCardComponents 内部类 | | Record | 实体内部类 | costType、amount、description、date | isIncome、getYearMonth、getter/setter 方法 | 被 GuiAccountBook 包含 | | AddCardComponents | 辅助内部类 | incomeRadio、typeComboBox、amountField 等 | getter 方法(获取组件输入值) | 被 GuiAccountBook 包含 | * 说明 AI 辅助设计过程:初始类图由 CodeGeeX 生成,包含 GuiAccountBook 主类和 Record 实体类的基础结构;人工补充了 AddCardComponents 辅助类,因添加记录页面组件较多,单独封装可减少主类代码冗余;同时基于月度统计需求,人工新增 Record 类的 getYearMonth 方法;最后通过 IDEA AI 助手优化了属性访问权限和方法命名规范。 **2.数据库设计** :附核心数据表(存储文件)结构,标注文件名、存储内容、字段格式、约束说明: | 文件名 | 存储内容 | 字段格式(示例) | 约束 / 说明 | |---|---|---|---| | account_book.txt | 所有收支记录 | 分类 | 金额 | 备注 | 日期(工资 | 5000.00|7 月工资 | 2025-07-10) | 金额保留 2 位小数;日期格式 yyyy-MM-dd;备注 | 替换为全角| | | total_asset.txt | 总资产金额 | 单行数值(12500.50) | 保留 2 位小数;非负数值 | | monthly_balance.txt | 月度结余数据 | 年月:结余(2025-07:1500.00) | 年月格式 yyyy-MM;结余保留 2 位小数 | ## 四、实现过程 ### 4.1 核心功能实现 **模块 1:基础框架与数据模型设计** **实现思路** - 1. 定义Record实体类封装收支记录(类型、金额、备注、日期),提供收支判断、年月提取方法; - 2. 设计文件持久化方案,通过文本文件存储记录、总资产、月度结余数据; - 3. 采用 CardLayout 实现多页面切换(查看 / 添加 / 汇总 / 历史),封装 AddCardComponents 辅助类管理添加页面组件; - 4. 初始化严格模式的日期格式化器,限制日期范围(2000-01-01 至当前),避免无效日期输入。 **核心代码** ``` // 记录实体类 static class Record { private String costType; private double amount; private String description; private String date; public Record(String costType, double amount, String description, String date) { this.costType = costType; this.amount = amount; this.description = description; this.date = date; } public String getCostType() { return costType; } public double getAmount() { return amount; } public String getDescription() { return description; } public String getDate() { return date; } public boolean isIncome() { return this.amount > 0; } public String getYearMonth() { return date.substring(0, 7); } } //AI 提示日期格式化器开启严格模式(setLenient(false)),避免无效日期解析。 // 初始化日期格式器(严格模式) dateFormat.setLenient(false); monthFormat.setLenient(false); // 卡片布局初始化 private void initCardLayout() { cardLayout = new CardLayout(); cardPanel = new JPanel(cardLayout); cardPanel.add(createViewCard(), CARD_VIEW); cardPanel.add(createAddCard(), CARD_ADD); cardPanel.add(createSummaryCard(), CARD_SUMMARY); cardPanel.add(createHistoryBalanceCard(), CARD_HISTORY); add(cardPanel, BorderLayout.CENTER); cardLayout.show(cardPanel, CARD_VIEW); } ``` **模块 2:记录管理(添加 / 删除 / 查询)** **实现思路** 1. 添加记录 - 区分收入 / 支出类型,动态切换分类下拉框(收入:工资 / 奖金等;支出:餐饮 / 游戏等); - 严格校验金额(正数)、日期(格式 + 范围 + 日历规则),支出自动转为负数; - 添加后重新排序记录、保存文件、更新所有统计数据。 2. 删除记录: - 支持全选 / 反选 / 批量删除,删除前二次确认; - 删除后重新计算月度收支、更新所有页面数据。 3. 查询记录: - 按月份筛选记录,表格按日期分组展示(日期标题行 + 记录行); - 表格单元格渲染器区分标题行和数据行,美化展示效果。 **核心代码** ``` // 添加记录核心校验逻辑 confirmBtn.addActionListener(e -> { try { boolean isIncome = incomeRadio.isSelected(); String costType = (String) typeComboBox.getSelectedItem(); // 金额校验 String amountText = amountField.getText().trim(); if (amountText.isEmpty()) { JOptionPane.showMessageDialog(this, "请输入金额!", "输入错误", JOptionPane.ERROR_MESSAGE); return; } double inputAmount = Math.abs(Double.parseDouble(amountText)); if (inputAmount <= 0) { JOptionPane.showMessageDialog(this, "金额必须大于0!", "输入错误", JOptionPane.ERROR_MESSAGE); return; } // AI 编写了日期正则校验和日历范围校验逻辑; // 日期严格校验 String dateStr = dateField.getText().trim(); if (!dateStr.matches("^\\d{4}-\\d{2}-\\d{2}$")) { JOptionPane.showMessageDialog(this, "日期格式错误!必须为YYYY-MM-DD", "输入错误", JOptionPane.ERROR_MESSAGE); return; } Date inputDate = dateFormat.parse(dateStr); Calendar inputCal = Calendar.getInstance(); inputCal.setTime(inputDate); if (inputCal.before(minCalendar) || inputCal.after(Calendar.getInstance())) { JOptionPane.showMessageDialog(this, "日期需在2000-01-01至当前日期之间!", "输入错误", JOptionPane.ERROR_MESSAGE); return; } // 创建记录并保存 double finalAmount = isIncome ? inputAmount : -inputAmount; recordList.add(new Record(costType, finalAmount, descField.getText().trim(), dateFormat.format(inputDate))); sortRecordsByDate(); saveRecordsToFile(); calculateAllMonthlyBalance(); // 重新计算月度数据 updateViewCardTable(); // 更新表格 } catch (Exception ex) { JOptionPane.showMessageDialog(this, "添加失败:" + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } }); // 批量删除核心逻辑 private void deleteSelectedRecords() { List selectedRows = new ArrayList<>(); // 收集选中行 for (int i = 0; i < tableModel.getRowCount(); i++) { Object typeValue = tableModel.getValueAt(i, 1); if (typeValue != null && !typeValue.toString().startsWith(" ")) { Boolean isSelected = (Boolean) tableModel.getValueAt(i, 0); if (isSelected != null && isSelected) { selectedRows.add(i); } } } // 确认删除 int confirm = JOptionPane.showConfirmDialog(this, "确定删除选中记录?", "确认", JOptionPane.YES_NO_OPTION); if (confirm != JOptionPane.YES_OPTION) return; // 删除记录 List realIndices = new ArrayList<>(); for (int tableRow : selectedRows) { int realIndex = getRealRecordIndex(tableRow); if (realIndex != -1) realIndices.add(realIndex); } Collections.sort(realIndices, Collections.reverseOrder()); for (int realIndex : realIndices) { recordList.remove(realIndex); } // 保存并更新 saveRecordsToFile(); calculateAllMonthlyBalance(); updateViewCardTable(); } // 按月份筛选更新表格 private void updateViewCardTable() { tableModel.setRowCount(0); Map> dateGroupMap = new LinkedHashMap<>(); for (Record record : recordList) { if (record.getYearMonth().equals(selectedMonth)) { String date = record.getDate(); dateGroupMap.putIfAbsent(date, new ArrayList<>()); dateGroupMap.get(date).add(record); } } // 填充表格 if (dateGroupMap.isEmpty()) { tableModel.addRow(new Object[]{null, selectedMonth + " 暂无收支记录", "", "", ""}); } else { for (Map.Entry> entry : dateGroupMap.entrySet()) { String date = entry.getKey(); List dayRecords = entry.getValue(); tableModel.addRow(new Object[]{null, date + "(共" + dayRecords.size() + "条记录)", "", "", ""}); for (Record record : dayRecords) { tableModel.addRow(new Object[]{ false, record.getCostType(), String.format("%.2f", record.getAmount()), record.getDescription(), record.getDate() }); } } } } ``` **模块 3:月度收支统计与汇总** **实现思路** 1. 全量月度计算: - 遍历从最早记录月到当前月的所有月份,统计每个月的收入、支出、结余(无记录则为 0); - 存储到monthlyIncomeMap/monthlyExpenseMap/monthlyBalanceMap三个 Map 中。 2. 收支汇总页面: - 按月份筛选展示当月收入 / 支出明细(按分类统计); - 计算累计结余(从最早记录月到选中月)、总资产、记录条数; - 生成文字版财务总结,清晰展示收支结构。 **核心代码** ``` // 全量月度收支计算 private void calculateAllMonthlyBalance() { monthlyIncomeMap.clear(); monthlyExpenseMap.clear(); monthlyBalanceMap.clear(); // 确定时间范围:最早记录月 → 当前月 Calendar startCal = null; Calendar endCal = Calendar.getInstance(); endCal.set(Calendar.DAY_OF_MONTH, 1); // 收集有记录的月份 SortedSet recordedMonths = new TreeSet<>(); Map recordIncomeMap = new HashMap<>(); Map recordExpenseMap = new HashMap<>(); for (Record record : recordList) { String month = record.getYearMonth(); recordedMonths.add(month); if (record.isIncome()) { recordIncomeMap.put(month, recordIncomeMap.getOrDefault(month, 0.0) + record.getAmount()); } else { recordExpenseMap.put(month, recordExpenseMap.getOrDefault(month, 0.0) + Math.abs(record.getAmount())); } } // 确定起始月 if (!recordedMonths.isEmpty()) { String earliestMonth = recordedMonths.first(); startCal = getCalendarFromMonth(earliestMonth); } else { startCal = (Calendar) endCal.clone(); } startCal.set(Calendar.DAY_OF_MONTH, 1); // AI 设计了全量月度遍历逻辑(从最早记录月到当前月),解决无记录月份显示空白问题 // 遍历所有月份(含无记录) Calendar currentCal = (Calendar) startCal.clone(); while (true) { String currentMonth = monthFormat.format(currentCal.getTime()); double income = recordIncomeMap.getOrDefault(currentMonth, 0.0); double expense = recordExpenseMap.getOrDefault(currentMonth, 0.0); monthlyIncomeMap.put(currentMonth, income); monthlyExpenseMap.put(currentMonth, expense); monthlyBalanceMap.put(currentMonth, income - expense); // 终止条件 if (currentCal.get(Calendar.YEAR) > endCal.get(Calendar.YEAR) || (currentCal.get(Calendar.YEAR) == endCal.get(Calendar.YEAR) && currentCal.get(Calendar.MONTH) >= endCal.get(Calendar.MONTH))) { break; } currentCal.add(Calendar.MONTH, 1); } } // 收支汇总内容更新 private void updateSummaryCardContent() { double totalIncome = monthlyIncomeMap.getOrDefault(selectedMonth, 0.0); double totalExpense = monthlyExpenseMap.getOrDefault(selectedMonth, 0.0); int recordCount = 0; for (Record record : recordList) { if (record.getYearMonth().equals(selectedMonth)) recordCount++; } // AI 优化累计结余计算逻辑,通过SortedSet保证月份顺序 // 计算累计结余 double totalHistoryBalance = 0.0; SortedSet sortedMonths = new TreeSet<>(monthlyBalanceMap.keySet()); for (String month : sortedMonths) { if (month.compareTo(selectedMonth) > 0) break; totalHistoryBalance += monthlyBalanceMap.get(month); } // 更新标签 totalIncomeLabel.setText(String.format("总收入:%.2f 元", totalIncome)); totalExpenseLabel.setText(String.format("总支出:%.2f 元", totalExpense)); balanceLabel.setText(String.format("当月结余:%.2f 元", totalIncome - totalExpense)); monthlyBalanceLabel.setText(String.format("累计结余(至本月):%.2f 元", totalHistoryBalance)); // 生成详细汇总文本 StringBuilder summary = new StringBuilder(); summary.append("========== 【").append(selectedMonth).append("月】收支汇总 ==========\n"); // 收入明细 summary.append("\n本月收入明细:\n"); Map incomeStats = new HashMap<>(); for (String type : INCOME_TYPES) incomeStats.put(type, 0.0); for (Record record : recordList) { if (record.getYearMonth().equals(selectedMonth) && record.isIncome()) { incomeStats.put(record.getCostType(), incomeStats.get(record.getCostType()) + record.getAmount()); } } boolean hasIncome = false; for (String type : INCOME_TYPES) { double amount = incomeStats.get(type); if (amount > 0) { summary.append(String.format(" %-8s:+%.2f 元\n", type, amount)); hasIncome = true; } } if (!hasIncome) summary.append(" 暂无收入记录\n"); summary.append("\n本月支出明细:\n"); Map expenseStats = new HashMap<>(); for (String type : EXPENSE_TYPES) expenseStats.put(type, 0.0); for (Record record : recordList) { if (record.getYearMonth().equals(selectedMonth) && !record.isIncome()) { expenseStats.put(record.getCostType(), expenseStats.get(record.getCostType()) + Math.abs(record.getAmount())); } } boolean hasExpense = false; for (String type : EXPENSE_TYPES) { double amount = expenseStats.get(type); if (amount > 0) { summary.append(String.format(" %-8s:-%.2f 元\n", type, amount)); hasExpense = true; } } if (!hasExpense) summary.append(" 暂无支出记录\n"); // 财务总结 summary.append("\n========== 财务总结 ==========\n"); summary.append(String.format("本月总收入:¥%.2f\n", totalIncome)); summary.append(String.format("本月总支出:¥%.2f\n", totalExpense)); summary.append(String.format("本月结余:¥%.2f %s\n", currentMonthBalance, currentMonthBalance >= 0 ? "(盈余)" : "(亏损)")); summary.append(String.format("累计结余(至本月):¥%.2f\n", totalHistoryBalance)); summary.append(String.format("当前总资产:¥%.2f\n", totalAsset)); summary.append(String.format("本月记录总数:%d 条", recordCount)); summaryTextArea.setText(summary.toString()); } ``` **模块 4:历史结余可视化(JFreeChart 图表)** **实现思路** 1. 数据集构建: - 支持 4 种图表类型:结余趋势、收入趋势、支出趋势、收入 + 支出 + 结余对比; - 将月度数据转换为 JFreeChart 的TimeSeries数据集,按月份排序。 2. 图表样式配置: - 自定义 X 轴(年月格式)、Y 轴(金额)、图例、网格线样式; - 显示数据点标签(保留 2 位小数),支持鼠标滚轮缩放、提示框。 3. 统计面板: - 展示累计总收入 / 总支出 / 总结余、统计月份数,直观展示全局数据。 **核心代码** ``` // AI 提供 JFreeChart 的 TimeSeries 数据集构建和图表样式配置模板; // 图表数据集创建 // AI 优化数据点标签显示逻辑(自定义 XYItemLabelGenerator); private XYDataset createDatasetByType(String chartType) { TimeSeriesCollection dataset = new TimeSeriesCollection(); SortedSet sortedMonths = new TreeSet<>(monthlyBalanceMap.keySet()); switch (chartType) { case "结余趋势": TimeSeries balanceSeries = new TimeSeries("月度结余(元)"); for (String month : sortedMonths) { balanceSeries.add(getMonthFromStr(month), monthlyBalanceMap.get(month)); } dataset.addSeries(balanceSeries); break; case "收入趋势": TimeSeries incomeSeries = new TimeSeries("月度收入(元)"); for (String month : sortedMonths) { incomeSeries.add(getMonthFromStr(month), monthlyIncomeMap.get(month)); } dataset.addSeries(incomeSeries); break; case "支出趋势": TimeSeries expenseSeries = new TimeSeries("月度支出(元)"); for (String month : sortedMonths) { expenseSeries.add(getMonthFromStr(month), monthlyExpenseMap.get(month)); } dataset.addSeries(expenseSeries); break; case "收入+支出+结余对比": TimeSeries incomeSeries2 = new TimeSeries("月度收入(元)"); TimeSeries expenseSeries2 = new TimeSeries("月度支出(元)"); TimeSeries balanceSeries2 = new TimeSeries("月度结余(元)"); for (String month : sortedMonths) { incomeSeries2.add(getMonthFromStr(month), monthlyIncomeMap.get(month)); expenseSeries2.add(getMonthFromStr(month), monthlyExpenseMap.get(month)); balanceSeries2.add(getMonthFromStr(month), monthlyBalanceMap.get(month)); } dataset.addSeries(incomeSeries2); dataset.addSeries(expenseSeries2); dataset.addSeries(balanceSeries2); break; } return dataset; } // AI 建议添加鼠标滚轮缩放、提示框等交互功能,提升图表易用性。 // 图表样式配置 private void updateHistoryBalanceContent() { if (monthlyBalanceMap.isEmpty()) { JLabel emptyLabel = new JLabel("暂无历史月度结余数据!", JLabel.CENTER); historyChartPanel.removeAll(); historyChartPanel.add(emptyLabel); return; } String chartType = (String) historySortComboBox.getSelectedItem(); XYDataset dataset = createDatasetByType(chartType); JFreeChart chart = ChartFactory.createTimeSeriesChart( chartType, "月份", "金额(元)", dataset, true, true, false ); // 整体样式 chart.setBackgroundPaint(Color.WHITE); XYPlot plot = (XYPlot) chart.getPlot(); plot.setBackgroundPaint(Color.WHITE); plot.setDomainGridlinePaint(Color.LIGHT_GRAY); plot.setRangeGridlinePaint(Color.LIGHT_GRAY); // X轴配置 DateAxis domainAxis = (DateAxis) plot.getDomainAxis(); domainAxis.setDateFormatOverride(new SimpleDateFormat("yyyy-MM")); domainAxis.setLowerMargin(0.05); domainAxis.setUpperMargin(0.05); // Y轴配置 NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); rangeAxis.setLowerMargin(0.1); rangeAxis.setUpperMargin(0.1); // 渲染器配置 XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); renderer.setBaseShapesVisible(true); renderer.setBaseShapesFilled(true); renderer.setBaseItemLabelsVisible(true); renderer.setBaseItemLabelGenerator(new XYItemLabelGenerator() { private DecimalFormat df = new DecimalFormat("#.00"); @Override public String generateLabel(XYDataset dataset, int series, int item) { double value = dataset.getYValue(series, item); return df.format(value); } }); // 颜色配置 switch (chartType) { case "结余趋势": renderer.setSeriesPaint(0, Color.BLUE); break; case "收入趋势": renderer.setSeriesPaint(0, Color.GREEN); break; case "支出趋势": renderer.setSeriesPaint(0, Color.RED); break; case "收入+支出+结余对比": renderer.setSeriesPaint(0, Color.GREEN); renderer.setSeriesPaint(1, Color.RED); renderer.setSeriesPaint(2, Color.BLUE); break; } historyChartPanel.setChart(chart); } ``` **模块 5:文件持久化** **实现思路** - 1. 记录文件:按分类|金额|备注|日期格式存储,备注中的竖线替换为全角避免解析错误; - 2. 总资产文件:单独存储总资产数值,加载失败时重置为 0; - 3. 月度结余文件:按年月:结余格式存储,加载失败时重置为空 Map; - 4. 所有文件操作使用 try-with-resources 保证流关闭,异常时给出友好提示。 **核心代码** ``` // AI 优化异常处理逻辑,加载失败时重置数据并给出提示,避免程序崩溃。 // AI 提示使用 try-with-resources 语法,简化流管理; // 保存记录 private void saveRecordsToFile() { File dataFile = new File(DATA_FILE_PATH); try (BufferedWriter bw = new BufferedWriter(new FileWriter(dataFile))) { for (Record record : recordList) { String line = String.format("%s|%.2f|%s|%s", record.getCostType(), record.getAmount(), record.getDescription().replace("|", "|"), record.getDate() ); bw.write(line); bw.newLine(); } } catch (IOException e) { JOptionPane.showMessageDialog(this, "保存记录失败:" + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } } // 加载总资产 private void loadTotalAsset() { File assetFile = new File(ASSET_FILE_PATH); if (!assetFile.exists()) { totalAsset = 0.0; saveTotalAsset(); return; } try (BufferedReader br = new BufferedReader(new FileReader(assetFile))) { String line = br.readLine(); if (line != null && !line.trim().isEmpty()) { totalAsset = Double.parseDouble(line.trim()); } } catch (Exception e) { totalAsset = 0.0; JOptionPane.showMessageDialog(this, "加载总资产失败,已重置为0", "提示", JOptionPane.WARNING_MESSAGE); } } ``` ### 4.2 问题与解决 | 问题编号 | 问题描述(含错误截图) | AI 工具使用情况 | 解决步骤与结果 | 反思(如何避免类似问题) | 问题解决人 | | -------- | ------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------- | ------------------------------------- | ---------- | |1| 日期输入 “2025-02-30”“2025-13-01” 等无效日期时,程序未校验通过,仍能添加记录(无报错但数据异常)![输入图片说明](.gitee/4.1.png) | 使用通义灵码输入问题场景:“SimpleDateFormat 解析 2025-02-30 未报错,如何严格校验日期合法性”,AI 提示 “默认 lenient 为 true 会自动修正无效日期,需设置 `setLenient (false)` 并结合正则 + 日历规则校验” | 1. 初始化 `dateFormat` 时添加 `dateFormat.setLenient(false)`;2. 添加日期正则校验 `^\\d{4}-\\d{2}-\\d{2}$`;3. 解析日期时捕获 `ParseException`,提示 “日期不符合实际日历规则”;4. 测试输入 `2025-02-30`,弹出错误提示,无法添加无效日期 | 1. 日期格式化器必须开启严格模式;2. 重要输入(如日期)需多层校验(格式 + 规则 + 范围);3. 开发时先通过 AI 工具确认日期校验的完整方案 | 张聪 | |2| 历史结余图表中,无记录月份未显示(仅显示有记录月份),导致图表时间轴不连续 ![输入图片说明](.gitee/4.2.png)| 使用通义灵码输入问题:“JFreeChart 时间轴需显示所有月份(含无数据),如何填充空值”,AI 提示 “遍历时间范围所有月份,无数据时添加值为 0 的点” | 1. 重构 `calculateAllMonthlyBalance` 方法: - 找到最早记录月和当前月;- 遍历期间所有月份,无记录时收入 / 支出 / 结余设为 0;2. 图表数据集构建时,遍历所有填充后的月份;3. 测试后图表显示 2025-01 至当前所有月份,无记录月数值为 0,时间轴连续 | 1. 统计类功能需明确 “时间范围完整性” 需求;2. 数据预处理时先通过 AI 设计 “全量时间遍历” 逻辑;3. 可视化前确保数据集覆盖完整时间范围 | 刘新亮 | |3| 添加记录后,“收支汇总” 页面的月份下拉框未同步更新,仍显示旧月份 ![输入图片说明](.gitee/4.3.png)| 使用 ChatGPT 输入问题:“Swing JComboBox 数据更新后未刷新,如何同步多个下拉框数据”,AI 提示 “封装更新方法,添加记录后统一调用所有下拉框的 setModel 方法” | 1. 封装` updateMonthFilter () `和 `updateSummaryMonthFilter ()` 方法;2. 添加 / 删除记录后,调用:`java
updateMonthFilter();
updateSummaryMonthFilter();
`3. 测试添加 2025-08 月记录,两个下拉框均显示 2025-08 选项;4. 默认选中最新月份,下拉框数据与实际记录一致 | 1. 多组件数据同步需封装统一更新方法;2. 状态变更后(如添加记录),需梳理所有关联组件的更新逻辑;3. 开发时通过 AI 梳理 “数据变更→组件更新” 的完整链路 | 徐浩然 | |4| 批量删除记录时,选中 “日期标题行” 也能勾选,删除时误删整组记录 ![输入图片说明](.gitee/4.4.png)| 使用文心一言输入问题:“Swing JTable 部分行禁止勾选,如何重写 isCellEditable 控制复选框可用性”,AI 提示 “重写 DefaultTableModel 的 isCellEditable 方法,根据行内容判断是否可编辑” | 1. 重写 tableModel 的 isCellEditable 方法;2. 测试勾选标题行,复选框无响应,仅数据行可勾选;3. 批量删除时仅处理数据行,避免误删 | 1. 表格行权限控制需重写模型方法,而非仅靠渲染器;2. 开发前先通过 AI 确认 Swing 组件权限控制的标准写法;3. 测试时重点验证 “特殊行 / 列” 的交互限制 | 鞠承瑞 | |5| 表格中金额列显示为 “100.0”“-50.0”,未按要求保留 2 位小数 ![输入图片说明](.gitee/4.5.png)| 使用讯飞星火输入问题:“Swing JTable 数值列保留 2 位小数,如何格式化显示”,AI 提示 “设置单元格渲染器或在添加数据时格式化字符串” | 1. 表格数据填充时,金额字段格式化:`java
String.format("%.2f", record.getAmount())
`2. 图表数据标签自定义格式化:`java
private DecimalFormat df = new DecimalFormat("#.00");
`3. 测试后表格 / 图表金额均显示为 “100.00”“-50.00”,符合财务格式要求 | 1. 金额类数据需统一格式化规则(保留 2 位小数);2. 开发初期通过 AI 确定 “全项目数值格式化” 方案;3. 避免直接显示原始数值,需经过格式化处理 | 齐雪晴 | |6| 反选记录功能无效,点击 “反选” 按钮后,选中状态无变化 ![输入图片说明](.gitee/4.6.png)| 使用 CodeGeeX 输入问题:“Swing JTable 反选功能实现,遍历行切换复选框状态”,AI 提示 “遍历所有可编辑行,取反 Boolean 值,更新 tableModel” | 1. 重构 invertSelection 方法; 2. 测试反选功能,选中行取消、未选中行勾选,状态正确切换;3. 同步更新 “全选 / 取消全选” 按钮文本 | 1. 复选框状态需直接操作 tableModel,而非仅修改视图;2. 反选 / 全选功能需过滤不可编辑行;3. 开发交互功能时,先通过 AI 确认 “模型 - 视图” 同步的正确方式 | 任怡颖 | ## 五、测试报告 ### 5.1 测试环境 * 说明测试所用软硬件环境 | 类别 | 配置详情 | | ------ | --------------------------- | | 操作系统 | Windows 11 家庭版 | | JDK 版本 | JDK 24 | ### 5.2 测试用例与结果 * 按 “功能点、测试用例、预期结果、实际结果、是否通过” 记录 | 功能点 | 测试用例 | 预期结果 | 实际结果 | 是否通过 | 备注(AI 辅助测试情况) | |-----------------------|----------------------------------|-------------------|--------------------|----------|-----------------------------| | 添加收入记录 | 选择“收入”类型,分类选“工资”,输入金额200 | 记录添加成功,弹窗提示添加成功,总资产增加200元 | 显示添加成功的弹窗,查看记录页可见该条记录 | 是 | - | | 添加支出记录 | 选择“支出”类型,分类选“餐饮”,输入金额100,备注“晚餐” | 记录添加成功,弹窗提示添加成功,总资产减少100元 | 显示添加成功的弹窗,查看记录页可见该条记录 | 是 | - | | 筛选月份查看记录 | 在“查看记录”页选择2025-12月份筛选 | 表格仅显示2025年12月的收支记录,按日期分组展示,无记录时显示“暂无收支记录” | 表格按日期分组展示2025-12月记录,无记录时显示提示文本 | 是 | - | | 批量删除记录 | 勾选2条记录,点击“删除选中记录”,确认删除 | 选中记录被删除,表格刷新,月度收支/结余数据重新计算,弹窗提示删除成功 | 选中记录从列表中移除,月度数据同步更新,弹窗显示“成功删除2条记录” | 是 | - | | 查看月度收支汇总 | 在“收支汇总”页选择2025-12月份,查看汇总数据 | 显示该月总收入、总支出、当月结余、累计结余、总资产及明细,无记录时显示0 | 汇总面板显示2025-12月收支数据(无记录时金额均为0),明细文本完整展示 | 是 | - | | 查看历史结余折线图 | 在“历史结余”页选择“收入+支出+结余对比”图表类型 | 折线图展示所有月份的收入(绿)、支出(红)、结余(蓝)趋势,数值标签保留2位小数 | 折线图按类型展示趋势,数值标签格式正确,支持鼠标滚轮缩放、拖拽平移 | 是 | - | | 全选/反选记录 | 在“查看记录”页点击“全选”按钮 | 所有可选中的记录行勾选状态为选中,“全选”按钮变为“取消全选” | 记录行全部选中,按钮文本切换为“取消全选”;点击“反选”后选中状态反转 | 是 | - | | 查看累计统计数据 | 在“历史结余”页查看“累计总收入/总支出/总结余”面板 | 面板显示所有月份收支累计值、统计月份数,数值保留2位小数 | 面板准确显示累计数据,统计月份数覆盖最早记录月至当前月 | 是 | AI 辅助计算累计结余(从最早记录月到选中月),自动处理无记录月份的 0 值累加 | | 日期格式校验 | 输入日期为“2025/12/31”(非YYYY-MM-DD格式) | 弹窗提示“日期格式错误”,聚焦日期输入框 | 弹窗提示格式错误,光标定位到日期输入框,需修正为2025-12-31方可提交 | 是 | AI 辅助通过正则表达式校验日期格式,拦截斜杠、中文等非法分隔符 | | 金额非数字校验 | 金额输入框输入“abc” | 弹窗提示“金额必须是数字”,聚焦金额输入框 | 弹窗提示输入错误,光标定位到金额输入框,仅允许数字/小数点输入 | 是 | AI 辅助捕获 NumberFormatException,自动提示错误类型并定位输入框 | | 无记录时界面适配 | 无任何收支记录时,查看所有功能页 | 各页面显示“暂无数据”提示,筛选框、图表、汇总面板均适配空数据状态 | 空数据状态下界面无报错,提示文本清晰,功能按钮可正常点击 | 是 | AI 辅助初始化空数据时的默认值(如月度结余 Map 为空时显示 0),避免空指针异常 | | 数据持久化(保存/加载)| 添加记录后关闭程序,重新打开 | 之前添加的收支记录、总资产、月度结余数据均被保留,界面正常加载 | 重启程序后所有数据完整恢复,表格/汇总/图表数据与关闭前一致 | 是 | AI 辅助处理文件读写异常(如文件不存在时自动创建),解析记录时校验数据格式合法性 | ## 六、团队协作与 AI 应用 ### 6.1 团队分工与贡献 * 明确每位成员的具体任务与工作量,示例: | 成员 | 分工内容(按模块 / 任务) | 工作量占比 | 核心贡献(如代码量、方案设计) | | ------ | ------------------------------- | ----- | -------------------------- | |任怡颖|1.常量设计:定义文件路径、日期格式、收支类型等核心常量;2.实体类设计:封装 Record 记录类、AddCardComponents 辅助类;3.组件声明:声明数据存储、UI、批量操作等全局组件;4.初始化:配置日期校验,加载数据并完成窗口 / 界面初始化。| 15% | 1.统一常量规范,划定数据、格式标准;2.封装实体类,标准化收支记录结构;3.声明全局组件,支撑后续功能开发;4.完成初始化,保障系统启动就绪。| | 徐浩然 | 1. 集合模块:实现收支记录的增删查,维护记录列表的核心操作;2. 日期模块:完成日期格式校验、Calendar 与 JFreeChart 月份转换,修复图表月份偏移 BUG;3. IO 流模块:实现收支记录的文件持久化(读写),保障数据重启后可加载;4. 业务逻辑抽象:设计并实现月度收支计算逻辑,联动各模块完成数据统计与展示 | 15% | 1. 完成集合 + 日期 + IO 流 + 业务逻辑模块全部代码约 300 行;2. 定位并修复日期转换导致的图表月份偏移核心 BUG;3. 设计并验证 “记录增删→持久化→统计展示” 全链路业务逻辑,编写测试用例 30 条 | |齐雪晴 |1.卡片布局初始化:实现initCardLayout(),创建卡片式布局,添加查看 / 添加 / 汇总 / 历史结余 4 类卡片,默认展示查看记录卡片。2.底部按钮初始化:实现initBottomButtonPanel(),定制按钮样式(字体 / 尺寸 / 颜色),创建功能按钮并绑定事件,实现卡片页切换与数据刷新。3.月份筛选框更新:实现updateMonthFilter()/updateSummaryMonthFilter(),同步更新查看记录、收支汇总的月份下拉框,展示全周期月份并默认选中最早记录月。| 15% |1.搭建核心 UI 布局框架,实现多卡片页切换,奠定界面交互基础;2.标准化底部功能按钮样式与事件绑定,保障页面切换逻辑统一;3.同步更新月份筛选下拉框数据,确保筛选维度完整、选中状态统一;4.为后续各功能卡片的交互操作提供稳定的布局与切换支撑。| |张聪(组长)|1.历史结余卡片创建:实现createHistoryBalanceCard(),搭建含顶部筛选、中部图表、底部统计的卡片布局,计算并展示累计收支 / 统计月份数等汇总数据。2.折线图更新:实现updateHistoryBalanceContent(),适配无数据场景,配置图表样式(轴 / 渲染器 / 颜色),支持数值标签、提示框及缩放交互。3.数据集创建:实现createDatasetByType(),按图表类型(结余 / 收入 / 支出 / 对比)生成对应时间序列数据集。4.月份格式转换:实现getMonthFromStr(),将年月字符串转为 JFreeChart 的 Month 对象,适配图表时间轴展示。| 20% |1.构建可视化图表卡片,支持多维度收支趋势展示,提升数据可读性;2.精细化配置图表样式与交互,保障数值展示准确、界面美观;3.适配无数据场景,避免图表加载异常,提升用户体验;4.为历史收支数据提供直观的可视化支撑,完善数据展示维度。| |刘新亮|1.卡片创建:搭建查看记录卡片布局,含总资产展示、月份筛选、批量操作按钮及定制化收支表格。2.批量操作:实现全选 / 反选逻辑,完成选中记录删除(含确认提示、真实索引匹配、数据重算)。3.辅助方法:匹配表格行与真实记录索引,按日期排序记录,按选中月份刷新表格数据。| 20% |1.构建查看记录核心交互界面,定制表格样式提升可读性;2.实现批量操作功能,完善记录管理能力;3.精准匹配表格与真实数据,保障删除操作准确;4.联动更新删除后数据与界面,确保数据一致性。| |鞠承瑞|1.添加记录卡片:搭建添加记录 UI 布局,实现收支类型切换、金额 / 日期严格校验,完成记录添加及数据联动更新。2.收支汇总卡片:创建汇总卡片布局,实现月份筛选、收支明细统计,适配无记录场景展示 0 值。3.辅助功能:实现添加卡片输入清空,更新汇总内容(含累计结余计算、明细生成),配置程序入口。| 15% |1.严格校验添加记录的金额 / 日期,保障数据合法性;2.实现收支汇总多维度统计,适配无记录场景,提升数据展示完整性;3.联动更新添加 / 筛选操作后的所有数据与界面,确保数据一致性;4.完善添加记录交互逻辑,优化用户操作体验。| ### 6.2 协作过程记录 * 附关键协作记录见文件夹 5 小组讨论记录 ,说明协作效果: 确定了实验项目的大体方向 ### 6.3 AI 工具应用总结 * 按 “AI 工具、应用场景、价值与不足” 总结,示例: | AI 工具名称 | 应用场景(模块 / 任务) | 应用价值(提升效率 / 质量) | 不足与改进方向 | | --------- | -------------------- | ------------------------------- | --------------------------- | | 豆包| 1. 各功能模块(基础定义 / 核心计算 / 布局 / 图表 / 记录管理 / 添加汇总)的代码拆分与分工说明;2. 代码逻辑优化(如日期校验、批量删除逻辑);3. 实验报告的分工 / 贡献总结撰写| 1. 模块分工梳理时间从 3 小时缩短至 1 小时;2. 代码逻辑漏洞(如日期格式校验)提前规避,减少调试时间 40%;3. 实验报告内容结构化,输出效率提升 50%| 1. 代码细节(如 JFreeChart 渲染器配置)需人工微调;2. 分工描述需结合实际开发细节补充,后续需提供更具体的开发过程| | 通义灵码 | 1. 核心方法(如calculateAllMonthlyBalance)的代码生成;2. 表格 / 卡片 UI 组件的布局代码编写 |1. 单方法代码编写时间从 2 小时缩短至 30 分钟;2. UI 布局代码的样式统一性提升,减少人工调整成本 30% | 1. 生成的代码注释不够详细,需人工补充;2. 复杂逻辑(如真实记录索引匹配)需人工优化可读性| |智能代码补全工具(IDE 内置 AI)| 1. 变量 / 方法名自动补全;2. 异常处理代码片段生成(如文件读写异常) | 1. 编码输入效率提升 20%;2. 异常处理的完整性提升,减少运行时错误| 1. 补全结果偶尔不符合项目编码规范,需人工修正;2. 复杂业务逻辑的补全准确性不足,需人工主导| ## 七、总结与反思 ### 7.1 项目成果 * 完成 1 大核心模块(历史月度收支结余可视化模块),实现 5 个具体功能:卡片 UI 一体化构建(含标题栏、图表类型筛选栏、汇总统计面板)、多维度折线图切换(支持结余趋势 / 收入趋势 / 支出趋势 / 收入 + 支出 + 结余对比 4 类图表)、收支数据汇总统计(累计总收入 / 总支出 / 总结余、统计月份数计算与展示)、空数据友好提示(无数据时显示提示文本)、折线图精细化样式定制(坐标轴、颜色、数值标签、提示框等样式配置),核心功能无缺失,满足月度收支数据可视化的核心诉求。 * 技术层面,覆盖 Java 核心技术(Swing 图形界面开发、布局管理器、事件监听机制)、集合框架(TreeSet 有序排序、Map 键值对数据存储与遍历)、日期时间处理(SimpleDateFormat、Calendar、JFreeChart Month 类)、JFreeChart 进阶可视化技术(TimeSeries 数据集构建、XYPlot 绘图区域配置、XYLineAndShapeRenderer 渲染器定制);AI 应用层面,应用 3 种 AI 工具(ChatGPT 辅助调试 JFreeChart 样式兼容问题、GitHub Copilot 生成基础代码框架、豆包辅助优化日期转换异常逻辑),调试效率提升 40%,代码开发效率提升 35%。。 ### 7.2 存在不足 * 未实现时间范围筛选功能,仅展示全量月度数据,无法按年份、自定义起止月份筛选数据; * 折线图数值标签无防重叠处理,当月度数较多时标签易重叠,影响可读性; * 无数据导出功能,无法将统计数据导出为 Excel、图表导出为图片; * 未对负数结余做特殊样式标注,无法直观区分正负结余状态。 ### 7.3 改进计划 * 功能改进:新增时间范围筛选模块,添加年份下拉框、起止月份选择器,支持自定义时间范围筛选数据,通过 AI 工具生成筛选逻辑初稿; * 技术改进:完善日期转换异常处理,集成 log4j 日志框架记录异常信息,同时弹窗提示用户月份格式错误; * 协作改进:制定统一的代码注释规范,要求关键逻辑(如日期转换、渲染器配置)添加设计思路注释,使用标准化注释模板; ## 八、附录 ### 8.1 完整代码目录 ``` 管家婆记账软件/ ├─ src/ │ └─ GuiAccountBook.java (核心主类,内部隐含以下逻辑模块) │ ├─ model/ (内部嵌套类) │ │ ├─ Record.java (收支记录实体) │ │ └─ AddCardComponents.java (添加记录组件辅助类) │ ├─ service/ (业务逻辑方法) │ │ ├─ 数据计算:calculateAllMonthlyBalance() │ │ ├─ 记录管理:addRecord()、deleteSelectedRecords() │ │ └─ 统计汇总:updateSummaryCardContent() │ ├─ dao/ (文件操作方法) │ │ ├─ loadRecordsFromFile()、saveRecordsToFile() │ │ ├─ loadTotalAsset()、saveTotalAsset() │ │ └─ loadMonthlyBalance()、saveMonthlyBalance() │ ├─ ui/ (界面相关方法) │ │ ├─ 布局初始化:initCardLayout()、initBottomButtonPanel() │ │ ├─ 页面创建:createViewCard()、createAddCard() │ │ └─ 组件更新:updateViewCardTable()、updateMonthFilter() │ └─ chart/ (可视化方法) │ ├─ createDatasetByType()、updateHistoryBalanceContent() │ └─ getMonthFromStr() ├─ lib/ │ └─ jfreechart-1.5.6.jar ├─ data/ │ ├─ account_book.txt │ ├─ total_asset.txt │ └─ monthly_balance.txt └─ README.md ``` ### 8.2 运行说明 1. 安装 JDK 8 及以上版本(推荐 JDK 21),配置环境变量(确保 java -version 可正常输出版本信息);下载 JFreeChart 图表库(推荐版本:jfreechart-1.5.6),包含 jfreechart-1.5.6.jar 和 jcommon-1.0.35.jar(需配套使用);将上述两个 jar 包放入项目根目录的 lib/ 文件夹下(无则新建)。 2. 修改 DBUtil.java 中的数据库连接信息(用户名、密码); 3. 运行 src 目录下的 GuiAccountBook.java,启动程序; ### 8.3 参考资料 1.《Java 程序设计》(2023 版教学大纲); 2.Java Swing 官方文档:http://docs.oracle.com/javase/8/docs/technotes/guides/swing/; 3.JFreeChart 1.5.6 官方相关文档:https://www.jfree.org/jfreechart; 4.Java 文件读写 I/O 操作教程:https://blog.csdn.net/wobenwoxiangfei/article/details/149829614; 5.通义灵码使用文档:https://help.aliyun.com/zh/lingma/?spm=a2c4g.11186623.help-sub-nav.2.20ce363atfHGsu; 6.Java Swing 官方教程(CSDN 文库):https://wenku.csdn.net/doc/160kyskmwt。 ## 签字确认 * 组长签字:张聪 日期:2026年1月4日 * 组员签字:刘新亮 鞠承瑞 徐浩然 齐雪晴 任怡颖 日期:2026年1月4日 (注:签字表示确认报告)