第四次结对作业
2026秋软件工程第二次结对作业:校园失物招领程序实现
| 项目 | 内容 |
|---|---|
| 结对同学博客 | https://www.cnblogs.com/chizhi |
| 本次作业博客 | https://i.cnblogs.com/posts/edit |
| GitHub 项目 | https://github.com/Feion-Chen/102402118-102402117 |
| 作业要求 | https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16744 |
| 小组成员 | 陈飞扬 102402118、蔡以恒 102402117 |
一、分工与完成情况
陈飞扬负责前端页面、LostFoundStore 数据逻辑、搜索和状态转换、单元测试与 README。蔡以恒负责根据第一次原型检查页面流程、补充物品分类和地点筛选需求,并核验发布、搜索、详情、状态更新等测试场景。两人共同完成页面检查、边界情况讨论和最终验证。
程序选择 WEB 形式,使用原生 HTML、CSS 和 JavaScript,不使用第三方框架。用户可以在 Chrome 或 Edge 中直接打开 index.html。
二、需求与实现范围
本次程序实现围绕以下闭环:
发布信息 → 浏览或搜索 → 查看详情 → 联系发布者 → 更新状态
已实现功能:
- 浏览校园失物和招领信息。
- 发布寻物信息或招领信息。
- 按物品名称、地点或描述搜索。
- 按类型、分类、地点筛选。
- 查看物品详情和联系方式。
- 一键复制联系方式。
- 在“我的发布”中修改状态。
- 表单校验、空结果提示、图片预览和响应式布局。
没有加入实名认证、即时聊天和地图定位,保持流程简单。
三、解题思路与代码设计
项目分为界面层、逻辑层和数据层。index.html 提供页面结构,src/app.js 处理 DOM、事件和 localStorage,src/store.js 负责校验、搜索和状态规则,tests/store.test.js 独立测试数据逻辑。逻辑层不依赖 DOM,因此可以在 Node.js 中直接运行测试。
搜索实现会把物品名称、分类、地点和描述合并为搜索文本,支持多个关键词跨字段匹配。核心代码:
const query = normalizeText(filters.query);
if (query) {
const haystack = normalizeText([
item.title,
item.category,
item.location,
item.description,
].join(' '));
const tokens = String(filters.query).trim().toLowerCase().split(/\s+/);
if (!tokens.every((token) => haystack.includes(normalizeText(token)))) {
return false;
}
}
状态转换不直接在界面中任意修改,而是由配置约束。寻物信息只能在“寻找中”和“已找到”之间切换,招领信息只能在“待认领”和“已归还”之间切换:
const STATUS_OPTIONS = {
lost: ['searching', 'found'],
found: ['holding', 'returned'],
};
updateStatus(id, status) {
const item = this.getById(id);
if (!STATUS_OPTIONS[item.type].includes(status)) {
throw new Error('该状态不适合当前信息类型');
}
item.status = status;
item.updatedAt = new Date().toISOString();
return item;
}
数据使用 localStorage 保存,刷新或重新打开页面后仍能保留。图片通过 FileReader 转为 Data URL 后随信息一起保存在本机,不需要上传服务器。
四、附加特点
- 分类与地点双重筛选,方便缩小搜索范围。
- 一键复制联系方式,减少手工抄写。
- 发布成功直接打开详情页,方便核对内容。
- 图片大小限制为 2 MB,并支持移除预览。
- 空结果状态提供“清除筛选”按钮。
- 逻辑层与界面层分离,后续可以在不修改页面的情况下替换数据存储方式。
五、目录说明与使用说明
102402118-102402117/
├── index.html
├── package.json
├── src/
│ ├── app.js
│ ├── store.js
│ └── styles.css
├── tests/
│ └── store.test.js
├── docs/
│ ├── architecture.svg
│ ├── user-flow.svg
│ ├── images/
│ ├── PSP.md
│ ├── 测试报告.md
│ └── 博客园提交稿.md
└── README.md
运行方法一:下载仓库后,用 Chrome 或 Edge 双击 index.html。
运行方法二:在项目根目录执行 python -m http.server 8080,然后访问 http://localhost:8080。
六、单元测试
测试使用 Node.js 自带的 node:test,不需要额外安装测试框架。运行命令:
node --test tests/store.test.js
共设计 14 个白盒测试用例,覆盖文本规范化、必填字段校验、非法类型、默认状态、名称搜索、跨字段搜索、组合筛选、新增信息、状态合法转换、非法状态、排序和统计。
test('T10 寻物信息不能设置为已归还', () => {
const store = new LostFoundStore([createItem(baseInput, { id: '1' })]);
assert.throws(
() => store.updateStatus('1', ITEM_STATUS.RETURNED),
/状态/
);
});
构造测试数据时不只测试正常流程,还考虑缺少字段、非法筛选、非法状态和不存在的 id。这样以后增加新的分类或状态时,可以先补测试再修改实现,减少回归问题。
七、遇到的问题与解决
问题一:最初使用 ES Module 时,直接双击 HTML 可能被 Chrome 的本地文件策略拦截。解决方法是改用普通脚本并通过全局对象连接 store.js 和 app.js,保证无需服务器也能运行。
问题二:文件直接打开时,部分浏览器不允许访问 localStorage。代码对此做了容错处理,读取失败时使用示例数据,保存失败时提示但仍保留当前页面数据。
问题三:寻物和招领的状态含义不同。解决方法是使用状态配置表限制可选状态,并在“我的发布”中只显示合法的下一步操作。
八、队友评价
评价组员蔡以恒:能够从测试者角度检查页面流程,指出筛选条件和空结果反馈的重要性,提出的分类和地点筛选提高了查找效率。后续协作中可以在开始编码前更早确定接口字段和测试用例,减少返工。
九、PSP
| 阶段 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|
| 计划与估计 | 25 | 20 |
| 需求分析 | 30 | 20 |
| 设计与设计复审 | 75 | 60 |
| 具体编码 | 120 | 95 |
| 代码复审 | 20 | 15 |
| 测试与测试报告 | 60 | 45 |
| 报告、总结与改进 | 70 | 50 |
| 合计 | 400 | 305 |
完整 PSP 见仓库中的 docs/PSP.md。
浙公网安备 33010602011771号