🚀 Nova的软件工程冒险:从原型到代码,让散落星辰真正回家

📚 第一关:任务加载,双人小队再次出发

结对成员:102402102 张佩琪、102401403 潘岳

项目 内容
课程 软件工程与软件工程实践
本次作业要求 校园失物招领核心功能实现
本篇作业博客 🚀 Nova的软件工程冒险:从原型到代码,让散落星辰真正回家
结对同学博客 软件工程第二次结对作业:从原型到代码,让散落星辰真正回家
GitHub主仓库 Starry-Nova/102402102-102401403
潘岳的Fork pyyy123/102402102-102401403
上次原型设计 双人组队,让遗失的星星回家
呈现方式 PC端Web,使用Google Chrome运行

🧑‍🚀 Nova × 小面包,再次组队!  
上一次,我们画出了星星回家的地图;这一次,要让按钮真的有回应,让信息真的能更新。  
Pick the lost stars, send roses in return.  
拾起散落星辰,归还赠以玫瑰。

“拾星”关注的是校园失物信息分散、群聊消息容易被覆盖的问题。本次把原型中的设计落到代码:发布寻物与招领信息,浏览和搜索,查看详情与联系方式,最后由发布者确认找回或归还。

大家好,我是Nova。🌟

上一次,我和小面包在墨刀里给星星安排好了回家的路线。这一次,老师把任务从“画出页面”升级成了“写出能运行的程序”。于是,页面上的发布按钮不能只负责好看,玫瑰不能只是装饰,已找回也不能只是一句祝福——它们都得有数据、有判断、有后续。

本来以为给原型接上几段代码就可以通关,后来才发现,背包里还藏着状态管理、测试、Fork、PR和合并冲突。星星还没全部回家,Git先给我们出了几道题。🙂

我们没有把画出地图等同于实现真实定位,也不把模拟认证称为真实身份验证,课程要求的核心流程优先。

【首页全景】
3850876-20261010011335111-1628495107

🤝 第二关:分配装备,各自负责也互相检查

本次分工围绕视觉与浏览、业务与测试两条线展开。下表与提交方向对应,具体实现、学习和复核范围仍由两人按实际操作确认。

成员 主要负责范围 协作检查
张佩琪 / Nova 浅色星空视觉、北斗导航、物品卡片、浏览搜索、详情展示、最终说明文档 检查潘岳提交的业务流程,记录问题并复查
潘岳 / 小面包 模拟账号、发布校验、本地存储、模拟认证、联系方式、交接状态、玫瑰与测试 通过Fork和PR提交,检查视觉及文档能否复现

实际仓库里也留下了互相检查的记录:导航阶段由小面包提出修改意见,我提交修复;业务阶段由小面包从Fork提交,我审阅;最终文档再交给小面包按说明检查。这不是两份代码最后放进同一个文件夹,而是交接时需要让对方看懂、用起来并提出问题。

⏱️ 第三关:PSP计时——星光不能代替时间记录

PSP2.1 Personal Software Process Stages 预估(人分钟) 实际(人分钟)
Planning 计划(小计) 30 40
Estimate 估计任务所需时间 30 40
Development 开发(小计) 890 1100
Analysis 需求分析,包括学习新技术 90 100
Design Spec 生成设计文档 60 70
Design Review 设计复审 30 40
Coding Standard 制定代码规范 20 20
Design 具体设计 120 150
Coding 具体编码 360 420
Code Review 代码复审 90 120
Test 自测、修复与回归测试 120 180
Reporting 报告(小计) 140 160
Test Report 测试报告 60 70
Size Measurement 计算工作量 20 20
Postmortem & Process Improvement Plan 总结及过程改进计划 60 70
合计 三个阶段小计相加,不重复计入子项 1060 1300

导航调整与合并冲突都提醒我们,计划不能只考虑编码,还应为互相检查、修复和复查留出时间。两人分别在各自电脑上操作时,也需要安排同步代码与确认版本的时间。

🧭 第四关:接通星路,让核心流程走到底

4.1 先守住任务范围

