2026秋软工团队实践现场编程作业
| 作业所属课程 | H202601软件工程与软件工程实践 |
|---|---|
| 作业要求 | 现场编程 |
| 作业目标 | 通过团队协作开发一款具备收支记录、数据查询与统计分析功能的个人记账软件,掌握模块化开发、GitHub协作、软件测试与演示的基本流程,提升团队协作能力和实际编程能力。 |
| 团队名称 | 组长说的都队 |
| 团队成员 | 任宝硕、黄亦博、吴莉芊、陈果、陈铮、向飞燕、卓滢、郭航铭、吴昊阳、颜志航、林烨、曾子瑞 |
| GitHub 仓库 | renbaoshuo/202601-software-engineering-lab1107 |
项目演示地址:https://se-lab-1107.baoshuo.dev
成员分工
我们将分工定为了主体架构和十一个子模块,于是产生了下面的分工:
- 主体架构:任宝硕。
- 收支记录:吴莉芊,#3,#20。
- 账单流水:陈果,#11,#12。
- 账单编辑:吴昊阳,#8,#23。
- 账单查询:曾子瑞,#1,#16。
- 收支总览:林烨,#6,#14。
- 分类分析:黄亦博,#5,#27。
- 收支趋势:向飞燕,#9,#18。
- 预算管理:颜志航,#10,#24。
- 数据管理:郭航铭,#7,#26。
- 账单日历:陈铮,#2,#25。
- 储蓄目标:卓滢,#4,#13。
我们的开发模式是先由组长搭建好主体架构,之后各个成员分别开发自己的模块来填充项目的血肉,大家从自己的开发分支提 PR 到 master 上来上线功能。
在这个过程中,我们充分地进行了 AI Coding、AI CR 等 Agentic Development 的实践。我们采用了 Codex、ZCode、Trae、Claude Code、GitHub Copilot 等多款 AI 辅助编程工具来辅助编程,并引入了 GitHub Copilot、CodeRabbit 等工具来辅助完成合入前的代码审查。
运行环境
使用最新版 Google Chrome 浏览器打开 https://se-lab-1107.baoshuo.dev 即可使用。
项目展示
个人记账本支持记录日常收支、查询账单、分析消费结构,以及管理月度预算和储蓄目标。
记录收支
「记一笔」支持录入收入和支出,记录日期、金额、类别和备注。早餐、地铁、工资等常见账单可通过快捷模板填写。「智能识别」可从一句话中提取账单信息,例如将「昨天打车花了 25」填写为前一天的 ¥25.00 交通支出。

查看、编辑与查询账单
「账单」可查看全部流水或最近 5 条记录,并支持编辑已有账单。全部账单可按日期或金额排序。查询时可组合日期区间、收支类型、类别、金额区间和关键词。关键词匹配类别或备注,图中展示了金额至少为 ¥10.00 的交通支出。

收支总览
「概览」汇总全部账单,分别展示总收入、总支出和当前结余。

账单日历
「账单日历」按月展示每日收支及当月合计。选择某一天,右侧显示当日收入、支出、结余和逐笔明细。图中 11 月 6 日的两笔支出合计 ¥224.00。

统计分析
「收支趋势」按日、周或月展示收入与支出的变化。「分类分析」以饼图或圆环图展示全部支出的构成,并在表格中列出各类别的金额和占比。图中购物占总支出的 57.9%。

月度预算和储蓄目标
「预算管理」支持按月设置预算。页面根据当月的支出账单计算已花金额、剩余金额和使用率,也可以通过历史预算表查看各月的使用情况。

「储蓄目标」可为旅行等计划设定目标金额,并随时追加存入。卡片显示累计存入金额、目标金额和完成比例。图中的旅行基金已存 ¥1,200.00,完成目标的 40%。

数据导出、导入与恢复
「数据管理」支持将账单、预算和储蓄目标一并导出为 JSON,也可将账单导出为 CSV,便于在表格软件中查看。

导入时,页面先校验文件并展示记录摘要。CSV 可用于追加账单,完整 JSON 备份可用于覆盖现有数据。

「备份管理」可以创建包含账单、预算和储蓄目标的快照。「恢复数据」支持选择应用内备份或上传 JSON 备份,确认后将数据恢复到所选快照。覆盖导入和恢复前,系统会自动备份当前数据。

