软件工程第二次结队作业

结对编程作业:拾光·校园失物招领 Web 项目

项目内容
这个作业属于哪个课程 202601软件工程
这个作业要求在哪里 2026秋软件工程第二次结对作业之程序实现
这个作业的目标 基于第一次结对作业的原型设计,完成核心功能,使“发布信息—浏览或搜索—查看详情—联系发布者—更新状态”的流程清晰、可用
学号 292400336、182400143
结对同学博客链接 张宇天的博客
GitHub 仓库 292400336-182400143

一、项目成员与协作过程

成员 主要工作内容
吴涵彬(292400336) 负责项目整体组织、核心功能整合、数据逻辑设计、测试整理以及最终版本维护。
张宇天(182400143) 负责页面交互优化、界面细节调整、展示效果完善以及部分文档整理工作。
双方共同 通过 GitHub 协作完成代码交流、功能检查和最终验收。

在这次结对开发中,我们采用“共同设计、分工实现、集中完善”的方式推进项目。前期先根据第一次作业中的原型重新梳理需求,明确失物招领系统最重要的不是单纯展示信息,而是让用户能够快速找到目标信息,并完成后续联系和状态更新。

吴涵彬主要负责项目功能实现和整体整合,包括数据结构设计、核心逻辑编写、搜索筛选、状态变化以及测试检查;张宇天主要负责前端页面体验,对布局、交互细节和展示效果进行优化。开发过程中双方会针对功能是否符合使用习惯进行讨论,再决定具体修改方案。

版本管理方面,我们使用 GitHub 保存开发过程。功能完成后及时提交代码,通过提交记录记录阶段成果,并使用 Pull Request 检查合并内容。这样既保留了开发过程,也方便双方发现问题并进行调整。

二、PSP 2.1 时间记录

下表按 docs/PSP.md 明细汇总。Development 和 Reporting 是小计;合计只加 Planning、Estimate 和两个小计,避免重复计数。

PSP 阶段 内容 预估(分钟) 实际(分钟)
Planning 计划 18 17
Estimate 估计任务耗时 14 11
Development 开发小计 440 483
Analysis 需求分析与技术学习 43 37
Design Spec 生成设计文档 34 30
Design Review 设计复审 19 23
Coding Standard 代码规范 14 11
Design 具体设计 67 78
Coding 具体编码 172 187
Code Review 代码复审 29 33
Test 测试和修改 62 84
Reporting 报告小计 98 109
Test Report 测试报告 44 51
Size Measurement 计算工作量 15 11
Postmortem 事后总结与改进计划 39 47
合计 570 620

实际比预估多 50 分钟,具体设计和测试环节超出较多。设计时补充了状态维护入口,测试时增加了空结果和非法输入等边界情况。下次会在编码前先列出状态变化与异常路径。

三、系统设计思路与功能实现

需求和数据结构

在实际校园环境中,失物信息往往存在发布时间分散、信息不完整、后续状态无法同步等问题。因此本项目围绕“信息发布、快速查找、双方联系、结果反馈”这一完整过程展开设计。

系统整体划分为数据处理层和页面交互层。js/store.js 负责信息保存、校验、搜索、排序以及状态管理;js/app.js 负责页面展示和用户操作响应。每条失物信息包含类型、物品名称、分类、地点、时间、描述、联系方式和当前状态等内容,使一条信息能够完整描述寻找过程。

考虑到课程作业需要轻量运行,本项目采用浏览器本地存储方案。首次进入页面会加载示例数据,用户新增的信息会保存到当前浏览器的 localStorage 中。虽然没有接入服务器和账号系统,但这种实现可以完整展示失物招领业务流程。

核心流程图

校园失物招领核心流程图

图中发布和浏览是两条可独立进入的路径。表单经 validateDraft 检查,createItem 创建对象,saveItems 保存;首页调用 filterItems 产生列表;本机发布者调用 updateStatus 后再次保存。数据只在当前浏览器的 localStorage 中更新,没有服务端或跨设备同步。

关键代码片段

发布前先查缺失字段,再查类型和文本长度。页面与数据层共用这一校验规则。

function validateDraft(draft) {
  const required = ['type','title','category','location','date',
    'description','contactName','contact'];
  const missing = required.filter((key) => !String(draft?.[key] ?? '').trim());
  if (missing.length) return { ok:false, message:'请完整填写所有必填项。', missing };
  if (!['lost','found'].includes(draft.type))
    return { ok:false, message:'信息类型无效。', missing:['type'] };
  if (String(draft.title).trim().length < 2)
    return { ok:false, message:'物品名称至少填写 2 个字。', missing:['title'] };
  if (String(draft.description).trim().length < 5)
    return { ok:false, message:'物品描述至少填写 5 个字,便于他人辨认。', missing:['description'] };
  return { ok:true, message:'', missing:[] };
}