核心需求 当前设计对应
发布寻物、招领 “我丢失了 / 我捡到了”及发布表单
浏览与搜索 首页便签、关键词与组合筛选
查看详情 物品描述、时间、地点、状态及发布者信息
联系发布者 查看联系方式及复制;不做站内聊天
更新状态 发布者确认交接,寻找中→已找回,待认领→已归还

项目使用HTML、CSS和原生JavaScript。index.html提供入口,styles.css管理样式,app.js负责页面渲染和交互。物品、账号及玫瑰等数据通过浏览器本地存储保存,无需部署服务器即可演示。

整体是单页式交互:go()切换页面,render()根据当前页面和选中物品生成内容;点击和表单事件更新状态,再重新渲染。浏览器通过普通脚本加载本地文件,避免把运行网页变成安装依赖的任务。

4.2 关键流程图

【核心流程图】

3850876-20261010011827493-890586823

【数据流图】

3850876-20261010011855772-1061297009

4.3 关键代码:多个筛选条件如何一起生效?

以下取自本地交付版filtered(),仅调整格式;发布前核对与最终仓库版本一致。

function filtered() {
  return items.filter(x =>
    (!filter.keyword || `${x.title} ${x.description}`.toLowerCase()
      .includes(filter.keyword.toLowerCase().trim())) &&
    (!filter.category || x.category === filter.category) &&
    (!filter.area || x.area === filter.area) &&
    (!filter.date || x.date >= filter.date) &&
    (!filter.status || x.status === filter.status)
  );
}

每个条件都采用“没有选择就不限制,选择后必须匹配”的逻辑,条件之间通过&&连接。关键词匹配名称和描述;日期作为起始日期,在数据遵循YYYY-MM-DD格式时可以比较先后。这样“耳机+电子产品+图书馆”不是三个独立按钮,而是在同一批数据上共同缩小范围。

4.4 关键代码:不是谁都能替别人结束信息

交接确认处理中的核心片段如下,省略了弹窗、交接对象校验和保存后的渲染部分。

function isOwner(item) {
  return item.publisher === activeAccount;
}

// confirm-finish 事件处理内
const x = current();
if (!isOwner(x) || !['seeking', 'claimed'].includes(x.status)) return;
// 校验并记录交接对象后更新状态
x.status = x.type === 'lost' ? 'found' : 'returned';

这里检查“当前账号是否为发布者”和“物品是否仍未完成”,既在页面入口做提示,也在事件处理中做检查。课程演示中的前端限制不能替代真实服务端授权,但可以把“谁修改、从哪里修改、修改后变什么”讲清楚。

【测试发布】

3850876-20261010012334948-1327947290

🌌 第五关:附加装备——星光负责指路,玫瑰负责感谢

5.1 多条件筛选与一键复制

意义:当物品变多时,用户不必逐条翻找;核对后可以复制联系方式,减少手动抄写。关键词搜索属于核心要求,多条件筛选和复制属于便捷性扩展。

实现:筛选复用filtered();复制调用浏览器剪贴板接口,失败时提供手动复制提示。这里的重点不只是“复制成功”,也要给权限被拒绝的用户一个出口。

【带筛选条件的结果页】

3850876-20261010012427181-719530999

5.2 四色星星与北斗导航

蓝星寻找中、紫星待认领、黄星已找回、亮橙星已归还,颜色与文字同时出现,避免只靠颜色区分状态。完成状态便签保持静止,未完成便签保留轻微悬停效果。

校园图只作为地点示意与手动选点,不能称作真实地图定位功能。

【四色星星与校园星图】

3850876-20261010013258468-300980341

页面底部的七星导航保留普通页面名,天枢也叫“星图首页”,天权也叫“登记星启”,避免让找物品变成星座考试。星点与连线共用坐标,星名和页面名组合展示。

【页面底部七星导航】

QQ20261010-164607

5.3 赠人玫瑰,把感谢送给正确的人

意义:交接后增加自愿致谢,而不是把送花变成完成流程的强制任务。可以跳过,每次交接最多一朵。