核心代码
保存账单
账单表单先校验输入,再将金额从元转换为整数分。新增和编辑共用一套表单,根据是否已有账单选择写入方式。以下节选自 src/components/TransactionForm.tsx 的 handleSave。
if (saving || !validate() || !category) return;
const input = {
type,
// 用整数分存储,便于后续汇总收支。
amount: toCents(Number(amount)),
category,
date,
note: transaction ? note : note.trim(),
};
// 此处省略保存状态设置和外层异常处理。
if (transaction) {
const updated = await transactions.update(transaction.id, input);
if (!updated) {
setSaveError("这笔账单已不存在");
return;
}
}
else {
await transactions.add(input);
}
解析一句话账单
src/utils/smartParse.ts 的 parseTransactionText 将一句话拆成表单字段。例如「昨天打车花了 25」会生成前一天的交通支出,金额为 2500 分。
export function parseTransactionText(text: string): SmartParseResult | null {
const input = text.trim();
if (!input) return null;
// 无法识别金额时交由表单提示,避免填入猜测的数值。
const amount = parseAmount(input);
if (amount === null) return null;
const type = parseType(input);
return {
type,
amount,
// 根据收支方向选择对应的分类,原句保留为备注。
category: parseCategory(input, type),
date: formatDate(parseDateText(input)),
note: input,
};
}
封装浏览器数据库
src/shared/db.ts 使用 Dexie 统一定义账单、月度预算和储蓄目标的 IndexedDB 表结构,并向页面提供类型明确的读写接口。以下以账单查询为例,列表读取和计数复用同一套筛选逻辑。
const db = new Dexie("personal-ledger") as Dexie & {
transactions: Table<Transaction, string>;
budgets: Table<MonthlyBudget, string>;
savingsGoals: Table<SavingsGoal, string>;
};
db.version(1).stores({
// 日期和金额作为二级索引,支持范围查询和排序。
transactions: "id, date, amount",
budgets: "month",
savingsGoals: "id",
});
const { transactions: bills, budgets: monthlyBudgets, savingsGoals: goals } = db;
// 此处省略查询构造函数和其他账单接口。
export const transactions = {
list(query: TransactionQuery = {}) {
const { order = "desc", offset = 0, limit } = query;
const rows = findTransactions(query);
if (order === "desc") rows.reverse();
if (offset > 0) rows.offset(offset);
if (limit !== undefined) rows.limit(limit);
return rows.toArray();
},
count(filter: TransactionFilter = {}) {
// 与列表共用筛选逻辑,让匹配条数对应同一组条件。
return findTransactions(filter).count();
},
};
汇总分类支出
分类图表使用 src/utils/categoryTotals.ts 的 getCategoryTotals 按类别汇总支出,并按金额从高到低排列。
export function getCategoryTotals(records: Transaction[]): CategoryTotal[] {
const expenses = records.filter(record => record.type === "expense");
const groups = groupBy(expenses, record => record.category);
// 合并实际账单中的类别,兼容导入数据携带的自定义分类。
const names = union(DEFAULT_CATEGORIES.expense, Object.keys(groups));
const totals = names.filter(name => Object.hasOwn(groups, name)).map(name => ({
name,
amount: sumBy(groups[name], "amount"),
}));
return orderBy(totals, ["amount"], ["desc"]);
}
恢复本地备份
src/shared/db.ts 的 ledgerBackup.restore 在一个 IndexedDB 事务内替换账单、预算和储蓄目标。
restore(backup: LedgerBackup) {
return db.transaction("rw", ledgerTables, async () => {
// 清空和写入放在同一事务,写入失败时保留恢复前的数据。
await Promise.all(ledgerTables.map(table => table.clear()));
await Promise.all([
bills.bulkAdd(backup.transactions),
monthlyBudgets.bulkAdd(backup.budgets),
goals.bulkAdd(backup.savingsGoals),
]);
});
},
项目亮点
可使用、可迁移、很安全
我们实现的是一个真实线上可访问、可使用的项目。数据完全在浏览器端存储,不上云,并且完成了数据导出、迁移等功能,保证用户数据的安全与可持续使用。
在线访问地址:https://se-lab-1107.baoshuo.dev
一句话记账与快捷模板
输入「昨天打车花了 25」,系统会识别日期、支出类别和金额,自动填入可编辑的账单表单。早餐、地铁、工资等常见账单也可通过快捷模板录入,减少重复填写。

