软件工程第二次结对作业
2026 秋软件工程第二次结对作业:校园失物招领程序实现
| 项目 | 内容 |
|---|---|
| 课程 | 2026 秋软件工程 |
| 作业要求 | 第二次结对作业之程序实现 |
| 本人 | 秦文贵,102401615 |
| 结对同学 | 吴圣洲,102401622 |
| GitHub 仓库 | vg666-NiKo/102401615-102401622 |
| 项目形式 | PC Web,兼顾手机尺寸;HTML、CSS、原生 JavaScript |
| 运行方式 | 完整下载仓库并解压,使用 Chrome 打开根目录 index.html |
一、项目简介:从原型到可以操作的网页
第一次结对作业中,我们使用墨刀设计了校园失物招领原型,确定了首页、搜索、发布、详情和“我的发布”等页面。本次沿着原来的使用流程,把按钮背后的数据处理和异常情况补齐。
校园失物信息经常分散在班级群、宿舍群和朋友圈里。想找东西的人需要反复翻消息,捡到东西的人也未必知道应该发到哪里。我们希望用一个统一入口,把寻物与招领信息按照名称、类别和地点组织起来,让“发布—查找—联系—结束信息”能够接着完成。
本次程序保留了蓝白配色和底部导航,实现以下三条主要路径:
- 发布信息:选择寻物/招领,填写物品信息,选填照片,校验通过后保存。
- 浏览查找:首页浏览或组合搜索,进入详情,查看或复制联系方式,再通过外部渠道核对和交接。
- 结束与维护:在“我的发布”中编辑、删除、标记已找回/已归还,必要时恢复进行中。
程序使用浏览器 localStorage 保存记录和照片,适合下载后直接验收。它目前是本地运行的功能实现版本,没有后台账号、跨设备同步、地图定位或应用内聊天。联系功能提供联系方式,实际沟通与物品交接在应用外完成。