实现:交接时记录拾主和失主;赠送时保存颜色、时间、赠送者和感谢语;星册只展示当前账号收到的玫瑰。颜色从六种颜色中随机产生一次,刷新时读保存结果,不重新随机。

function canThank(x) {
  return ['found', 'returned'].includes(x.status)
    && !x.rose
    && x.finder !== activeAccount
    && accountNames.includes(x.finder)
    && (recipientOf(x) === activeAccount
      || (isOwner(x) && x.type === 'lost'));
}

这个判断依次检查:已经完成交接、还没送过、不是自己收礼、拾主账号有效,以及当前账号确实是有资格致谢的失主。只画一朵玫瑰不难,难点是不能送错人,也不能重复送。

【赠送页面】

3850876-20261010013415700-1442636900

🗂️ 第六关:打开背包,助教怎样运行?

项目根目录/
├── index.html          # Chrome打开的入口
├── styles.css          # 主题、卡片和导航布局
├── app.js              # 页面渲染、状态与业务事件
├── assets/             # 图片、头像、SVG装饰
├── tests/
│   └── pages.test.js   # 当前网页的自动化测试
├── package.json        # 测试命令配置
└── README.md           # 目录和操作说明
  1. 在GitHub主仓库选择main,点击Code → Download ZIP。
  2. 完整解压,用Google Chrome打开根目录index.html。
  3. 无需安装Node.js即可运行网页;不要单独移动HTML而丢掉脚本、样式和图片。
  4. 顶部切换Nova、小面包、星星同学;绑定使用虚构手机号和演示验证码123456。
  5. 认证使用虚构资料,结果页演示审核状态;测试时不要填写真实证件。

项目数据仅保存到当前浏览器,不跨设备同步;清除浏览器数据可能丢失记录。这里的认证、短信和定位均为演示,不提供真正的身份保障。

【最终仓库目录与README运行说明】

3850876-20261010014129496-1510265004

🧪 第七关:给星路设置检查点——单元测试

7.1 工具选择与学习路径

项目采用Node.js内置node:test及node:assert/strict,无需额外安装测试框架。测试通过node:vm加载真实app.js,用轻量DOM和本地存储替身模拟页面环境。

7.2 一份可以照着做的简易教程

  1. 安装Node.js 24.x LTS,重新打开终端,用node -v确认可用。
  2. 在包含package.json和tests的项目根目录打开终端。
  3. 理解测试的三步:准备输入→调用被测行为→用断言比较结果。
  4. 执行:
node --test
  1. 查看tests / pass / fail。有失败时先读测试名称与报错位置,复现并修复,再执行全套测试。

7.3 项目测试代码

下面是现有测试中的组合筛选用例,依赖同文件的setup()环境,不能脱离辅助函数单独复制执行。

test('search combines keyword, area, category and date', () => {
  const t = setup();
  assert.equal(t.run(`
    filter = {keyword:'耳机', area:'图书馆',
              category:'电子产品', date:'2026-10-03'};
    filtered().length
  `), 1);
  assert.equal(t.run(`filter.date='2026-10-04';filtered().length`), 0);
});

它测试filtered():第一组条件匹配示例耳机;把起始日期推进一天后,不再匹配。不是只检查函数能运行,而是比较返回数量是否符合预期。

玫瑰资格测试直接覆盖canThank()的正常与拒绝分支:

test('rose requires completed handover and recipient; duplicate is blocked', () => {
  const t = setup();
  assert.equal(t.run('canThank(items[0])'), false);
  assert.equal(t.run('canThank(items[2])'), true);
  assert.equal(t.run("items[2].rose={message:'谢谢'};canThank(items[2])"), false);
});

7.4 用例设计:不能只让程序走顺路

以下对应本地交付版中已有的测试,当前文件共23项,下面列出其中15项。