检索采用多个条件同时满足,关键词在名称、描述、地点和类别中任一命中即可;最终复制数组并按发布时间倒序排列,不改变原始数组。

function filterItems(items, filters = {}) {
  const keyword = normalize(filters.keyword);
  const type = filters.type || 'all';
  const category = filters.category || 'all';
  const location = filters.location || 'all';
  const status = filters.status || 'all';
  return items
    .filter((item) => type === 'all' || item.type === type)
    .filter((item) => category === 'all' || item.category === category)
    .filter((item) => location === 'all' || item.location === location)
    .filter((item) => status === 'all' || item.status === status)
    .filter((item) => !keyword ||
      [item.title,item.description,item.location,item.category]
        .some((field) => normalize(field).includes(keyword)))
    .slice()
    .sort((a,b) => new Date(b.createdAt) - new Date(a.createdAt));
}

更新状态前先检查条目是否存在、演示身份是否匹配。已经解决的信息再次更新时返回副本,以保留原解决时间。

function updateStatus(items, id, actorId = OWNER_ID) {
  const index = items.findIndex((item) => item.id === id);
  if (index < 0) throw new Error('信息不存在。');
  if (items[index].ownerId !== actorId)
    throw new Error('只有发布者可以更新这条信息。');
  if (items[index].status === 'resolved') return clone(items);
  const next = clone(items);
  next[index].status = 'resolved';
  next[index].resolvedAt = nowIso();
  return next;
}

四、项目特色与实际效果

灵活的信息检索。 校园中寻找物品时,用户通常无法记住完整信息,只能提供部分线索。因此项目支持关键词、类别、地点和状态联合查询。用户可以通过多个条件缩小范围,同时系统会给出匹配数量和无结果提示,降低查找成本。

便捷的沟通入口。 失物招领的最终目标是帮助双方建立联系,因此详情页面提供联系方式复制功能。用户查看物品详情后可以直接复制联系方式,避免手动输入产生错误,也让整个流程更加连贯。

async function copyText(text) {
  try { await navigator.clipboard.writeText(text); showToast('联系方式已复制'); }
  catch (_) {
    const input = document.createElement('textarea');
    input.value = text;
    input.style.position = 'fixed';
    input.style.opacity = '0';
    document.body.appendChild(input);
    input.select();
    document.execCommand('copy');
    input.remove();
    showToast('联系方式已复制');
  }
}

下面是实际页面的运行截图:

信息广场首页

发布信息表单

我的发布与状态

类型、类别和地点组合筛选后,只剩一条符合条件的招领信息:

组合筛选结果

发布“银色U盘”后,详情页展示物品信息和联系方式;点击复制按钮后出现成功提示:

信息详情与联系方式

复制联系方式成功提示

同一条信息在“我的发布”中从“进行中”变为“已找到”,回到首页搜索也能看到更新后的状态:

状态更新前

状态更新后

首页显示更新后的状态

五、目录组织与测试人员使用说明

292400336-182400143/
├─ README.md                         项目功能、运行和使用说明
└─ 校园失物招领-结对编程作业/
   ├─ README.md                     网页目录内的同版使用说明
   ├─ index.html                     页面入口和模板
   ├─ favicon.svg                    标签图标
   ├─ css/styles.css                 页面样式与响应式布局
   ├─ js/store.js                    数据校验、筛选、状态与存储
   ├─ js/app.js                      页面渲染、路由和事件
   ├─ tests/store.test.js            单元测试
   ├─ docs/PSP.md                    时间记录
   ├─ docs/TEST_REPORT.md            测试说明
   ├─ docs/BLOG_DRAFT.md             本文草稿
   ├─ docs/SUBMISSION_CHECKLIST.md   提交检查
   ├─ screenshots/                   页面截图与流程图
   ├─ run-tests.bat                  Windows 测试脚本
   └─ 双击打开网页.bat                Windows 启动脚本

两份 README.md 现按使用者阅读顺序介绍项目功能、目录、运行、使用和测试方法;成员分工、PSP 时间、测试设计与结对协作过程在本博客和 docs/ 中记录。测试人员下载并完整解压项目后,找到包含 index.html 的网页目录,用 Google Chrome 打开该文件即可。网页无需安装依赖、启动服务器或数据库;Windows 也可以双击 双击打开网页.bat。

建议依次检查:搜索“校园卡”→组合筛选→打开详情→发布一条新信息→进入“我的发布”→标记“已找到”或“已归还”→返回首页确认状态。发布信息保存在当前浏览器。要恢复初始 6 条示例数据,可在 Chrome 开发者工具 Application → Local Storage 删除 campus-lost-found-v1 后刷新。

六、单元测试:工具、简易教程和测试数据

