软件工程第二次结对作业
从原型到程序:校园拾光——第二次结对编程作业
| 项目 | 内容 |
|---|---|
| 所属课程 | 2026 软件工程与软件工程实践 |
| 作业要求 | 第二次结对作业:程序实现 |
| 本人 | 052402234 郭泽强 · 个人博客 |
| 结对同学 | 032401339 沈冠宇 · 队友博客 |
| GitHub 主仓库 | monkery-king-nice/052402234-032401339 |
| 队友 Fork | 2074599796-cpu/052402234-032401339 |
| 协作 PR | PR #1:日期筛选、本地评论与界面完善,已合并 |
| 第一次作业原型 | Figma:校园失物招领原型 |
一、项目介绍与分工
上一次作业完成了校园失物招领的原型设计,这次作业则需要让页面上的操作真正生效。我们将项目命名为“校园拾光”,围绕“发布信息—浏览或搜索—查看详情—联系发布者—更新状态”完成核心流程,并增加照片上传、日期筛选、本地评论等功能。
项目使用 HTML、CSS 和 JavaScript,下载后可直接用 Google Chrome 打开 index.html。启事、照片和评论保存在当前浏览器的 localStorage 中,无需服务器。联系方式由发布者填写,联系与交接在线下或其他通信工具中完成,页面不提供即时聊天。
1.1 分工
以下按本次主仓库与 Fork 的开发范围整理。
| 成员 | 负责范围 | 主要产出 |
|---|---|---|
| 郭泽强 | 基础功能与主仓库整合;发布、搜索、详情、状态维护;照片上传和删除功能;提交版本检查与博客整理 | 基础页面与业务模型、原有单元测试、图片资源、主仓库开发记录 |
| 沈冠宇 | 在 Fork 中增加日期范围筛选、本地评论和界面优化;补充测试与展示材料;向主仓库提交 PR | 日期及评论规则、新增测试、Chrome 验收脚本、截图、PR #1 |
协作采用“主仓库维护基础版本—队友 Fork 后在功能分支开发—提交 PR—合并回主仓库”的方式。队友的开发分支为 feat/date-filter-comments-ui,PR #1 已合并,合并提交为 ff5ffd1。
开发过程中使用了 Codex 辅助编码、测试和文档整理。本文中的功能结论以代码、提交记录与实际运行结果为依据;个人工作量不能直接用 AI 的运行时间或提交之间的时间间隔代替。
二、PSP 与时间复盘
| PSP2.1 | 工作内容 | 参考估算 | 本次实际耗时 |
|---|---|---|---|
| Planning / Estimate | 明确范围、拆分任务与安排顺序 | 30 | 40 |
| Analysis | 分析核心流程、学习本地存储及测试方法 | 90 | 100 |
| Design Spec | 整理数据结构、页面与异常流程 | 60 | 65 |
| Design Review | 检查原型与课程范围是否一致 | 30 | 40 |
| Coding Standard | 约定命名、目录、校验与提交方式 | 20 | 30 |
| Design | 设计模型函数、筛选条件与交互细节 | 90 | 120 |
| Coding | 实现基础功能、图片、日期、评论及样式 | 360 | 450 |
| Code Review | 检查数据兼容、保存顺序和权限判断 | 60 | 80 |
| Test | 单元测试、Chrome 页面测试与问题修正 | 180 | 150 |
| Reporting / Test Report | 整理测试结果、说明与截图 | 60 | 60 |
| Size Measurement | 统计模块、测试和有效提交 | 20 | 30 |
| Postmortem & Process Improvement Plan | 总结问题、整理博客和改进计划 | 80 | 60 |
三、解题思路与设计实现
3.1 先完成一个能走通的流程
| 用户操作 | 页面入口 | 程序处理 | 用户得到的结果 |
|---|---|---|---|
| 发布寻物或招领 | 首页“我丢了东西”“我捡到了”或发布按钮 | 校验必填项、处理图片、写入本地存储 | 在“我的发布”及广场看到新启事 |
| 搜索或筛选 | 广场搜索框和筛选区域 | 组合关键词、类型、类别、状态、日期条件 | 展示匹配结果;无结果时可清除筛选 |
| 查看与联系 | 物品卡片的详情入口 | 展示描述、地点、日期、图片和联系方式 | 复制联系方式,进一步核实物品 |
| 结束信息 | “我的发布”中打开详情 | 核对当前浏览器的发布者标识,保存新状态 | 寻物显示“已找到”,招领显示“已归还” |
| 补充线索 | 详情下方“评论与线索” | 校验昵称和正文,按启事 ID 保存 | 当前浏览器刷新后仍可查看留言 |
状态由发布者维护,更新入口在“我的发布”的详情中。完成后,广场卡片和详情都会显示新状态;误操作时可以恢复为进行中,减少错误结束信息带来的影响。
3.2 模块与数据结构
程序将业务规则和页面交互分开:model.js 管理校验、筛选、状态和存储;app.js 绑定事件并刷新界面;images.js 处理照片;seed.js 提供演示启事。这样测试核心规则时不必启动整个页面。
一条启事包含 id、ownerId、type、title、category、location、date、description、contact、publisher、images、status、createdAt。其中 date 是丢失或拾取日期,createdAt 是发布时间,两者分别用于日期筛选和列表排序。
| 数据 | 存储键 | 作用 |
|---|---|---|
| 启事列表及照片 | campus-light-items-v1 |
保存物品信息和状态 |
| 当前发布者标识 | campus-light-owner-v1 |
限制页面上维护他人启事的操作 |
| 单条启事评论 | campus-light-comments-v1: + 编码后的启事 ID |
避免不同物品的留言混在一起 |
3.3 核心流程图