编号 被测行为 构造数据/情形 预期
T01 页面渲染 依次进入12个路由 不抛异常,有内容
T02 esc及详情 标题包含HTML标签与事件文本 转义,不直接插入该标签
T03 详情认证分支 未认证与approved分别渲染 未认证隐藏号码,通过后显示
T04 filtered 关键词+类别+地点+日期 正确匹配,日期越界后为空
T05 canThank 未完成交接 不允许赠送
T06 canThank 完成交接且尚未送花 有资格账号可赠送
T07 canThank 已有rose记录 拒绝重复赠送
T08 发布处理 空表单 错误提示,物品数量不变
T09 发布绑定分支 合法草稿、账号未绑定 跳转绑定,草稿标题保留
T10 认证结果 pending、approved、rejected 显示对应状态
T11 makeRose 固定随机源到六个区间 六种颜色可覆盖,时间可解析
T12 玫瑰保存 保存后重渲染并读回 颜色、时间与感谢语一致
T13 账号隔离 三账号轮换 绑定、认证与所属物品独立
T14 confirm-finish 非发布者直接触发事件 状态不改变
T15 核心事件流程 发布→搜索→详情→认证→联系→完成 新物品可找到,最后转为found

白盒设计从条件分支出发:发布校验的失败与成功路径、认证状态分支、所有者与非所有者、完成与未完成、送过与未送过都要考虑。随机颜色用固定随机源分别命中六个分支,而不是靠反复随机碰运气。

7.5 实际结果

本次测试版本SHA(本人提供) Node.js版本 总数 通过 失败
6f5a605 v24.21.0 23 23 0

在Windows PowerShell中执行node --test,本次23项测试全部通过,无失败、取消或跳过的用例,运行耗时约129.85毫秒。

【已提供的23项测试通过截图和Node.js v24.21.0版本截图】

3850876-20261010014430681-444460919

🌿 第八关:GitHub组队轨迹,不只是一键上传

我维护主仓库,小面包通过自己的Fork创建功能分支并提交PR,另一位审阅。解决问题后再合并,下一阶段从同步后的main开始。

截至本次核查,仓库PR列表显示9个PR已合并。项目按10个功能阶段整理,初始化直接提交。

阶段 功能范围 真实提交或PR
01 项目骨架 张佩琪提交4b074c5
02 浅色星空布局与北斗导航 张佩琪PR #1;包含布局与导航修复两次提交
03 校园图与物品便签 张佩琪PR #2
04 搜索和筛选 张佩琪PR #3
05 详情展示 张佩琪PR #4
06 模拟账号与发布校验 潘岳从Fork提交PR #5
07 本地存储、绑定与模拟认证 潘岳从Fork提交PR #6
08 联系方式与交接状态 潘岳从Fork提交PR #7,处理冲突后合并
09 玫瑰感谢与测试 潘岳从Fork提交PR #8
10 README交付说明 张佩琪PR #9,潘岳审阅

在提交历史中,能看到项目初始化、功能提交、修复和合并记录

【主仓库commits记录】

3850876-20261010014924606-55121832

【潘岳Fork页面】

3850876-20261010015946254-391751243

最能说明协作的一段是PR #1:小面包先指出星点与连线顶点偏移、标签分离;随后我提交d0e6c2d修复;她再检查七个入口并批准,最后合并。比“已阅,同意”更有价值的是,审阅确实推动了一次修改。

【PR #1】

3850876-20261010021031132-1016274988

最后的PR #9则把检查对象从代码变成了文档:我提交运行、测试和限制说明,小面包记录按README下载运行后的审阅意见。软件交付不仅要让作者会用,也要让第一次拿到文件的人知道从哪里开始。

提交说明应描述实际改动,例如fix: align dipper stars and group navigation labels,比“修改了一下”更容易回看。功能完成后检查并及时提交。

🛠️ 第九关:Boss战——星点要对齐,分支也要同步

9.1 星星没有站在连线交点上

  • 问题: 北斗导航星点与连线错位,星名和页面名分离,部分文字遮挡。
  • 尝试: 检查按钮尺寸和CSS偏移;将星点容器固定为44×44,以中心定位,连线与按钮共用坐标;把标签组合成一个整体。
  • 处理: 天璇、天玑、开阳文字移到星点下方,其余标签保持在上方。修改已进入本地阶段包。
  • 是否解决: PR #1中保留了修复提交与潘岳复查批准的记录,随后已合并;最终显示效果见导航截图。
  • 收获: 图形和按钮如果各用一套位置规则,界面很容易“看起来差一点”;统一坐标比不断加偏移更容易维护。

