软件工程第二次结对作业
2026秋软件工程第二次结对作业:校园寻物站程序实现
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 202601 软件工程 |
| 这个作业要求在哪里 | 2026秋软件工程第二次结对作业之程序实现(在线作业地址待补,内容依据老师原文) |
| 这个作业的目标 | 基于第一次原型,实现发布、浏览与搜索、详情与联系、发布者更新状态的完整流程 |
| 结对成员(学号、姓名) | 102401421丁政伟;102401435 乔震宇 |
| 结对同学的博客链接 | https://www.cnblogs.com/102401435q/p/23239144 |
| GitHub项目地址 | 102401421-102401435 |
一、分工与PSP
| 成员 | 模块与任务 | 验收重点 |
|---|---|---|
| 丁政伟 | 发布表单、输入校验、本机保存与草稿;阅读 domain.js 和 storage.js 对应逻辑 | 空白必填、日期、保存失败、刷新恢复 |
| 乔震宇 | 首页、搜索与组合筛选、详情、我的发布及状态确认;阅读 app.js 对应逻辑 | 搜索结果、联系方式、本人权限、完成状态一致性 |
| 共同 | 核对第一次原型、审查接口、复测、整理 README 与博客、进行真实 fork/PR 协作 | 两种发布类型均走通,说明与程序一致 |
| PSP2.1 | 阶段 | 建议预估 | 用时 | 用时 |
|---|---|---|---|---|
| Planning | 计划(小计) | 15 | 17.8 | 16.4 |
| Estimate | 估计任务时间 | 15 | 17.8 | 16.4 |
| Development | 开发(小计) | 395 | 468.7 | 463.0 |
| Analysis | 需求分析与技术学习 | 35 | 41.6 | 38.7 |
| Design Spec | 设计文档 | 30 | 33.2 | 28.9 |
| Design Review | 设计复审 | 15 | 18.4 | 19.6 |
| Coding Standard | 代码规范 | 10 | 12.3 | 9.7 |
| Design | 具体设计 | 40 | 46.7 | 51.3 |
| Coding | 编码 | 170 | 198.6 | 187.4 |
| Code Review | 代码复审 | 25 | 29.5 | 32.8 |
| Test | 测试与修改 | 70 | 88.4 | 94.6 |
| Reporting | 报告(小计) | 60 | 65.7 | 67.6 |
| Test Report | 测试报告 | 25 | 28.7 | 31.2 |
| Size Measurement | 工作量统计 | 10 | 9.4 | 10.6 |
| Postmortem & Process Improvement Plan | 总结与改进 | 25 | 27.6 | 25.8 |
| 合计 | 明细之和 | 470 | 552.2 | 547.0 |
二、实现思路
校园失物信息容易被群聊消息覆盖,因此程序把寻物和招领放在统一列表中。首页卡片显示名称、地点、日期和状态,用户可以先筛选或搜索,再进入详情查看特征与联系方式。
实现采用原生 HTML、CSS、JavaScript,业务规则、保存逻辑和页面交互分开组织:domain.js 负责校验、搜索、筛选和状态规则;storage.js 负责保存信息与草稿;app.js 负责呈现页面、处理点击及表单提交。页面通过哈希地址切换,浏览器返回键可返回上一视图。

发布流程是“填写表单 → 校验 → 保存 → 发布成功”。必填项缺失、日期无效或晚于今天、文本超长时,表单提示原因;只有保存成功才进入成功页。


查看流程是“浏览或搜索 → 详情 → 联系发布者”。物品交还后,发布者从“我的发布”进入本人详情,确认并更新状态。寻物完成显示“已找到”,招领完成显示“已归还”;首页的状态筛选可以查看已完成记录。

状态更新的核心代码如下:
function finishItem(items, id, ownerId, now = new Date().toISOString()) {
const item = items.find(entry => entry.id === id);
if (!item) throw new Error('这条信息不存在');
if (item.ownerId !== ownerId || item.isDemo)
throw new Error('只能修改自己发布的信息');
if (item.status === 'done') return items;
return items.map(entry => entry.id === id
? { ...entry, status: 'done', updatedAt: now } : entry);
}
函数先检查信息是否存在和发布者是否匹配,再更新目标记录。重复完成操作返回原数据,避免覆盖完成时间;其他物品保持原样。


三、附加特点
1. 类别与地点组合筛选
用户可能只记得物品类别或遗失区域。按类别、地点和状态筛选,可以缩小查找范围;筛选条件与关键词同时生效,并提供一键重置。
if (filters.category && filters.category !== 'all'
&& item.category !== filters.category) return false;
if (filters.place && filters.place !== 'all'
&& item.place !== filters.place) return false;
这是 selectItems 中的部分逻辑。条件取交集,剩余代码处理类型、状态和关键词,结果按发布时间排序。关键词支持匹配名称、地点或特征,并统一外围空格、英文大小写与全角字符。