我们使用 Node.js 内置的 node:test 和 node:assert/strict,不需要安装其他测试框架。测试目标是可脱离 DOM 运行的 js/store.js。学习时先看基本断言写法,再对照函数的条件分支设计输入和期望结果,最后运行测试并修正失败用例。

简易教程:安装 Node.js 18 或更新版本,进入项目目录运行 node --test tests\store.test.js。测试文件顶部用 require('node:test')、require('node:assert/strict') 引入工具,再引入被测模块。一个用例按“准备输入 → 调用函数 → 断言输出”编写。若断言失败,终端会给出用例名和错误;修复后重新运行全部测试。

以下是两项真实用例。T08 验证类别与地点同时生效;T12 验证非发布者更新状态被拒绝。

test('T08:按类别和地点组合筛选', () => {
  const result = Store.filterItems(Store.seedItems,
    { category:'校园卡/证件', location:'教学楼' });
  assert.equal(result.length, 1);
  assert.equal(result[0].title, '蓝色校园卡');
});

test('T12:非发布者不能修改他人状态', () => {
  const data = [{ ...Store.seedItems[0], id:'other-1',
    ownerId:'other', status:'active' }];
  assert.throws(() => Store.updateStatus(data, 'other-1', 'me'), /只有发布者/);
});

14 个用例分别检查:完整发布、空联系方式、过短描述、创建后的默认状态、名称搜索、描述与地点搜索、类型筛选、类别加地点筛选、状态筛选、本人更新、招领状态文案、非本人更新、发布时间排序、搜索词空格和英文大小写。测试数据覆盖正常输入、边界输入和异常输入。针对可能的“刁难”,还考虑了不存在的搜索词、多条件无结果和重复更新;其中空结果有页面反馈,重复更新有实现分支,现有自动化用例尚未单独覆盖这两项,应如实区分。

本地执行上述命令得到 14 个通过、0 个失败。另有 Chrome 手工走查记录:发布、详情、复制联系方式、更新状态、返回首页核对。手工走查验证页面操作,单元测试验证数据函数。

下图根据本机实际运行 node --test tests/store.test.js 的标准输出排版生成,保留了 14 项用例及通过统计;它是运行结果图,不是终端窗口的原生截屏。

14项单元测试运行结果

七、GitHub 代码签入与结对协作

主仓库:endlessmaybe/292400336-182400143;Fork:Ther-ux/292400336-182400143。PR #1:界面与移动端适配 包含 3 次提交,PR #2:目录与使用说明 和 PR #3:README 使用指南 各包含 1 次提交,均已合并。提交信息按改动性质使用 feat:、docs:,例如 feat: refresh campus lost and found interface、docs: add directory and usage guide for testers 和 docs: present project usage without coursework details。

下图截自 GitHub 主仓库的 Commits 页面,可见双方提交、提交信息及三次合并记录;三张 PR 截图显示了已合并状态。

GitHub代码签入记录

PR1已合并

PR2已合并

PR3已合并

八、开发过程中的问题与收获

问题一:如何体现信息处理完成后的变化。 初始版本主要关注信息展示,但真实使用中找到物品后还需要反馈结果。因此我们增加“我的发布”和状态更新功能,让用户可以把信息从寻找阶段更新到完成阶段。这个过程让我们认识到,一个完整系统不仅需要输入和展示,也需要考虑业务结束后的状态变化。

问题二:搜索功能需要兼顾准确性和易用性。 开发初期发现不同用户输入习惯会影响搜索结果,例如多余空格或大小写差异。因此我们统一处理搜索文本,并增加空结果提示。通过测试不同输入情况,我们进一步认识到异常情况同样属于功能设计的一部分。

协作中的版本衔接:界面改动从 Fork 通过 PR 合入主仓库后,目录说明和 README 也需要跟随最终页面更新。我们用后续两次文档 PR 补齐运行步骤与测试人员使用说明。这个过程提醒我们,功能合并后还要同步检查文档与界面是否一致。

九、对队友张宇天的评价

张宇天在本次结对编程中主要负责页面设计优化和项目展示部分。在合作过程中,他能够从使用者角度观察网页操作是否自然,并针对页面布局、交互细节提出修改意见,使最终作品不仅能够运行,也具有更好的展示效果。

同时,在合作过程中也发现还有提升空间。例如部分页面细节和文档内容如果能够在开发初期同步规划,可以减少后期调整时间。未来进行类似项目时,我们可以提前制定更明确的阶段目标,加强开发过程中的交流,提高整体效率。

十、总结

项目完成了发布、检索、查看联系方式和更新状态的完整本地流程,并以 14 个单元测试和 Chrome 手工走查核对关键路径。如果继续用于真实校园场景,需要服务端存储、账号身份验证和跨设备同步。

posted @ 2026-10-07 22:10  heimdallr_w  阅读(4)  评论(0)    收藏  举报