二、具体分工与协作过程
我们采用先完成主体、再由搭档完善的方式推进。我的重点是把页面和核心流程接起来,吴圣洲在 fork 仓库中补充功能、调整界面和完善说明,最后通过 PR 合并到主仓库。
| 成员 | 主要工作 | 可以核对的成果 |
|---|---|---|
| 秦文贵(102401615) | 原型归档、项目目录和页面框架;首页、发布、校验、本地保存;搜索、详情和复制;我的发布、编辑删除、状态更新;照片上传和线索推荐;初始测试与阶段文档 | 主仓库各阶段功能提交、业务与页面模块、初始 80 个单元测试 |
| 吴圣洲(102401622) | 首页最新 30 条限制;新增边界测试;全局样式和页面视觉优化;README 重写;发布、浏览查找、我的管理三张流程图 | faaedf8d、f0d2a302 等提交,以及 PR #2 |
| 交接与整合 | 我将已完成的主体交给搭档继续完善;搭档提交 PR 后,我查看变更并合并到主仓库 | 已合并 PR #2 |
协作过程中没有把 fork 仓库里的文件再手动复制一遍,而是通过 Pull Request 查看修改内容、比较差异并合并。这让新增功能和界面调整能够对应到具体提交,也便于后续发现问题时回查版本。
PR #2 包含 4 个提交、9 个文件的变更。最终测试由原来的 80 个增加到 85 个,其中新增的 5 个主要覆盖首页 30 条上限及其边界。
三、PSP 2.1 表格与时间分析
下表采用老师提供的 PSP 2.1 阶段,保留 Planning、Development、Reporting 及其子项。单位均为分钟。
| PSP 2.1 阶段 | 内容 | 102401615 预估 | 102401615 实际 | 102401622 预估 | 102401622 实际 |
|---|---|---|---|---|---|
| Estimate | 估计任务时间 | 15 | 20 | 15 | 15 |
| Analysis | 需求与技术分析 | 40 | 45 | 30 | 35 |
| Design Spec | 生成设计文档 | 30 | 25 | 20 | 30 |
| Design Review | 设计复审 | 15 | 15 | 20 | 20 |
| Coding Standard | 代码规范 | 15 | 10 | 10 | 10 |
| Design | 具体设计 | 45 | 40 | 25 | 30 |
| Coding | 编码/功能完善 | 240 | 260 | 100 | 90 |
| Code Review | 代码复审 | 30 | 25 | 50 | 45 |
| Test | 测试、修复和回归 | 80 | 90 | 120 | 110 |
| Test Report | 测试报告 | 20 | 20 | 30 | 25 |
| Size Measurement | 工作量统计 | 10 | 10 | 10 | 10 |
| Postmortem | 总结与改进计划 | 40 | 35 | 40 | 40 |
| 合计 | 不重复计入分组 | 580 | 595 | 470 | 460 |
我的投入主要增加在测试、复审和材料整理:首次发布成功只是开始,还要检查保存失败、取消编辑、连续选图以及删除后的搜索结果。AI 辅助减少了重复编码,但对照需求审阅代码、复跑测试和验证页面仍需要投入时间。
吴圣洲的完善工作集中在界面、文档和首页列表边界。首页限制为最新 30 条之后,还必须确认搜索仍可查到全部记录、类型筛选统计正确以及原数据没有被截断。这些工作说明“小功能”也需要明确边界和对应测试。
四、解题思路与设计实现
4.1 原型中保留什么,程序中补充什么
我们保留了第一次原型的五个主要页面和核心操作顺序,重点补充真实的数据处理:
- 发布前清理首尾空格,检查必填项、类别、长度和日期。
- 每条信息使用唯一编号,详情按编号读取,避免展示错位。
- 只有本浏览器标记为自己发布的记录可以编辑、删除或更新状态。
- 更新前先保存;写入失败时保留原记录和表单,不提示成功。
- 从搜索结果进入详情后,返回列表保留已有搜索条件。
- 补充无结果、信息不存在、复制失败、照片处理失败和存储异常等分支。
原型中“点一下有反应”与程序中“操作完成并正确保存”是两件事。这次我们主要把后者落实到函数、状态和反馈中。
4.2 模块划分
| 模块 | 职责 |
|---|---|
js/core.js |
发布校验、数据创建、搜索、首页 30 条限制、状态规则、编辑删除和个人统计 |
js/storage.js |
数据读取、格式与版本检查、本地写入及失败反馈 |
js/app.js |
共享数据、导航、通用卡片、首页和发布/编辑表单 |
js/pages/search.js |
搜索条件、组合筛选、清空条件和结果列表 |
js/pages/detail.js |
详情、联系、复制和相似线索展示 |
js/pages/my-posts.js |
我的统计、状态筛选及管理操作 |
js/images.js |
图片校验、压缩、选择状态和图片渲染 |
js/recommendations.js |
名称相似度、类别与时间筛选、排序及推荐理由 |
js/clipboard.js |
联系方式复制的异步成功/失败判断 |
业务规则尽量不直接操作 DOM,因此可以在 Node.js 中单独测试。页面模块负责展示结果,保存模块统一处理写入。首页、搜索、详情和“我的发布”读取同一份数据,减少各页面状态不一致的可能。
4.3 数据结构与状态
一条记录的主要字段如下,示例联系方式为虚构内容:
{
id: 'post-example',
type: 'lost',
title: '黑色雨伞',
category: '生活用品',
place: '食堂门口',
eventTime: '2026-10-06 12:00',
createdAt: '2026-10-06T04:10:00.000Z',
description: '黑色长柄,伞柄有蓝色标签',
contact: '演示微信 demo',
owner: '我',
isMine: true,
status: 'active',
image: ''
}
eventTime 是丢失/捡到时间,createdAt 是发布时间。列表按发布时间排序,推荐使用事件时间比较,避免混用这两个概念。
| 信息类型 | active 显示 |
completed 显示 |
|---|---|---|
寻物 lost |
待寻找 | 已找回 |
招领 found |
待认领 | 已归还 |
编辑保留编号、发布时间、归属和完成状态;照片通过独立的图片更新规则更换或移除。状态完成后可以恢复进行中。本地 isMine 是演示版本的归属标记,不是登录认证。
4.4 主要业务流程图
以下三张图已由搭档整理并合并到仓库。
发布信息流程:

