2026秋软件工程第二次结对作业——校园失物招领程序实现

2026秋软件工程第二次结对作业——校园失物招领程序实现

一、作业回顾与从原型到程序

回顾

上次作业完成了“寻回”校园失物招领的 Figma 原型,这次把首页、发布、搜索、详情和“我的发布”变成能实际使用的网页。范围仍然是发布寻物与招领、按名称查找、查看联系方式,以及交接后的状态更新。没有增加地图、实名认证和站内聊天。

1.容易忽略的问题:

有“已找回”按钮,不代表结束流程已经完整。因此本次明确了操作入口和结果:发布者从“我的发布”进入详情,确认结束;取消不写入数据;确认后详情显示“已找回”或“已归还”,默认首页隐藏该记录,“我的发布”仍然保留。其他使用共享服务的浏览器刷新后也能看到新状态。

2.方法模式

我们本次实现采用 HTML、CSS、原生 JavaScript。助教完整下载、解压后,用 Chrome 打开 index.html 即可使用。考虑到浏览器本地数据不能在同学之间共享,还提供了 Python 标准库和 SQLite 实现的共享服务,两种运行方式使用相同的页面。

环节 具体处理
输入 名称、地点、联系方式去掉首尾空白;检查必填、长度和发生时间
查找 按物品名称包含匹配;可叠加类型、类别、地点筛选
保存 本地版写入 localStorage,共享版由服务端写入 SQLite
权限 浏览器持有随机管理凭证;共享服务核对凭证哈希后才修改
展示 用户填写的内容用 textContent 显示,避免被当成 HTML 执行
异常 保存失败保留输入并显示错误,不跳转到成功页

二、分工与协作记录

职责分配:

戴锴斌负责主仓库、实现材料和交付整理,程序开发与测试使用工具辅助。

周兴宇通过自己的 GitHub 账户 fork 仓库并提交测试计划,在后续提交中补充了两处验收条件

三、PSP 与过程记录

PSP2.1 阶段 预估(分钟) 实际(分钟)
Planning 计划 10 8
Estimate 估计任务时间 10 8
Development 开发 195 220
Analysis 需求分析与技术选择 15 20
Design Spec 设计文档 15 10
Design Review 设计复审 5 5
Coding Standard 代码规范 5 5
Design 具体设计 10 10
Coding 编码 90 80
Code Review 代码复审 15 15
Test 测试、修复与回归 40 30
Reporting 报告 30 30
Test Report 测试报告 15 20
Size Measurement 工作量统计 5 5
Postmortem & Process Improvement Plan 总结与改进 10 5
合计 汇总行不重复累加 235 411

四、关键流程与代码

1. 发布信息

发布流程

2. 查找、联系与结束

信息结束闭环

function close(item, ownerId) {
  if (!ownerId || item.ownerId !== ownerId)
    throw new Error('只有发布者可以修改状态');
  if (item.status !== 'active')
    throw new Error('这条信息已经结束');
  return {...item, status: 'closed'};
}

3. 数据流

数据流

另外:

页面通过统一的 list/create/close 方法访问数据。直接打开 HTML 时选择本地存储,通过项目服务访问时选择 HTTP 接口。接口失败不会悄悄切回本地存储,否则用户可能以为发布成功,实际上其他人看不到。

管理权限绑定浏览器,并不等于实名认证。换浏览器、清除数据或丢失管理凭证后,无法再管理原记录,本版没有找回凭证功能。本地模式只适合单机演示;跨浏览器查看同一批数据需要运行共享服务。

五、两个附加特点

1. 类别与地点组合筛选

只搜索“校园卡”可能得到很多相似结果,因此增加类别和地点筛选。比如选择“校园卡”,再输入“图书馆”,列表只保留同时符合条件的记录。筛选条件之间使用 AND,避免选得越多结果反而越多。下面是 query 中的条件片段:

(!filters.category || p.category === filters.category) &&
(!filters.place || p.place.includes(filters.place.trim())) &&
(!keyword || p.name.toLocaleLowerCase().includes(keyword))

其中 keyword 业已去除首尾空白并转为小写;显式搜索时全空白会提示输入关键词。默认不展示已结束信息,也可以勾选“包含已结束”查看历史结果。

首页与筛选

2. 复制联系方式

小注释:详情页增加复制按钮,减少在页面与微信等应用之间切换时重新输入的操作。不能一点击就提示成功,而要等剪贴板写入完成:

await navigator.clipboard.writeText(p.contact);
toast('联系方式已复制');

已归还详情与复制按钮

六、目录与运行说明

index.html                 页面入口
assets/
  app.js                   路由、表单、列表及详情交互
  core.js                  输入校验、查询、状态规则
  store.js                 本地存储与 HTTP 适配器
  styles.css               响应式样式
  favicon.svg              图标
server.py                  可选共享服务与 SQLite 存储
package.json               前端测试命令
tests/
  core.test.js             业务规则测试
  store.test.js            存储与异常处理测试
  ui-state.test.js         异步请求顺序回归测试
  test_server.py           HTTP 与数据库测试
docs/                      实现、测试、过程记录及截图
测试计划.txt                通过 PR 修改的验收计划
data/                      运行后自动生成,不提交仓库

验收方法与安装环境

最方便的验收方法:在仓库选择 Code → Download ZIP,完整解压,用普通 Chrome 窗口打开 index.html。

共享模式:安装 Python 3.9 或以上,在项目目录执行:

python3 server.py