图中的“联系与交接”属于页面之外的用户行为。程序提供联系方式及状态维护入口,不会把复制联系方式视为物品已经找回。
3.4 关键代码一:先校验,再创建记录
下面节选自 js/model.js 的 createItem:
const errors = validateDraft(draft);
if (Object.keys(errors).length) {
const error = new Error('信息不完整,请检查表单');
error.fields = errors;
throw error;
}
if (!clean(ownerId)) throw new Error('缺少发布者标识');
validateDraft 会分别检查类型、名称、类别、地点、真实日历日期、描述、联系方式、称呼和照片。例如物品名称为 2~40 个字符,详细描述为 10~500 个字符。错误返回到具体字段,页面能够提示用户修改哪一项;通过校验后才创建默认状态为 active 的记录。
保存时也遵循“先保存、后更新界面”的顺序,下面是 app.js 中实际使用的函数:
function commitItems(next) {
M.saveItems(storage, next);
items = next;
}
如果本地存储已满,saveItems 会抛出明确错误,内存中的启事列表不会提前切换成未保存的结果,避免页面提示成功而刷新后数据消失。
3.5 关键代码二:由发布者维护状态
function updateStatus(items, id, ownerId, status) {
if (!['active', 'resolved'].includes(status)) {
throw new Error('无效状态');
}
const item = items.find(entry => entry.id === id);
if (!item) throw new Error('信息不存在');
if (!ownerId || item.ownerId !== ownerId) {
throw new Error('只有发布者可以更新状态');
}
return items.map(entry =>
entry.id === id ? { ...entry, status } : entry
);
}
这里分别检查状态是否合法、记录是否存在、操作者是否匹配。函数返回新数组,并保留照片等其他字段。对于同一个 resolved 状态,寻物信息展示“已找到”,招领信息展示“已归还”,使结果符合业务含义。
四、附加特点及展示
4.1 按丢失或拾取日期组合筛选
只记得物品大概在哪几天丢失时,可以设置日期范围,再结合类别和关键词缩小查找范围。开始和结束当天都包含在结果内;仅填写一端时,另一端不限;起始日期晚于结束日期时显示错误,保留原输入。
下面节选自 filterItems:
if (validateDateRange(filters)) return [];
const start = clean(filters.startDate), end = clean(filters.endDate);
// 以下判断位于 items.filter 的回调中。
if (start && (!validDate(item.date) || item.date < start)) return false;
if (end && (!validDate(item.date) || item.date > end)) return false;
在确认日期有效后,固定格式的 YYYY-MM-DD 字符串可按日历顺序比较,避免将日期转成本地时区时间点后产生一天偏差。类型、类别、状态和关键词判断继续在同一筛选函数中执行。