填写文字和照片后,先检查输入,再尝试保存。任一步失败都应有提示,并允许用户修正后重试。
浏览、搜索、详情与联系流程:

搜索条件可以组合使用。详情与联系方式对应同一条编号,线索卡片可继续进入另一条详情。
我的发布管理流程:

状态更新、编辑和删除都要考虑取消与保存失败。取消操作不写入数据;保存成功后,各页面读取更新后的记录。
4.5 关键数据流

发布/编辑表单把输入交给业务函数,生成下一份记录集合。saveItems 调用统一存储接口,只有写入成功才替换内存中的 items,然后刷新页面。失败时保留当前数据和待提交输入。首次打开时读取本地记录;没有已存记录才使用演示数据,保存过的空列表不会重新灌入演示信息。
4.6 关键代码:保存成功后才更新页面数据
以下代码来自最终版本的 js/app.js:
function saveItems(nextItems) {
const result = store.save(nextItems);
if (result.ok) items = nextItems;
return result;
}
这里不是先修改页面再尝试保存,而是依据保存结果决定是否替换共享数据。这样,存储容量不足时不会出现“页面说保存了,刷新却消失”的误导。
4.7 关键代码:组合搜索
以下代码来自 js/core.js:
function searchItems(items, options) {
const opts = options || {};
const type = opts.type || 'all';
const category = opts.category || 'all';
const words = String(opts.keyword || '').trim()
.toLocaleLowerCase().split(/\s+/).filter(Boolean);
return filterItems(items, type).filter(function (item) {
if (category !== 'all' && item.category !== category) return false;
if (opts.activeOnly && item.status !== 'active') return false;
const haystack = [item.title, item.place, item.category, item.description]
.join(' ').toLocaleLowerCase();
return words.every(word => haystack.includes(word));
});
}
例如“黑色 图书馆”要求同一条记录同时包含两个词,但两个词可以分别出现在名称和地点中。联系方式不纳入检索。首页最多显示 30 条,搜索查询的是完整数据集,因此首页的展示限制不会删除旧信息。
五、附加功能设计与展示
5.1 照片上传、压缩与保存
纯文字描述有时难以区分相似物品,因此我们给发布表单增加了选填照片。照片会出现在首页、搜索和我的卡片中,也能在详情查看较完整的比例。
图片支持 JPG、PNG、WebP,原文件不能超过 5 MB。处理时把最长边缩到 1280 像素以内,再转换为 JPEG;透明背景填充白色,图片数据控制在 500000 字符以内。图片处理完只表示预览准备完成,仍需提交并保存成功才会更新记录。
连续选择不同照片时,先选的照片可能更晚读取完。我们用递增编号判断回调是否仍属于最新选择;用户移除照片或取消编辑时,也会让旧任务失效。
下面节选自 js/images.js:
const current = ++ticket;
const previous = state.image;
emit({image: previous, busy: true, error: ''});
try {
validateFile(file);
const image = await process(file);
if (!image || !validImage(image)) {
throw Error('图片处理失败或压缩后仍过大,请换一张图片。');
}
if (current === ticket) emit({image, busy: false, error: ''});
} catch (error) {
if (current === ticket) {
emit({image: previous, busy: false,
error: error.message || '无法读取图片,请重新选择。'});
}
}
这个处理也覆盖“处理中移除照片”和“取消编辑恢复原发布草稿”。读取失败时保留之前照片,显示错误,不把失败当作成功。