然后用 Chrome 访问 http://127.0.0.1:8000。Windows 可用 py -3 server.py。首次列表为空,发布后存入 data/campus.sqlite,重启服务仍保留。普通窗口发布、无痕窗口查看,可验证非发布者没有结束权限。端口被占用时用 python3 server.py --port 8765。请使用项目自带服务,普通静态网页服务器没有所需接口。

发布页面

七、单元测试与复现方法

1. 工具及简易教程

前端使用 Node 自带的 node:test 和 node:assert/strict,服务端使用 Python 标准库 unittest。业务规则从界面中分离后,不必每次点页面就能验证输入、筛选和状态变化。测试方法从一个有效输入开始,先断言正常结果,再每次只改变一个条件,检查拒绝分支。

安装 Node.js 18 或以上,在项目目录运行:

npm test
python3 -m unittest discover -s tests -v

前者出现 fail 0,后者末尾为 OK,代表这次运行通过。若要添加测试,在相应测试文件中新增 test('描述预期行为', () => {...}),准备数据,调用函数,再用断言检查结果。失败后先看是哪一项预期不符,修改后重跑全部测试,防止修好一处又破坏另一处。运行网页本身不需要 Node。

2. 测试代码示例

下面摘自对 close 函数的测试:

const p = {...posts[0], ownerId: 'owner'};
assert.throws(() => C.close(p, 'other'));
const out = C.close(p, 'owner');
assert.equal(out.status, 'closed');
assert.equal(p.status, 'active');
assert.throws(() => C.close(out, 'owner'));

这段同时检查拒绝其他人修改、本人可以修改、原对象不被改变和不能重复结束。仅测纯函数还不够,Python 测试会对实际启动的临时 HTTP 服务发请求,检查非发布者直接请求接口也会得到403。

3. 数据设计与结果

按照白盒思路检查条件分支,再使用等价类、边界值和异常输入补充。首版实际运行通过 27项前端测试、13项服务端测试,共40项。原始输出在仓库 docs/evidence,不是两位同学独立试用的记录。

检查方向 测试数据及预期
必填分支 名称、地点、联系方式为全空白,拒绝发布
长度边界 名称40字通过、41字拒绝,另检查 emoji 计数
时间校验 未来时间、2月30日、异常时区和24点规范化输入被拒绝
输入类型 未知类别、非法类型和无效 Unicode 字符被拒绝
搜索分支 前后空白、全空白、大小写、部分匹配及无结果
组合筛选 类别、地点、类型同时约束,默认排除已结束
状态权限 本人、其他人、不存在的记录、已结束记录分别处理
持久化 新建存储对象重新读取,服务重启后数据仍在
存储异常 JSON损坏、结构错误、配额不足,不能误报保存成功
接口异常 网络失败、请求格式错误、超大请求、跨站修改请求
并发顺序 旧列表请求后返回时,不覆盖新数据
公开内容 响应不包含管理凭证,静态服务不暴露数据库

测试环境是 macOS 26.6.2、Chrome 155.0.8059.40、Node.js 25.1.0、Python 3.9.6。Chrome 中还实际检查了发布招领、空白搜索、零结果、取消结束后刷新不变、确认归还后刷新保持,以及另一浏览器能查看共享记录但不能结束它。390px 窄屏补查没有横向溢出。

这些测试覆盖了核心流程和已发现的异常,但没有验证长期运行、大量并发或真实校园使用效果,因此不能据此声称寻物时间缩短了多少。

八、提交记录与实际问题

GitHub提交记录

提交按设计与估算、业务规则、本地存储、共享服务、页面交互、修复和文档逐步进行,截图之后的兼容性修复可在完整提交记录中查看。

问题 定位与处理 结果及收获
较早发出的请求较晚返回,覆盖新列表数据 用两个可控制完成顺序的 Promise 复现;将请求序号检查移到写入状态之前 已修复并加入回归测试,不能只检查当次画面是否正确
Python 接受异常时区偏移,前后端判断不一致 加入 +00:99 输入检查,显式限制偏移范围 接口返回400,两端不能只依赖各自日期库的默认行为
孤立 Unicode 代理字符写入数据库时报错 在前后端增加无效字符检查,保留异常样本 已修复,能通过JSON解析不代表可以正常保存
Node 18 的全局 Web Crypto 可用性与开发环境不同 Node 端显式使用内置 node:crypto.webcrypto,在没有全局 crypto 的隔离上下文验证 已修复;未声称在未安装的版本上实际完整跑过程序

协作上的一个具体不足是第一轮 PR 以测试计划为主,尚未形成队友独立运行测试代码后的结果。因此后续操作包已安排补充“我的发布”筛选和时间边界两个用例,并要求提交实际运行输出,便于下一轮共同验收。

九、个人总结与队友评价

本次实现需要特别关注权限校验与操作可追溯性。日志落库之前,不能仅依赖前端提示成功;删除操作也应该和新增操作一样完整记录。对我来说,下一步需要能独立说清楚软删除和物理删除的区别,以及为什么客户端已经拦截了非法请求,服务端仍然必须再校验一次。李婉婷在测试方案里补充了并发提交和重复点击的场景验证,这种把边界条件提前列清楚的做法值得借鉴。协作还可以优化的地方是尽早同步真实接口联调结果与异常日志,避免只停留在方案讨论、缺少统一验收依据的情况。

posted @ 2026-10-09 22:51  干的湿垃圾  阅读(9)  评论(0)    收藏  举报