4.2 每条启事独立的本地评论
详情下方可以填写昵称和线索,昵称为 2~24 个字符,正文为 1~500 个字符。提交前清除首尾空白;保存中禁用提交按钮;成功后清空正文;失败时保留输入和原评论,方便重试。
function commentStorageKey(itemId) {
if (!clean(itemId)) throw new Error('缺少启事标识');
return COMMENTS_KEY + encodeURIComponent(itemId);
}
每条启事使用不同存储键,因此校园卡的线索不会出现在耳机的详情下。展示正文时采用以下代码:
const text = document.createElement('p');
text.textContent = comment.body;
使用 textContent 后,即便正文包含 <script> 或 <img> 字样,也会作为文字显示,而不会作为 HTML 执行。评论按发表时间从早到晚显示;已完成的启事仍能查看和补充留言。


本功能是纯前端本地留言演示,评论仅保存在当前浏览器,不同用户的设备和不同浏览器之间不会同步。它能演示补充线索的交互,但不能称为已上线的公共聊天室。
4.3 照片上传、压缩与预览
每条启事可上传最多 3 张 JPG、PNG 或 WebP 图片,单张原图不超过 5 MB。图片先在浏览器解码,最长边缩至不超过 1280 像素,再通过 JPEG 编码质量和必要的继续缩放,将每张保存图片控制在 200 KB 以内;压缩失败会提示重新选择。
images.js 中缩放比例的关键计算如下:
const scale = Math.min(
1, 1280 / Math.max(image.naturalWidth, image.naturalHeight)
);
let width = Math.max(1, Math.round(image.naturalWidth * scale));
let height = Math.max(1, Math.round(image.naturalHeight * scale));
等比例缩放避免物品变形,上传前可预览和移除照片。详情优先展示发布者实际上传的照片;六条演示启事使用与名称对应的写实示例图片,并标注为示例图。示例素材用于展示界面,不代表真实失物。
4.4 其他便捷设计
| 设计 | 使用意义 | 实现方式 |
|---|---|---|
| 一键复制联系方式 | 减少手动抄录 | 优先剪贴板 API,失败时尝试兼容复制,并反馈结果 |
| 状态恢复 | 修正误标记 | 发布者可将已完成信息恢复为进行中 |
| 删除前二次确认 | 减少误删 | 确认后保存新列表,再清理对应评论 |
| 空结果与清除筛选 | 帮助用户继续搜索 | 显示明确空状态,统一清空筛选条件 |
| 手机布局与键盘焦点 | 方便不同屏幕和操作方式 | 响应式布局、底部导航、可见焦点;桌面 / 聚焦搜索 |