5.2 可解释的相似线索推荐
寻物者可能不知道已经有人发布了招领信息。我们在未完成信息的详情页增加“相似线索”,主动推荐相反类型的未完成记录,并显示推荐理由。
推荐规则先筛选,再评分:
| 项目 | 规则 |
|---|---|
| 类型与状态 | 寻物推荐招领,招领推荐寻物;排除自己和已完成记录 |
| 类别 | 必须属于同一类别 |
| 名称 | 规范化后计算相似度;低于 0.25 不推荐 |
| 时间 | 两条事件时间都有效时,相差超过 7 天排除 |
| 地点 | 根据地点文字的相似程度加分,没有地图定位 |
| 排序 | 名称为主,结合地点与时间;同分按发布时间和编号排序 |
| 展示 | 最多 3 条,显示名称、类别、地点与时间方面的匹配理由 |
名称相同得到最高相似度;至少两个字符的包含关系次之;其余比较连续双字集合。比如“黑色雨伞”和“蓝色雨伞”共享“色雨”“雨伞”,可以成为待核对线索;“黑色雨伞”和“黑色水杯”的相似度较低,会被排除。
以下代码来自 js/recommendations.js:
if (item.id === source.id || item.type !== opposite ||
item.status !== 'active' || item.category !== source.category) return;
const name = similarity(source.title, item.title);
if (name < 0.25) return;
const targetTime = eventTimestamp(item.eventTime);
const validTime = Number.isFinite(sourceTime) && Number.isFinite(targetTime);
const gap = validTime ? Math.abs(sourceTime - targetTime) / DAY : NaN;
if (validTime && gap > 7) return;
推荐只是缩小人工核对范围,不代表两条信息一定属于同一物品。照片和联系方式不参与评分,地点也是文字匹配。详情每次根据最新记录重新计算,状态完成或删除后不会继续推荐该记录。