2. 复制联系方式
详情页提供复制按钮,减少手工抄写错误。浏览器不允许自动复制时,选中联系文字并提示手动复制。
try {
await navigator.clipboard.writeText(value);
toast('联系方式已复制');
} catch {
const node = document.getElementById('contact-value');
const range = document.createRange();
range.selectNodeContents(node);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
toast('已选中联系方式,请按 Ctrl+C 或长按复制');
}
3. 草稿恢复与手机布局
表单修改后自动保存草稿,刷新后恢复,发布成功后清除。桌面采用多列卡片,手机改为紧凑列表;本次检查的手机首页和发布表单没有横向溢出。

四、目录及运行说明
仓库根目录/
index.html 网页入口
README.md 使用说明
package.json 统一测试入口
.gitignore Git忽略规则
.github/
workflows/test.yml GitHub自动测试配置
assets/
css/app.css 页面样式与手机布局
js/domain.js 输入校验、搜索、筛选和状态规则
js/storage.js 信息保存与草稿
js/app.js 页面交互
images/logo.svg 本地图标
tests/
domain.test.cjs 业务规则测试
storage.test.cjs 保存逻辑测试
下载完整程序目录,用 Google Chrome 打开 index.html,保留旁边的 assets。网页运行无需安装依赖,也不需要联网加载字体或前端框架。
首页选择“发布信息”,填写物品和联系方式后提交;通过搜索或卡片进入详情;交还完成后,在“我的发布”中更新状态。首页默认显示未完成信息,选择“全部状态”或“已完成”可查看历史记录。
五、单元测试及简易教程
测试使用Node自带的node:test和node:assert/strict,无需下载测试框架。测试文件采用CommonJS格式,业务规则可通过require导入,这样不用打开网页也能检查输入校验、搜索和状态转换。
学习单元测试时,我们先理解“准备输入—调用函数—断言结果”的基本结构,明确测试需要比较程序的实际结果与预期结果。随后结合项目中的输入校验和状态更新函数,了解如何使用断言检查返回值和异常。测试数据从正常输入扩展到空白字段、无效日期、长度边界和修改他人信息等情况,逐步理解白盒测试中分支覆盖与边界检查的作用。
一个最小测试包含三步:准备输入,调用函数,断言结果。例如合法数据应返回成功,未来日期应返回日期错误,修改他人信息应抛出异常。assert.equal比较值,assert.deepEqual比较数组或对象,assert.throws检查预期异常。
安装Node.js 24或兼容版本,在仓库根目录,即index.html所在目录打开终端:
node --test tests/domain.test.cjs tests/storage.test.cjs
运行后查看每个用例是否通过以及末尾的汇总。如果出现失败,先阅读断言给出的实际值与预期值,检查相应规则,再修复程序或错误的测试输入;不要仅为让测试通过而删除失败用例。修复后重新运行整套测试,确认没有影响其他功能。
下面的测试验证“更新他人信息”被拒绝:
const test = require('node:test');
const assert = require('node:assert/strict');
const D = require('../assets/js/domain.js');
test('不能修改他人信息', () => {
const entries = [{id:'x',ownerId:'a',isDemo:false,status:'active'}];
assert.throws(() => D.finishItem(entries,'x','b'), /自己/);
});
本段是简化教程示例;项目实际权限用例同时覆盖他人、示例及不存在的信息。
测试数据采用白盒分支与边界设计:空白必填、错误枚举、闰年与未来日期、文本恰好上限和多一个字、大小写与全角搜索、无结果及组合筛选;还考虑重复更新、HTML字符、保存空间不足、损坏JSON和草稿异常。存储测试使用内存替身,不修改网页里的个人记录。
2026-10-05,在Node.js 24.15.0下运行28个测试,全部通过。业务规则17个、存储11个,满足附录至少10个的要求。
六、异常与解决
对程序本身的异常,设计了“保存成功才进入成功页”和“损坏数据不自动覆盖”。测试用模拟写入失败确认旧记录保留;模拟损坏JSON确认返回只读提示。这提醒我们,按钮跳转正确和数据真正保存是两件需要分别检查的事。
手机检查时发现发布导航的读屏名称会重复“+”,现补充明确的无障碍名称“发布信息”。同时把完成提示改为本机列表与详情同步,避免让演示界面暗示已经实现跨设备共享。
代码签入记录截图:

七、队友评价
值得我学习的是从用户操作的角度考虑问题,关注搜索无结果时的提示、页面之间的跳转,以及物品交还后的状态变化。这让我认识到,界面设计不仅要清楚,也要让用户能够顺利完成操作。后续可以改进的是更早整理需要调整的内容,按优先顺序处理,减少反复修改。

浙公网安备 33010602011771号