软件工程第二次结对作业
第二次结对作业:校园失物信息共享平台
一、作业信息
| 这个作业属于哪个课程 | 202601 软件工程 |
|---|---|
| 这个作业要求在哪里 | [2026秋软件工程结对作业(第二次之程序实现)](https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16744 |
| 这个作业的目标 | 1. 完成校园失物信息共享平台的前端程序实现 2. 实现失物和招领信息的发布、查询、筛选与详情查看 3. 完善数据校验、状态管理、图片处理和主题切换等功能 4. 使用测试和 GitHub 协作记录验证程序质量 |
| 学号 | 102401608 |
| 结对成员 | 成员一:102401608 魏之皓;成员二:102401609 王智钦 |
| 个人 GitHub 仓库 | wizneko/102401609-102401608 |
| 队友 GitHub 仓库 | King-Zhi/102401609-102401608 |
| 项目名称 | 福州大学失物信息共享平台 |
| 项目形式 | PC 端 Web 页面,纯前端、免部署运行 |
| 结对同学的博客链接 | 魏之皓:https://www.cnblogs.com/wizneko/p/23255881 王智钦:https://www.cnblogs.com/zhiking/p/23257216 |
| 本作业博客链接 | https://www.cnblogs.com/wizneko/p/23255881 |
二、结对分工
魏之皓(102401608)
- 参与最新首页方案的确定和页面结构调整,将入口页面统一为
index.html。 - 负责日间/夜间主题切换、主题记忆和首页视觉层级调整。
- 协助补充首页顶部最新线索区域,并检查列表、卡片和窄屏布局。
- 参与近 90 天日期校验、微信号格式校验和页面说明同步。
- 负责本地浏览器验证及 README、项目说明文档更新。
王智钦(102401609)
- 负责核心数据管理、发布/编辑/删除/结贴等功能的实现与维护。
- 负责搜索、关键词高亮、相关度排序、状态分层和图片上传校验等功能。
- 增加福州大学七大校区筛选、发布校区绑定和校区与地点联合筛选。
- 编写和维护核心单元测试、页面逻辑测试及测试运行器。
- 负责主仓库的功能合并和整体版本维护。
三、PSP 表格
PSP各阶段耗时如下,单位为分钟。
| PSP2.1 | Personal Software Process Stages | 预计耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 25 |
| Estimate | 估计这个任务需要多少时间 | 20 | 25 |
| Development | 开发 | 40 | 45 |
| Analysis | 需求分析(包括学习新技术) | 40 | 50 |
| Design Spec | 生成设计文档 | 30 | 40 |
| Design Review | 设计复审 | 20 | 25 |
| Coding Standard | 代码规范(为目前的开发制定合适的规范) | 10 | 15 |
| Design | 具体设计 | 50 | 60 |
| Coding | 具体编码 | 180 | 235 |
| Code Review | 代码复审 | 30 | 35 |
| Test | 测试(自我测试,修改代码,提交修改) | 60 | 75 |
| Reporting | 报告 | 40 | 50 |
| Test Report | 测试报告 | 20 | 30 |
| Size Measurement | 计算工作量 | 10 | 10 |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 20 | 30 |
| 合计 | 580 | 750 |
四、项目简介与需求分析
校园失物信息通常散落在班级群、年级群和社交平台中,信息容易被新消息覆盖,也不便于按物品、地点和状态查找。本项目面向福州大学校园场景,提供一个即开即用的失物信息共享页面:用户可以发布寻物或招领信息,按关键词和多个条件检索物品,查看详情并复制联系方式,最后把已经找回或归还的记录标记为已解决。
本项目采用本地浏览器 localStorage 保存数据,不包含服务器、账号系统和真实的多用户权限控制。isMine 只用于本地示例中区分“我的发布”,不能等同于后端鉴权。
五、设计与实现思路
5.1 页面结构
页面分为四个主要区域:
- 顶部导航:平台名称、日间/夜间切换、浏览信息、我的发布和发布信息。
- 最新线索:自动展示当前进行中的三条信息,帮助用户快速看到最新内容。
- 搜索与筛选:关键词搜索、寻物/招领切换、物品分类、校区、地点大区和状态筛选。
- 全部物品信息:按照状态和搜索相关度排列的卡片列表,支持详情查看和状态操作。
5.2 数据流
用户操作
|
v
index.html 事件入口
|
v
js/app.js 组织页面状态、弹窗和重新渲染
|
+--> js/utils.js:校验、搜索、排序、关键词高亮
|
+--> js/data.js:读取/保存 localStorage,增删改和状态流转
|
v
DOM 卡片、弹窗、Toast 提示和主题样式
发布信息时,表单先生成待校验对象,再依次经过字段校验、日期范围校验和图片读取状态检查;校验成功后由数据管理层保存,并调用页面刷新。搜索时先执行多条件过滤,再在相同状态内按相关度排序,最后由页面重新生成卡片。
5.3 关键代码片段:校区与多条件筛选
if (campus && campus !== 'all') {
const itemCampus = item.campus || this.detectCampus(item);
if (itemCampus !== campus && !String(item.location || '').includes(campus)) {
return false;
}
}
if (location !== 'all' && !item.location.includes(location)) {
return false;
}
if (kw) {
const fields = [item.title, item.desc, item.location, item.category, item.campus || ''];
if (!fields.some(field => this.matchKeyword(field, kw))) return false;
}
这里将校区、地点和关键词作为独立条件处理,只有同时满足条件的记录才会保留。detectCampus 还可以从旧数据的地点文字中补齐校区字段,兼容升级前的本地缓存。
5.4 关键代码片段:状态分层与相关度排序
return filtered.slice().sort((a, b) => {
const solvedA = a.status === 'solved' ? 1 : 0;
const solvedB = b.status === 'solved' ? 1 : 0;
if (solvedA !== solvedB) return solvedA - solvedB;
if (calculateScore) {
const scoreDiff = calculateScore(b) - calculateScore(a);
if (scoreDiff !== 0) return scoreDiff;
}
return 0;
});
排序先把进行中的记录放在前面,再在同一状态内使用标题、地点、校区、分类和描述的权重计算相关度。这样用户优先看到仍需要处理的信息,同时搜索结果也更符合使用习惯。
5.5 关键代码片段:发布信息的数据保存
const validation = Utils.validateItem(itemPayload);
if (!validation.isValid || date < minAllowedDate) {
// 保留输入内容,显示错误原因,允许用户修正后重试
return;
}
const updatedItem = DataManager.addItem({
...itemPayload,
img: this.uploadedImageBase64 || '',
publisherName: '校内同学(我)'
});
发布失败时不关闭表单,保存成功后才刷新列表。图片为选填项,未上传图片时保持空值,并由界面显示统一的无图占位,避免把其他物品的示例图片错误套到新记录上。
图片格式和大小校验属于发布流程的边界保护,测试结果统一放在第八章展示。
六、项目特色设计
- 七大校区全局筛选:支持旗山、铜盘、怡山、泉港、晋江、厦门集美和厦门鼓浪屿校区,并可以与地点大区组合筛选。
- 状态分层展示:进行中的信息自动置前,已解决信息自动后移,减少用户在历史记录中寻找有效信息的成本。
- 搜索结果有依据:标题、地点、校区、分类和描述使用不同权重;当关键词只命中描述时,卡片展示上下文摘要。
- 日间/夜间主题:切换主题后首页、筛选区域和弹窗一起更新,并使用浏览器本地存储记住选择。
- 发布流程有边界保护:对标题、地点、日期、联系方式、图片格式和图片大小进行校验;上传图片的异步读取不会覆盖后一次选择。
- 本地可运行:无需安装数据库和后端服务,下载完整仓库后使用现代浏览器打开
index.html即可体验。
七、目录结构与运行说明
campus-lost-found/
├── index.html # 网页唯一入口
├── README.md # 项目简介、目录、运行和测试说明
├── css/
│ ├── style.css # 公共样式、弹窗和基础状态
│ └── home.css # 首页布局、日间/夜间主题
├── js/
│ ├── app.js # 页面控制器和交互事件
│ ├── home.js # 首页最新线索、主题切换
│ ├── data.js # localStorage 数据层和示例数据
│ └── utils.js # 校验、过滤、排序和安全转义
├── assets/
│ ├── favicon.svg # 网站图标
│ ├── images/ # 预设物品图片
│ ├── web_preview.png # 夜间首页截图
│ └── web_preview_day.png # 日间首页截图
├── docs/
│ ├── 开发记录.md # 问题、修改和验证过程
│ ├── PSP记录表.md # PSP 估计与实际耗时
│ └── images/ # 开发验证截图
└── test/
├── test_runner.html # 浏览器单元测试运行器
├── unit_tests.js # 54 个核心测试用例
├── app_tests.js # 15 个页面逻辑测试
├── test_helpers.js # 测试隔离辅助代码
└── run_tests.js # Node.js 命令行入口
运行方法:
- 下载或 Clone GitHub 仓库,保持整个目录结构不变。
- 使用 Chrome、Edge 等现代浏览器打开根目录下的
index.html。 - 点击顶部主题按钮可以切换日间/夜间模式;点击校区胶囊可以切换浏览校区。
- 通过搜索框、类型标签和三个筛选下拉框查找物品;点击卡片查看详情。
- 点击“发布信息”填写表单,可以选择校区和上传不超过 2MB 的图片。
- 打开“我的发布”可以编辑、删除或标记自己发布的记录。
八、单元测试
8.1 测试工具与学习过程
本项目使用 JavaScript 自定义断言实现轻量级测试,没有额外依赖。测试按照“准备输入数据—调用函数—判断结果”的方式编写,并按表单校验、搜索筛选、数据持久化、图片上传和校区功能分类。浏览器测试运行器显示每个用例的通过状态和耗时,Node.js 入口用于重复运行整套测试。
8.2 测试数据设计
- 正常数据:包含寻物、招领、不同分类、不同校区和已解决记录。
- 边界数据:空标题、超长标题、未来日期、闰年日期、非法日期、空图片和超过 2MB 的图片。
- 组合数据:校区 + 地点、类型 + 分类 + 状态、标题命中 + 描述命中。
- 异常数据:损坏 JSON、非法校区、非法联系方式、存储失败和连续选择图片。
- 安全数据:包含
<script>、特殊字符和正则字符的搜索词,确认输出经过 HTML 转义。
8.3 测试结果
在项目根目录运行:
node test/run_tests.js
最终结果:
54/54 PASSED, 0 FAILED
15/15 application checks passed
重点测试用例包括:
| 测试方向 | 验证内容 |
|---|---|
| 表单校验 | 标题、地点、日期、手机号、QQ、微信号和非法校区拦截 |
| 搜索功能 | 大小写、空格、简称、子序列匹配、描述摘要和高亮 |
| 筛选排序 | 类型、分类、校区、地点、状态联合筛选;进行中优先 |
| 数据管理 | 发布、编辑、删除、结贴、空列表和存储失败保护 |
| 图片处理 | 类型、扩展名、大小、损坏图片、异步回调和重复提交 |
| 安全性 | 特殊字符和恶意脚本输入不会直接生成可执行 HTML |
九、截图与展示材料
主要功能展示截图如下:
夜间首页

首页总览展示平台名称、最新线索、搜索筛选和物品列表。日间主题与夜间主题使用相同的页面结构,仅调整颜色和对比度。
正常文字搜索

输入正常关键词“耳机”后,页面筛选出对应的白色 AirPods 记录,并在结果标题中高亮关键词。
物品详情

点击物品卡片后打开详情弹窗,展示物品图片、拾获地点、日期、状态、特征描述和联系方式,并提供一键复制功能。
发布信息

发布信息表单包含信息类型、物品名称、分类、日期、地点、详细描述、实物照片和联系方式等字段,提交前会进行输入校验。
| 展示内容 | 对应功能 |
|---|---|
| 夜间主题首页 | 首页布局、最新线索、主题切换和信息卡片 |
| 正常文字搜索结果 | 关键词搜索、结果筛选和关键词高亮 |
| 物品详情弹窗 | 物品信息查看、联系方式复制和状态操作 |
| 发布信息表单 | 信息录入、图片上传和表单校验 |
| GitHub 提交与 Pull Request 记录 | GitHub 分支协作、功能提交和合并过程 |
十、GitHub 提交记录
组长仓库 main 分支的相关提交如下:
9433a1f fix(home): ensure latest clues section dynamically displays without page reload and bust script cache
490a43f fix(home): retain latest clues after publishing and support type switching
9f266c5 fix(home): allow latest clues to display per-campus without disappearing
c3517c5 docs: correct member name to 王智钦 across README and development log
84d88a5 docs: clean up defensive notes and update development log for blog synthesis
23f152d docs: update README, development log and add .gitignore for multi-campus support and test suite expansion
b6dc50d feat: 将校区选择移至顶部地标定位栏作为全局切换胶囊,简化搜索栏
4805f9e feat: 支持福州大学全7大校区精细化筛选与发布绑定 (测试用例扩充至54项全绿)
61aaddb style: remove redundant footer action buttons and center brand note
其中,9433a1f 是组长最后一次首页修复。之前页面在发布信息、切换寻物/招领类型或切换校区后,顶部“最新线索”区域可能不刷新或直接消失。修复后,首页根据当前筛选条件重新计算进行中的最新记录;发布成功时清除会造成误过滤的搜索条件,并在必要时自动调整校区、分类和状态筛选。页面资源同时增加版本参数,避免浏览器继续使用旧缓存脚本。
个人参与的页面相关提交包括:
7404683 更新首页布局并加入日间夜间切换
45741c8 同步组长仓库的日期校验和页面更新
eadae48 更新日夜首页截图和使用说明
Pull Request 协作记录

图 5 GitHub Pull Request 页面:仓库中保留了首页布局、图片处理、搜索日期校验、发布管理和 README 更新等协作记录。
GitHub 提交记录截图
以下截图展示了仓库中按日期排列的实际提交记录,内容包括首页功能修复、成员信息更正、文档整理、校区筛选、搜索排序、图片处理和测试完善等工作。

图 6 2026 年 10 月 9 日的提交记录:完成首页最新线索显示、类型切换、成员信息和文档内容的修正。

图 7 2026 年 10 月 8 日的提交记录:包含主题切换、日期校验、搜索排序、文案调整、单元测试和多个 Pull Request 合并记录。

图 8 2026 年 10 月 7 日的提交记录:包含数据修复、图片处理、搜索高亮、测试隔离和项目文档整理等提交。
十一、遇到的问题、尝试与解决
1. 首页版本合并后功能和视觉风格不一致
问题:第一次页面方案与主仓库的功能分支同时修改入口文件和样式,直接合并容易覆盖校区、搜索和发布功能。
尝试:先比较主分支和个人分支的文件差异,再保留主仓库的数据层和交互函数,将主题与首页布局放到 home.css、home.js,最后在 index.html 统一入口。
结果:保留了主仓库功能,并完成日间/夜间主题和首页布局调整。收获是合并前先划分文件职责,比直接覆盖文件更容易定位冲突。
2. 用户发布的物品图片与文字不对应
问题:未上传实物图片时,旧逻辑会套用分类示例图,导致“手机”等新记录显示成其他物品。
解决:发布时未上传图片保持空字符串,卡片和详情使用“暂无实物图片”占位;预设数据继续使用对应的本地示例图片。
结果:新增图片相关测试,并验证图片为空时不会误用其他物品图片。
3. 已解决信息和进行中信息混在一起
问题:按照原始数组显示时,已解决记录会穿插在仍需处理的记录中。
解决:在过滤结果上增加状态分层,open 优先,solved 后移;有搜索词时再按标题、地点、分类和描述的权重排序。
结果:列表更符合失物招领的实际使用优先级。
4. 搜索结果命中描述但用户看不出原因
问题:关键词只出现在描述中时,卡片没有展示描述内容,用户难以理解为什么出现该结果。
解决:增加描述上下文摘要和关键词高亮,并对标题、地点、校区、分类和描述设置不同权重。
结果:搜索结果能说明命中原因,同时使用 HTML 转义防止恶意输入生成标签。
5. 首页最新线索在筛选和发布后显示异常
问题:发布新信息、切换寻物/招领类型或选择具体校区后,顶部最新线索区域可能仍使用旧状态,甚至因为筛选条件不兼容而不显示。
解决:在 js/home.js 中根据当前关键词、类型和校区重新计算最新的进行中记录,并分别显示“最新寻物”“最新招领”或对应校区标题;在 js/app.js 中发布成功后清理遗留关键词,遇到校区、分类和状态冲突时自动调整筛选条件,再重新渲染页面。同时在 index.html 的 CSS 和 JavaScript 引用后加入版本参数,降低浏览器缓存旧脚本造成的影响。
验证:通过发布信息、切换类型、切换校区和刷新页面进行浏览器验证,确认新记录能够进入最新线索区域,已有线索不会因为视图切换消失;核心测试保持 54/54 通过,页面逻辑测试保持 15/15 通过。
十二、队友评价
王智钦同学做事认真,遇到问题会主动排查,也能及时沟通修改方案。合作过程中,他对细节和测试的重视让我学到了不少,整体配合也很顺利。
十三、个人总结
这次作业让我更清楚地认识到,一个页面能打开并不代表程序已经完成。需求分析、模块划分、GitHub 分支协作、边界测试和文档整理都会影响最终质量。实际开发中,日夜主题、校区筛选和图片处理都经历了多次调整,很多问题只有在真实浏览器操作后才会暴露。通过这次作业,我对前端页面状态管理、localStorage 数据持久化、纯函数测试和 GitHub 协作流程有了更具体的认识,也体会到先写清楚数据结构和交互规则,再开始修改代码,能够减少返工。

浙公网安备 33010602011771号