5.3 首页展示上限与界面完善
搭档在最终版本中增加首页最新 30 条限制,避免记录较多时首页过长。限制发生在筛选与排序之后,同时返回筛选后的总数;超出部分仍保存在本地,可从搜索页查询。
const homeLimit = 30;
function homeItems(items, type) {
const sorted = filterItems(items, type);
return {
items: sorted.slice(0, homeLimit),
total: sorted.length,
limit: homeLimit
};
}
搭档还统一了渐变、徽标、卡片、输入框和导航样式,并添加窄屏适配及减弱动效设置。这里更看重操作入口、错误反馈和内容层次是否清楚,最终显示效果需要通过浏览器截图展示。
六、目录说明与使用说明
6.1 目录说明
| 路径 | 内容与作用 |
|---|---|
index.html |
正式程序入口 |
README.md |
使用流程、规则和运行方法 |
css/style.css |
全局视觉、卡片、表单、导航与响应式样式 |
js/core.js、js/storage.js |
业务规则与本地存储 |
js/app.js |
应用导航、共享数据、首页及发布/编辑 |
js/pages/ |
搜索、详情和我的发布模块 |
js/images.js、js/clipboard.js、js/recommendations.js |
图片、复制和线索推荐 |
js/data.js |
首次运行的虚构演示记录 |
tests/ |
9 个测试文件,共 85 个单元测试 |
docs/images/ |
发布、浏览查找与我的发布三张流程图 |
docs/开发计划.md、docs/PSP.md |
开发计划及初始化 PSP 预估 |
docs/阶段2验收.md 至 docs/阶段8验收.md |
各阶段自动检查与人工验收步骤 |
docs/线索推荐设计.md |
推荐功能规则和设计说明 |
prototype/ |
第一次作业的参考原型及归档材料 |
6.2 测试人员运行方法
- 打开 GitHub 仓库,点击 Code → Download ZIP。
- 完整解压,保留 HTML、CSS、JavaScript 和目录关系。
- 使用 Google Chrome 打开根目录的
index.html。网页运行无需 Node.js、npm 安装或服务器。 - 首次会显示 5 条虚构演示信息,可先浏览、搜索和查看详情。
- 发布自己的寻物/招领信息,再到“我的发布”检查编辑、删除和状态更新。
- 在相同目录、相同 Chrome 用户配置下刷新,检查保存结果。
推荐的验收路线是:搜索“校园卡”并打开详情;展开联系方式;发布一条带照片的寻物信息;发布一条名称、类别、时间接近的招领信息;查看相似线索;取消一次状态更新,再确认完成;最后刷新页面核对状态和照片。
浏览器本地数据不跨设备共享。未提交的草稿只在本次页面运行中暂存,刷新后不会恢复;搜索条件也不持久化。读取到损坏数据时,程序显示警告并暂停写入,避免自动覆盖原记录。
七、单元测试:从“看起来正常”到明确预期
7.1 测试工具与学习过程
本项目采用 Node.js 内置的 node:test 和 node:assert/strict,无需额外安装测试库。test() 定义用例,断言比较实际结果与预期;异常分支通过 assert.throws() 检查,异步选择则控制 Promise 的完成顺序。
学习资料与复跑入口:
- Node.js 24 Test runner 官方文档:测试组织、命令行运行和报告。
- Node.js 24 Assert 官方文档:严格相等、结构比较和异常断言。
- 项目源码:tests 目录。
在 Windows 安装 Node.js 22 或更新版本后,用 PowerShell 切换到包含 index.html 和 tests 的项目根目录,执行:
node --version
node --test tests/*.test.js
我第一次复跑时在解压目录的上一层执行了测试,输出的测试数为 0,看起来像命令执行成功,实际没有运行项目用例。进入正确的仓库文件夹后才得到正常结果。
7.2 最终结果与测试分布
2026 年 10 月 7 日,按仓库提交 94858a87 读取最终代码,在 Node.js v24.19.0 中复跑,结果为:
tests 85
pass 85
fail 0
cancelled 0
skipped 0
todo 0
| 测试文件 | 数量 | 主要检查内容 |
|---|---|---|
core.test.js |
11 | 排序、类型、状态文案、首页 30 条边界 |
publish.test.js |
8 | 必填、空白、长度、类别与时间、创建记录 |
storage.test.js |
7 | 初次载入、恢复、空列表、损坏数据和写入失败 |
search.test.js |
10 | 多词、大小写、组合筛选、空结果与详情编号 |
my-posts.test.js |
8 | 归属、统计、完成与恢复、持久化 |
manage-posts.test.js |
10 | 编辑字段保留、权限、删除与同步 |
clipboard.test.js |
4 | 成功、拒绝、接口缺失与空文本 |
images.test.js |
15 | 图片规则、缩放、连续选择、取消与恢复 |
recommendations.test.js |
12 | 类型、类别、名称、七天边界、排序与状态变化 |
| 合计 | 85 | 正常、异常、边界和数据一致性 |

7.3 白盒测试用例设计
下面按代码中的判断分支说明部分用例。表中“通过”指对应自动用例已通过。
| 编号 | 检查目标 | 输入或操作 | 预期结果 | 结果 |
|---|---|---|---|---|
| T01 | 发布必填校验 | 名称、地点、联系方式只填空格 | 校验失败,错误定位到对应字段 | 通过 |
| T02 | 文本边界 | 名称 40/41 字,描述 500/501 字 | 边界值通过,超出值拒绝 | 通过 |
| T03 | 时间有效性 | 2024-02-29 与 2026-02-30 | 闰日通过,非法日期拒绝 | 通过 |
| T04 | 类型与类别 | 非法枚举值 | 不创建记录 | 通过 |
| T05 | 多关键词 AND | “黑色 图书馆”及“雨伞 食堂” | 前者命中对应记录,后者无结果 | 通过 |
| T06 | 组合条件 | 名称、类别、类型与仅未完成叠加 | 必须同时满足条件 | 通过 |
| T07 | 检索边界 | 查询联系方式或 .* |
联系方式不参与匹配,特殊字符不作正则 | 通过 |
| T08 | 首页上限 | 3/30/35 条场景 | 不足全部显示,正好不截断,超出只显示最新 30 条 | 通过 |
| T09 | 数据不变性 | 排序、搜索、截断、编辑或删除 | 不修改传入数组与原记录 | 通过 |
| T10 | 修改归属 | 他人记录或不存在编号 | 业务函数拒绝操作 | 通过 |
| T11 | 状态恢复 | 标记完成,再恢复进行中 | 文案、未完成检索及推荐对应变化 | 通过 |
| T12 | 编辑保持字段 | 修改名称和地点,并传入伪造 ID/状态 | 更新允许字段,保留编号与完成状态 | 通过 |
| T13 | 空列表保存 | 删除全部自己的记录后保存空数组 | 重新读取仍为空,不恢复演示数据 | 通过 |
| T14 | 存储异常 | 损坏 JSON、读取受限、容量不足 | 不覆盖原记录,返回失败或警告 | 通过 |
| T15 | 图片校验 | SVG、空文件、超过 5 MB 和无效数据 | 拒绝处理,保留原照片 | 通过 |
| T16 | 图片异步顺序 | 第二张先完成,第一张后完成 | 以最后选择的照片为准 | 通过 |
| T17 | 移除与取消 | 图片处理中移除或恢复草稿 | 迟到回调不能重新覆盖照片 | 通过 |
| T18 | 推荐时间边界 | 相差 7 天及 7 天零 1 分钟 | 前者保留,后者排除 | 通过 |
| T19 | 推荐更新 | 候选完成、恢复或删除 | 线索相应消失、恢复或移除 | 通过 |
| T20 | 复制异常 | 剪贴板拒绝、接口缺失、空文本 | 不误报复制成功 | 通过 |
7.4 部分真实测试代码
以下节选自 tests/search.test.js。items 是文件中定义的固定样例:
const ids = options => searchItems(items, options).map(x => x.id);
test('多个关键词需同时命中,可分别命中不同字段', () => {
assert.deepEqual(ids({keyword: '黑色 图书馆'}), ['1']);
assert.deepEqual(ids({keyword: '黑色 食堂'}), ['2']);
assert.deepEqual(ids({keyword: '雨伞 食堂'}), []);
});
第一条判断两个词能否分别命中不同字段,第二条确认描述中的颜色也参与匹配,第三条提供不命中的情况。这样测试的是检索规则,不只是“返回了一个数组”。
以下来自搭档补充的 tests/core.test.js:
test('首页上限为 30 条,超出时只保留最新 30 条', () => {
const list = manyItems(35);
const shown = homeItems(list, 'all');
assert.equal(homeLimit, 30);
assert.equal(shown.limit, 30);
assert.equal(shown.total, 35);
assert.equal(shown.items.length, 30);
assert.deepEqual(shown.items.map(x => x.id),
list.slice().reverse().slice(0, 30).map(x => x.id));
});
它同时检查总数、显示数量和具体顺序,防止把“删除了旧数据”误写成“只限制首页展示”。
7.5 如何考虑测试人员的“刁难”
设计测试时,我不只考虑完整输入,而是先找出每个分支的触发条件:空串与空白、恰好到上限与超出一字、合法闰日与日期溢出、本人和他人记录、先后顺序相反的异步任务,以及保存成功和失败。
真实浏览器还需要补充另一类检查:照片是否显示正确,确认框选取消后是否保持原状态,复制成功后能否粘贴到记事本,返回详情是否保留筛选,窄屏中按钮是否可点击。这些检查要按“操作、预期、实际”记录,而不能用函数测试代替。
八、GitHub 签入与协作记录
我们按功能逐步提交,使原型归档、页面搭建、核心操作、附加功能和搭档完善可以分开检查。下面列出主要提交;短编号可以在仓库中检索。
| 提交 | 提交内容 | 贡献者 |
|---|---|---|
| 0d81baed | 归档第一次原型,添加开发计划和 PSP 预估 | 秦文贵 |
| 4e8a216f | 页面框架、首页浏览和类型筛选 | 秦文贵 |
| 6c2c49dc | 发布、表单校验和本地保存 | 秦文贵 |
| e8d3fee9 | 组合搜索、详情和返回列表 | 秦文贵 |
| 2bbfba06 | 复制、我的发布和状态更新 | 秦文贵 |
| eaf8c0b5 | 编辑、删除和取消编辑 | 秦文贵 |
| 565d9f8d | 照片上传、预览和本地保存 | 秦文贵 |
| f10a6062 | 相似线索推荐与匹配理由 | 秦文贵 |
| 06a8664f | 按使用流程重写 README | 吴圣洲 |
| 16e27afc | 补充三张使用流程图 | 吴圣洲 |
| faaedf8d | 首页最新 30 条限制与新增单元测试 | 吴圣洲 |
| f0d2a302 | 重构全局样式,优化页面视觉与图标 | 吴圣洲 |
| 1c40e6ad | 合并 PR #2 | 秦文贵 |

九、遇到的问题、尝试与解决方法
9.1 解压和增量更新容易混淆
开发采用分阶段更新,我曾不确定应该上传累积项目的全部文件,还是只上传新阶段解压出的内容。外层压缩包目录如果直接上传,也可能让代码多出一层目录,导致 HTML 找不到脚本。
处理方法是:本地把本次包内内容合并到同一个项目根目录,替换同名文件并保留之前文件;GitHub 上传本次增量包内的内容,保持根目录下的 index.html、css/、js/ 等结构。更新后再检查路径和测试数量。
9.2 保存失败与异步选图的潜在问题
这两类不是已确认发生在我电脑上的故障,而是实现时主动处理并用测试检查的风险:保存失败后如果仍然更新内存,页面与刷新结果会不一致;连续选图如果不区分任务,较早的读取可能覆盖最新选择。
我们分别采用“先保存、后更新共享数据”和“递增任务编号、忽略过期回调”。对应存储失败、图片乱序与取消操作的自动测试已通过,浏览器层面的反馈仍按验收步骤核对。
十、个人总结与对搭档的评价
10.1 我的总结
这次作业让我更明确地理解了从原型到程序的差别。原型主要描述页面和操作顺序,程序则必须说明输入怎样进入记录、记录怎样保存、失败时怎么办,以及一次修改如何影响其他页面。
我负责主体实现,开始时更关注发布、搜索和详情能否跑通。随着功能增加,我发现最容易遗漏的往往是边界:编辑是否保留完成状态,取消是否恢复草稿,删除后搜索能否继续找到旧记录,照片读取先后顺序是否影响结果。这些问题需要用规则和测试表达,而不是仅凭点几次页面判断。
本次使用 Codex 辅助代码生成、规则整理、测试和文档初稿。我负责明确需求、按阶段整合与提交、安装环境并复跑测试,再根据实际结果调整。AI 能帮助加快实现,但测试数量、权限范围、保存能力和协作贡献仍需要自己核对,不能把生成结果直接当作已经验收的成果。
后续我会改进两点:一是在开发开始时就统一数据字段、目录和测试命令,减少交接时的歧义;二是把实际时间与问题记录同步写下来,避免最后只能靠回顾估算。功能增加时,也应尽早讨论分工,让搭档更早参与接口和边界的确认。
10.2 我对吴圣洲的评价
值得学习的地方:
吴圣洲在这次合作中表现出了认真负责的态度,也能主动考虑项目还有哪些地方需要完善。主体功能完成后,他没有只停留在“程序能够运行”,而是进一步调整页面样式、补充使用说明和流程图,让项目更便于使用和验收。我觉得他比较突出的优点是做事细致,既关注功能,也重视用户能否看懂、操作是否顺畅以及文档是否清楚。
他值得我学习的一点,是修改功能时会同时考虑边界和验证。例如,增加首页最新 30 条限制时,他也补充了相应测试,检查数量、排序和类型筛选,避免界面调整影响已有数据。这让我认识到,完善一个功能不能只看正常情况下的效果,还应该说明它在不同条件下是否正确。
需要改进的地方:
我认为吴圣洲还可以改进的地方,是更早参与整体设计,并在提交修改时补充更具体的说明。这次他的工作主要集中在主体完成后的完善,如果能在开发初期就一起讨论界面布局、功能边界和验收标准,一些调整就可以提前落实,减少后期集中修改的压力。另外,PR 中除了说明“完善了什么”,还可以写清修改原因、测试方法和结果,让我在合并时更容易判断改动的影响。这也是我们双方下一次合作都需要加强的地方:及时沟通,并把修改依据和验证过程记录清楚。
十一、成果展示与后续改进
本次完成了首页浏览、发布校验、本地保存、组合搜索、详情与联系方式、我的发布管理、照片和相似线索推荐。搭档进一步补充首页展示限制、视觉样式和使用流程说明。项目按功能提交,并通过 PR 合并双方成果,最终 85 个单元测试全部通过。
当前版本仍受本地数据范围限制,规则推荐也可能遗漏同义词或产生相似物品误报。后续可以评估统一后端数据、真实身份机制、同义词和地点规范化,但应先确保当前的核心流程与异常反馈在最终浏览器验收中稳定。
对我而言,这次最主要的收获是:完成项目不只是让功能出现,还要让数据、页面、测试、文档和协作记录彼此一致。

浙公网安备 33010602011771号