月度预算与储蓄计划
预算管理根据当月账单自动计算已花金额、剩余金额和预算使用率。储蓄目标支持为旅行等计划设定目标金额,并随每次存入更新已存金额和完成比例。

AI 辅助开发
开发中使用各类 Coding Agent 协助实现、排查和验证。仓库根目录的 AGENTS.md 为 Agent 提供统一规范,明确页面、组件和工具函数的目录归属,划定入口、路由和共享数据契约等冻结文件,并要求复用共享数据接口和各类工具函数。修改后运行 linter 检查,并按小功能点分别提交,提交标题统一使用 feat、fix 或 chore 前缀。团队并行开发时,这些约定减少了反复说明要求的成本,也让改动更容易审查和合并。
我们还使用 GitHub Copilot 对 PR 的改动做初步复审,将潜在的逻辑错误、遗漏的边界条件和改进建议标注到具体代码行。成员可以沿评论直接定位并验证问题,减少逐行筛查和来回沟通的时间,把精力放在业务行为和设计取舍上。团队成员只需要检查是否采纳建议、修订是否通过,省去了大量重复的审查工作。
协作记录
我们的项目地址在 renbaoshuo/202601-software-engineering-lab1107。
协作时各分支的提交情况(部分)如图所示:

所有组员也都参与了代码编写:

PR 情况:

成员总结
052402241黄亦博:这次团队编程我负责分类分析模块,主要实现消费类别的金额、占比和饼图展示。一开始比较担心大家分开写的模块怎么合到一起,实际做下来发现,提前统一数据格式、公共接口和代码规范很重要,也能减少后续对接的麻烦。此外我也发现,commit 拆得细还不够,push 和 PR 也应该及时、适量,避免一次积累太多改动,增加组长的审阅负担。
102401301吴莉芊:这次现场编程我负责收支记录模块,完成了日期、金额、类别、备注的基础录入与校验、两种保存方式,以及快捷模板和一句话智能识别,并按约定的整数分金额、日期结构写入本地 IndexedDB,保存后广播事件通知总览、流水模块刷新。最大的体会是接口契约要先对齐:严格复用统筹的 db.ts 和 types.ts,联调几乎没返工;把功能拆成小 commit 逐个验证,也让合并和排错轻松很多。
102401302陈果:这次团队编程我负责账单流水页面。我在独立分支复用分页查询接口做页面接入和交互。最初我没按规范拆细提交,导致合并请求过程比较混乱,后来在组长指导下做了修正。持续集成里的代码检查、类型检查和构建也帮我提前拦下了代码风格和类型问题。我整体体会到统一数据格式、明确模块接口和工程化流程对协作效率的重要性,也感受到团队规范带来的实际收益。
102401303陈铮:在本次团队编程中我负责账单日历模块。按"从 master 拆分支、推送、提 PR"的流程协作,体会到分支命名和提交粒度会直接影响组长的合并效率。过程中也踩了几个坑:日期在模块加载时被固定,隔天进入页面仍显示旧日期;分类名为 __proto__ 时 emoji 查找会取到原型对象,导致页面白屏,花了不少时间去修缮。另外把日历上的圆点换成按当日结余正负显示的颜文字,算是这次短时间下为数不多的自由发挥。整体收获主要是不再只顾写完自己的页面,而是写代码前先想清楚改动会影响谁,以及怎么把改动拆成清晰的提交。
102401304向飞燕:这次团队编程我负责的是「收支趋势图表」模块。最大的收获是真正体验了多人协作的完整流程:以前写代码都是自己从头写到尾,这次要从认领 issue、基于最新代码建分支开始,到提交 PR、等 CI 检查和代码审查,最后被合并,每一步都有明确的规范。过程中也踩了坑:环境配置报错、依赖装错、分支和主分支冲突,每解决一个都对 Git 的理解更深一点。最大的体会是,团队项目里约定比技巧更重要,目录结构、提交格式、数据接口都统一了,几个人的代码才能顺利拼到一起。
102401305卓滢:我负责的是储蓄目标模块。这次团队编程让我体会到,模块开发并不只是简单的CRUD,而是要考虑到各种边界情况。最初我主要关注怎么把新建目标和存入金额这几个核心功能跑通,初期开发非常顺利,但在代码评审阶段却遇到了不少问题:我没有考虑到直接对储蓄进度进行计算并四舍五入,结果存入 99.5% 就会显示成 100%这样的细节问题。同时我还认识到,在多人协作里,还要及时关注主干更新、保持分支同步,才能减少合并时的麻烦。
102401217颜志航:本次现场编程中,我作为纯小白,负责了模块“预算管理”的开发,实现了多月份预算的增删改查以及当月支出的异步聚合与实时追踪;在技术实现上,我设计了绿、黄、红三色动态预警进度条以直观展示超支状态,严格遵循“分存元显”保证金额精度,并封装了安全百分比函数防止除零报错,同时加入了删除需3秒内二次确认的防误触设计以及切换月份自动预填表单的友好交互。在开发过程中,我遇到了很多挑战,但好在组长统筹有度,在队友全程指点下,我克服大部分困难并一步一步完成基本任务,受益匪浅。
102401335郭航铭:本次团队编程让我体会到,清晰的协作规范比单纯增加人手更重要。我们按照项目约定拆分任务,通过细粒度提交减少冲突。遇到并发写入、CSV 安全等问题时,通过代码审查和问题复盘及时发现并修正,也让我认识到边界情况、数据安全和可恢复性同样属于功能质量的一部分。团队的及时沟通和统一规范也很关键。提交信息、目录结构、类型契约和验证命令保持一致后,代码合并与问题定位都更高效。
102401337吴昊阳:本次团队作业,我负责的模块是“账单编辑”,包括账单编辑、删除单个账单和批量删除账单。开发初期我主要关注功能本身,经过组长指导,才意识到模块开发不仅要实现功能,还要考虑与团队项目的整体衔接,比如统一的数据格式、明确的接口定义和状态管理方式。通过分支开发、提交 PR、代码审查的完整流程完成了协作。这次经历让我熟悉了团队开发的规范流程,也认识到及时沟通的重要性。
102401339任宝硕:在这次个人记账本项目中,我主要负责搭建项目骨架和整合各模块。我实现了基于 IndexedDB 的本地数据层,统一了分类、金额和日期处理,并配置了自动检查和部署。在 GitHub 上,我评审和合并了组员的 PR,处理了合并冲突,协助把账单录入、筛选和编辑接入主流程,并修复了预算金额校验和备份导入中发现的问题。
102401513林烨:通过这次团队编程,我更加体会到统一规范和及时沟通的重要性。提前明确数据格式、接口、目录结构和代码风格,可以让我更顺利地完成自己的模块,并减少后续对接和修改的时间。在开发过程中,我也进一步熟悉了分支开发、commit、PR 和 CI 检查等协作流程,提高了自己的团队协作能力和开发效率。
102401514曾子瑞:这次团队编程让我体会到,模块开发不仅要实现功能,还要考虑如何与团队项目衔接。我负责账单查询模块,最初主要关注组件和查询逻辑,经过代码审查后,进一步完成了页面接入,并优化了切换页码时重复读取全部账单的问题。这个过程让我认识到统一数据格式、明确模块接口和及时沟通的重要性,也让我熟悉了通过分支、PR 和代码审查开展协作的流程。
PSP
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 10 | 10 |
| · Estimate | · 估计这个任务需要多少时间 | 10 | 10 |
| Development | 开发 | 170 | 205 |
| · Analysis | · 需求分析(包括学习新技术) | 15 | 20 |
| · Design Spec | · 生成设计文档 | 10 | 10 |
| · Design Review | · 设计复审(和同事审核设计文档) | 10 | 10 |
| · Coding Standard | · 代码规范(为目前的开发制定合适的规范) | 5 | 10 |
| · Design | · 具体设计 | 15 | 20 |
| · Coding | · 具体编码 | 90 | 95 |
| · Code Review | · 代码复审 | 10 | 15 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 15 | 25 |
| Reporting | 报告 | 30 | 45 |
| · Test Report | · 测试报告 | 15 | 20 |
| · Size Measurement | · 计算工作量 | 5 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 10 | 15 |
| 合计 | 210 | 260 |

浙公网安备 33010602011771号