五、目录组织与使用说明
052402234-032401339/
├─ index.html 页面入口,Chrome 直接打开
├─ css/
│ └─ style.css 配色、布局和手机适配
├─ js/
│ ├─ model.js 业务规则与本地存储
│ ├─ app.js 页面渲染、事件和反馈
│ ├─ images.js 上传照片的校验与压缩
│ └─ seed.js 六条演示启事
├─ assets/items/ 本地示例图片及素材说明
├─ tests/
│ ├─ model.test.js 基础功能及照片相关测试
│ ├─ date-comments.test.js 日期范围与评论输入测试
│ ├─ comments.test.js 评论存储、隔离与异常测试
│ └─ browser.cjs Chrome 页面自动验收
├─ docs/
│ ├─ screenshots/ 界面及异常状态截图
│ ├─ diagrams/ 核心流程和评论流程图
│ ├─ browser-tests.json 队友提交的浏览器测试记录
│ └─ unit-tests.xml 队友提交的单元测试记录
├─ package.json 开发测试命令
├─ package-lock.json 开发依赖版本锁定
├─ README.md 下载、操作与限制说明
├─ BLOG_DRAFT.md 早期博客草稿
└─ BLOG_FINAL.md 本文整理稿
5.1 测试人员如何运行
- 打开主仓库,选择 Code → Download ZIP,将整个压缩包解压。
- 保持
index.html、css、js、assets的相对位置,用 Google Chrome 打开index.html。 - 在广场浏览示例信息,输入关键词或日期筛选,点击卡片查看详情和复制联系方式。
- 发布一条自己的测试信息,可选上传照片;在“我的发布”中修改状态或删除。
- 打开任意启事,在评论区域填写昵称和线索,刷新后检查本地保存结果。
日常使用不需要 Node.js、服务器或联网。Node.js 和浏览器测试依赖只用于开发验收。请勿只下载 HTML 而遗漏资源,也不要继续使用合并前的旧压缩包。
5.2 数据保存范围
同一 Chrome 用户配置、同一路径下可继续读取原数据。更换设备、浏览器用户配置、无痕会话或移动文件路径后,不应假定旧数据会自动出现。清除浏览器存储会删除本地记录;升级时保留原存储键,无需清空数据。
六、单元测试与自动化验收
6.1 工具选择及学习思路
单元测试使用 Node.js 内置的 node:test 和 node:assert/strict。业务函数不依赖页面,通过输入、返回值和异常即可检查;存储测试使用内存中的 Map 模拟浏览器存储,因此不会修改用户实际发布的信息。
学习和应用这套方法可以分成四步:先找出可独立调用的函数;再写清给定输入的预期结果;然后使用 assert.equal、assert.deepEqual 或 assert.throws 表达预期;最后运行测试,从失败信息定位具体分支。修正后重新运行整个测试集,检查是否破坏原功能。
6.2 一份可以照着操作的简易教程
第一步: 安装 Node.js 20 或以上版本,仅做测试时需要。打开项目目录中的 CMD,输入:
node --version
node --test tests/*.test.js
第二步: 理解一个最小测试。下面是可运行的教学示例,数据使用固定日期,不受运行当天影响。需要实践时可另存为 tests/tutorial.example.js,再执行 node --test tests/tutorial.example.js;它不计入仓库现有的 47 个测试。
const test = require('node:test');
const assert = require('node:assert/strict');
const M = require('../js/model.js');
test('起止日期相同时,包含当天的物品', () => {
const items = [
{ id: 'a', date: '2026-09-28', createdAt: '2026-10-01' },
{ id: 'b', date: '2026-09-29', createdAt: '2026-10-01' }
];
const result = M.filterItems(items, {
startDate: '2026-09-28', endDate: '2026-09-28'
});
assert.deepEqual(result.map(item => item.id), ['a']);
});
这个测试刻意让发布时间与物品日期不同,可以发现“错误地使用发布时间筛选”的问题。把日期比较中的 >= 边界理解成严格大于时,同一天测试就会失败。
第三步: 阅读项目中的实际异常测试。下面节选自评论测试,用配额异常模拟存储已满:
const full = {
getItem: storage.getItem,
setItem: () => {
throw Object.assign(new Error('quota'), {
name: 'QuotaExceededError'
});
}
};
assert.throws(() => M.addComment(full, 'a', draft), /存储空间不足/);
assert.equal(storage.getItem(M.commentStorageKey('a')), before);
测试前已创建内存存储、保存原评论,并将原值记录为 before。两条断言同时检查“有明确提示”和“旧记录没有被覆盖”,而不仅是检查函数有没有抛出异常。
第四步: 页面功能还需要 Chrome 测试。安装仓库锁定的开发依赖后执行:
npm ci
npm run test:browser
脚本使用独立浏览器上下文,在离线模式下直接访问本地 HTML,覆盖真实点击、输入、上传、刷新和手机视口。运行时会生成截图及 test-results/browser.json。这与只检查模型函数的单元测试相互补充。
6.3 白盒思路与用例设计
用例围绕代码中的条件判断设计:有效/无效输入、合法/非法状态、发布者匹配/不匹配、存在/不存在记录、正常/失败存储。对于长度、日期和文件大小采用边界值;对于列表筛选采用条件组合;对于可能破坏原数据的操作检查写入前后是否一致。
下表列出实际测试覆盖的代表性情形,部分行对应同一个测试函数中的多条断言;全套测试共 47 项。
| 编号 | 函数或场景 | 输入与构造方式 | 预期结果 | 结果 |
|---|---|---|---|---|
| 1 | createItem |
完整合法的寻物信息 | 创建成功,默认进行中 | 通过 |
| 2 | validateDraft |
空物品名称 | 返回名称错误 | 通过 |
| 3 | validateDraft |
不存在的日历日期 | 拒绝创建 | 通过 |
| 4 | filterItems |
名称、地点、描述中的关键词及大小写变化 | 匹配对应启事 | 通过 |
| 5 | filterItems |
同时指定类型、类别、状态 | 只保留全部符合条件的记录 | 通过 |
| 6 | updateStatus |
正确发布者标记完成,再恢复 | 状态正确变化 | 通过 |
| 7 | updateStatus |
非发布者、记录不存在或非法状态 | 分别拒绝 | 通过 |
| 8 | removeItem |
删除自己的某条信息 | 其他启事和照片不变 | 通过 |
| 9 | removeItem |
尝试删除他人或演示信息 | 拒绝删除 | 通过 |
| 10 | 照片规则 | 无照片、三张以内、超过三张 | 接受可选合法照片,拒绝超量 | 通过 |
| 11 | 照片规则 | 空文件、不支持格式、超过 5 MB | 返回明确错误 | 通过 |
| 12 | 图片数据 | 200 KB 边界与超出边界、外链或 SVG | 只接受受限图片数据 | 通过 |
| 13 | 日期筛选 | 起止日期与物品日期相同 | 包含当天 | 通过 |
| 14 | 日期筛选 | 仅开始、仅结束、两端为空 | 单侧限制或不限日期 | 通过 |
| 15 | 日期校验 | 开始晚于结束、2 月 30 日 | 提示错误并返回空结果 | 通过 |
| 16 | 日期组合筛选 | 日期与类型、类别、状态、关键词组合 | 正确匹配或明确空结果 | 通过 |
| 17 | 评论校验 | 空白、昵称 24/25 字、正文 500/501 字 | 接受合法边界,拒绝超长或空白 | 通过 |
| 18 | 评论持久化 | 保存后重新读取 | 内容、时间和启事关联保持一致 | 通过 |
| 19 | 评论隔离 | 两个启事 ID,包括特殊字符 | 分别保存,不串条 | 通过 |
| 20 | 评论排序 | 时间乱序及相同时间 | 时间升序,同时间保持插入顺序 | 通过 |
| 21 | 保存失败 | 注入 QuotaExceededError |
提示失败,原记录不变 | 通过 |
| 22 | 损坏评论 | 无效 JSON、错误结构、关联错误 | 读取报错,提交不覆盖原数据 | 通过 |
| 23 | 评论清理 | 删除目标启事的评论 | 不影响其他启事、照片和评论 | 通过 |
| 24 | 页面安全展示 | 含 HTML 标签的昵称和正文 | 作为文字显示,无脚本执行 | Chrome 验收通过 |
对于测试人员可能输入的异常数据,重点考虑了空格与换行、不存在的日期、相反的日期范围、超量图片、超长评论、错误的发布者标识和保存失败。测试关注具体可观察结果,而不是只走一次正常发布流程。
6.4 实际结果与测试边界
| 验证轮次 | 环境与方式 | 结果 |
|---|---|---|
| 队友提交前验收 | 2026-10-10,Chrome 154.0.8037.93;记录已随 PR 提交 | 47/47 单元测试,10/10 页面场景 |
| 合并后复验 | 2026-10-10,合并版本 ff5ffd1;Chrome 155.0.8059.39 |
47/47 单元测试,10/10 页面场景 |
合并后页面复验在独立副本中使用已有 Playwright 1.62.1 的测试接口,未重新验证 package-lock.json 所锁定的 Playwright 1.64.0 安装过程。两轮环境不同,分别记录,不把它们混写为同一环境。合并后页面复验未出现页面脚本错误,也没有 HTTP 资源请求;覆盖了 320、390、768 像素宽度下的布局检查。


这些测试覆盖了主要业务分支、关键边界和保存异常,能够支持本次纯前端演示验收;没有生成分支覆盖率报告,因此不声称达到 100% 代码覆盖率,也不能据此保证没有任何缺陷。尚未验证真实多用户并发、跨设备共享或大量数据下的性能,因为本版没有对应的服务端能力。后续可补充更全面的可访问性检查和大数据量测试。
七、GitHub 协作与分阶段提交
本次保留了真实开发提交,功能完成后的本地 commit 与上传远程的 push 并不总是一一对应。早期受上传环境影响,曾通过 GitHub 网页上传并调整文件目录,随后合并保留了原有本地开发历史;后续部分本地提交由一次 push 一起上传。
| 阶段 | 提交或 PR | 内容 |
|---|---|---|
| 基础模型 | 77c68cc |
发布校验、筛选和状态规则 |
| 基础交互 | 27969a0 |
响应式页面与失物招领流程 |
| 测试与说明 | 55d1924 |
基础测试、使用文档 |
| 网页上传与目录整理 | bedbb3d、daedfbd、71d5ee9、fd202be、3810bae、d8fa1db |
上传文件并整理 CSS、JS 和测试文件的位置 |
| 历史整合 | 5a02abf |
保留最初本地开发提交,整合网页上传历史 |
| 照片功能 | 104cb16 |
图片上传、压缩与详情图库 |
| 图片展示 | 6e2d334 |
示例物品与写实照片对应 |
| 删除功能 | eb6ec0b |
发布者二次确认后删除 |
| 队友功能开发 | 78cb701 |
日期筛选、评论与界面;此时包含共享服务方案 |
| 调整运行范围 | 6e48bbf |
移除共享服务依赖,评论改为本地存储 |
| 验收与文档 | 2ab1d54、8f43d7a |
测试记录、截图、运行说明和提交链接 |
| 合并协作成果 | ff5ffd1 / PR #1 |
队友功能分支合并到主仓库 |
以下截图于 2026 年 10 月 10 日(北京时间)在 Chrome 中直接打开 GitHub 公开页面取得,保留网页显示的日期、提交信息和合并状态。页面原始日期显示与本地日期口径可能不同,未对截图内容进行改写。

上图可看到照片上传、图片匹配、删除功能,以及队友的日期筛选、评论调整和最终合并记录。

上图保留了基础模型、页面流程和测试阶段,也如实展示了网页上传期间的目录移动提交。

PR 页面显示 Merged,从队友 Fork 的功能分支合并了 4 个提交。完整提交历史长截图另存于

,截图来源及浏览器记录见 docs/github-screenshot-evidence.json。
八、遇到的问题、尝试与收获
8.1 本地有提交,远程却看不到完整开发过程
问题: 早期上传遇到 TLS、Git 辅助进程和认证相关错误,网页上传又产生了文件移动等提交,远程记录一度不能直观反映最初的开发阶段。
尝试: 检查远程地址和本地分支,先保留原开发历史;通过网页上传处理文件,再整合历史。后续在本机 CMD 中执行 push,确认本地提交上传成功,并用 git status -sb 检查本地与远程跟踪分支的关系。
结果: 原始开发提交已保留,照片、删除等后续成果也进入远程,队友 PR 已合并。不过 Codex 内的 Git 辅助进程故障尚未修复,不能把本地 CMD 上传成功写成所有环境都恢复正常。
收获: commit 是保存开发阶段,push 是同步到远程,两者要分别检查;目录整理提交应如实说明,不能充当新的功能开发记录。
8.2 评论方案超出了最终运行范围
问题: 希望不同浏览器看到公共评论时,需要共享数据来源,最初方案引入了本地共享服务,与最终“直接打开 HTML 使用”的要求不一致。
尝试: 重新明确纯前端范围,改为按启事 ID 保存本地评论,同时更新页面提示、README 和自动化测试,移除服务入口与运行依赖。
结果: 在关闭共享服务、浏览器离线的条件下,仍可直接打开 HTML 使用日期筛选、留言和原有功能;不同浏览器不会同步,文档与页面明确说明这一限制。
收获: 增加一个功能时,要一起确认它的运行条件和数据范围。提出“公共评论”与实现“本地留言”代表不同能力,验收材料必须与实际功能一致。
8.3 图片与旧数据的兼容
问题: 增加上传图片后,旧启事可能没有照片,例如此前自己发布的手机信息。图片较大时,还会增加本地存储压力。
尝试: 图片设为可选字段,读取旧记录时兼容缺失的照片数组;实际上传照片优先展示,示例图片只匹配对应演示启事;对原图限制数量、格式和大小,并在保存前压缩。
结果: 没有照片的旧记录仍可读取,不会错误地使用无关物品照片。测试验证了照片刷新保存、状态更新保留照片,以及容量不足时的错误提示。
收获: 新增字段不能要求用户清空旧数据才能升级,演示图片也不能假装成用户已经上传的照片。
九、对队友的评价
值得学习的地方: 沈冠宇补充功能时,也提交了对应测试、截图和运行说明。日期筛选没有停留在“增加两个输入框”,还处理了单侧日期、起止边界、无效范围和组合条件;评论也覆盖了保存失败、输入保留和启事隔离。这提醒我,功能交付应包括正常操作、异常反馈和可复现的验证材料。
需要改进的地方: 开发前对运行范围还可以确认得更早。评论最初采用共享服务方案,后面改为纯前端,带来了代码、测试和文档的同步调整。以后可以在任务开始时写清“是否需要服务器、数据是否共享、怎样验收”,完成一个小阶段后就按约定方式运行一次,减少最后集中返工。
对自己的反思: 我在整合阶段需要更及时地检查远程提交是否同步、博客说明是否过期,以及 PSP 是否持续记录。程序能运行只是交付的一部分,最终说明和协作证据也应随着开发更新。
十、总结
通过这次结对作业,我们将第一次设计的“校园拾光”原型转化成了可以实际运行的网页,完成了“发布信息—浏览或搜索—查看详情—联系发布者—更新状态”的基本流程,并增加了照片上传、日期筛选、本地评论、删除确认和手机布局。对我来说,这次作业让我更具体地理解了从原型设计到程序实现之间的差距:原型中一个简单的按钮,落实到代码里,往往还涉及输入校验、数据保存、异常提示和页面更新等一系列工作。
在我负责的基础功能与项目整合中,体会比较深的是数据处理的顺序。例如,发布信息时,如果先更新页面再保存数据,一旦浏览器存储失败,用户就可能看到“发布成功”,却在刷新后找不到刚才的内容。因此,程序采用先保存、后更新界面的方式。增加照片功能时,也需要兼容原先没有图片字段的启事,不能要求用户清空旧数据才能继续使用。这些细节让我开始关注功能在不同情况下是否都能给出合理结果。
日期筛选和评论功能的完善,也让我认识到提前明确需求范围的重要性。评论最初采用了共享服务方案,后来根据直接打开 HTML 使用的要求,调整为浏览器本地存储。这个变化同时影响了代码、测试方式和使用说明。如果一开始就明确“是否需要服务器、数据是否跨设备共享、测试人员如何运行”,就能减少后续调整。今后拆分任务时,我会把运行条件和验收方法一起写清楚,让双方对最终要交付的内容有一致的理解。
测试是这次作业中另一项重要收获。项目的 47 个单元测试和 10 个 Chrome 页面验收场景,除了检查正常操作,还覆盖了日期范围错误、空白评论、图片超限、存储失败和非发布者修改状态等情况。设计这些测试时,需要先明确程序在每一种情况下应该怎样表现,再用断言验证结果。这个过程也能反过来发现需求中没有考虑清楚的地方。测试全部通过能够为当前版本提供依据,但仍不能代替对真实使用情况的持续检查。
在结对协作方面,我负责基础功能和主仓库整合,沈冠宇通过 Fork 和功能分支补充日期筛选、评论与界面调整,再通过 PR 合并到主仓库。这让我熟悉了从独立开发到整合交付的协作过程。队友同时提交了测试、截图和说明,也提醒我:交付一个模块时,应让接收者能够理解改动,并复现验证结果。合并之后重新运行原有功能和新增功能的测试,是确认双方成果能够一起工作的必要步骤。
Git 上传过程中遇到的问题,则让我进一步区分了本地 commit 和远程 push。代码在本地保存了提交,并不代表 GitHub 上已经同步;网页上传产生的目录调整记录,也需要与实际功能开发记录区分。以后我会在每个阶段完成后及时检查分支状态和远程历史,并同步维护 README、截图和博客,减少最后集中整理时的遗漏。
浙公网安备 33010602011771号