9.2 PR已经批准,为什么仍然不能合并?

  • 问题: 第08阶段PR出现README.md与app.js冲突;审阅批准并不会消除冲突。
  • 原因: 这次小面包没有先在自己的Fork中同步主仓库已合并的进度,就继续了下一阶段。新功能与主仓库的修改在旧版本基础上分别推进,并改到了同一段内容,Git无法自动决定保留哪一边。并不是“落后几个提交”就一定冲突,而是这次确实存在重叠修改。
  • 尝试: 区分Current为功能分支、Incoming为main,逐段比较。新增归还页面和联系事件需要保留;详情长行不能只看开头就全部接受。
  • 处理原则: 避免直接Accept both导致旧详情覆盖新详情,或产生重复处理。冲突解决提交后还要重新运行。
  • 是否解决: PR #7中出现07dc52e合并提交,之后补充了“已解决合并冲突问题”的复查意见,最终通过5d79a1d合并到main。
  • 收获: 下一阶段要在前一阶段合并后,同步Fork的main再建分支;同步main不会自动更新提前创建的旧分支。

【PR #7】

3850876-20261010021031132-1016274988

🌹 第十关:队友结算——一起把星星送回家

值得学习的地方:

小面包没有只看“页面能打开”就直接通过。在北斗导航的PR里,她指出了星点偏移和标签分离,修复后又检查入口。这件事让我看到,从使用者角度认真看一遍,确实能发现写代码时容易忽略的问题。最后文档阶段,她又按README检查运行步骤,让交付不只停留在“我电脑上可以”。

需要改进的地方:

我们都需要把分支同步与验证记录做得更及时。联系与交接阶段发生了冲突,PR #8的说明也曾保留待填写的测试结果。这提醒我们:不只是功能完成了就可以交接,分支起点、测试结果和文档都应一起核对。下一次可以约定“同步后建分支,测试后补记录,再请对方审阅”。我也不能只催着把功能合进去,而忽略最后这些看似琐碎的检查。

Nova的本次总结:

这次我们不在同一个地方,没办法随时面对面讨论,也不能直接凑到对方电脑前说“这里再点一下”。PR成了我们异步沟通和交接代码的地方:这次改了什么、哪里需要调整、修复后是否通过,都能留下记录,不必只靠聊天里一句“我改好了”。

我们可以分别完成各自负责的部分,再在各自电脑上下载、检查和迭代。发现问题就在PR里说明,修改后继续复查;即使没有同时在线,也能顺着记录了解对方的进度。对我来说,GitHub不再只是“上传作业的地方”,而是真正方便两个人一起推进项目的工具。

上一次,我更关心“页面能不能连起来”;这一次,我开始追问“数据有没有留下、状态由谁修改、异常时用户往哪里走”。星空配色和玫瑰让拾星有自己的样子,但真正支撑它的,是一条能够走到底、也能够接受检查的流程。

目前的拾星仍是本地课程演示,没有解决真实多用户同步,也不能拿模拟认证当安全承诺。后续若继续完善,应先增加真实试用反馈和更充分的异常测试,再考虑后台与多人数据共享。

⭐ 北斗负责指引方向,我们负责把每一步走清楚。  
PickStar · Retrieve your lost star.  
寻回属于你的星辰,也把这次学到的方法带往下一关。

EXP +1 |解锁技能:原型落地、状态管理、Fork / PR协作、自动化测试与交付说明。

🌟 本次冒险任务,等待最后的截图与验收存档。

愿你抬头有星光,低头有代码。下一张地图,加载中…… 🚀

posted @ 2026-10-10 16:29  NovaVespera  阅读(16)  评论(0)    收藏  举报
愿你抬头有星光,低头有代码 ✨