2026秋软件工程结对作业(第二次之程序实现)

校园失物招领 —— 结对作业 2(程序实现)


1. 开头信息:队员信息与仓库地址

表格

项目 内容
这个作业属于哪个课程 软件工程
这个作业要求在哪里 结对作业 2
这个作业的目标 完成一个校园失物招领程序
仓库地址 https://github.com/ACAct/102402120-102402119
本人 学号:102402119 姓名:陈封
结对同学 学号:102402120 姓名:陈睿涛

呈现形式:PC 端 Web 网页。原生 HTML + CSS + JavaScript,零依赖、零构建,用谷歌浏览器打开根目录 index.html 即可运行。


2. 具体分工

分工依据来自仓库的真实 commit 记录(可执行 git shortlog -sne 复核):ACAct 13 次提交,cf919 4 次提交 + 1 个 Pull Request。

表格

成员 承担内容 对应提交
cf919(本人) Fork 仓库、创建功能分支 feat/publish‑flow,基于主干已有的数据模型、公共组件开展开发 —
发布页完整表单实现、寻物 / 招领切换逻辑、字段级表单校验、表单状态保留 4dfddcd
发布成功页面、我的发布页面开发;实现状态标记 / 撤销标记业务逻辑;本地身份识别演示模式 f24715f
编写发布流程整套冒烟测试,覆盖表单校验、提交入库、状态流转、空状态等场景 6cede20
完善项目 README 文档,编写目录结构、运行指引、操作走查表 662b0bb
提交 Pull Request #1,整理改动清单,等待主干复审合并 PR #1
ACAct(结对队友) 归档第一次作业原型,搭建项目基础目录、公共框架、全局组件、路由体系 caf45df、027cc85
数据模型 Item、仓储 repository、种子演示数据,完成核心单元测试 283eb17
首页、搜索页、详情页业务实现与对应单元、冒烟测试 4eac818、6019208、1ef6232
PR 复审合并,定位并修复合并引入的样式丢失缺陷,完善样式完整性自动化检查 57482d1、511ef5f

协作方式:队友搭建主干框架、数据层、首页 / 搜索 / 详情页面,把公共组件、数据访问接口全部封装完毕。我 Fork 主干,新建独立功能分支,专注开发发布全链路模块:发布页表单、发布成功页、我的发布状态管理;写完业务代码之后同步编写对应冒烟测试,最后提交 PR;队友做代码复审合并,合并后暴露出样式冲突缺陷,双方共同定位问题修复主干。

结对编程的收获:

  • 前置接口约定,降低并行开发冲突。开发前约定好数据模型、组件调用方式、路由常量,我不需要修改底层 core/data,只调用对外暴露的 API,实现两人并行开发,减少代码互相覆盖。
  • 功能分支 + PR 模式,变更可控。整块业务放在独立分支开发,PR 可以清晰看到全部改动文件,测试用例也一并附带,复审者可以完整评审逻辑、测试、文档,比直接向主干 push 更加安全。
  • 缺陷复现优先于主观描述。合并后出现样式丢失问题,一开始只描述 “页面样式没了” 很难定位,借助 git diff 比对文件差异,精准定位 css 合并冲突,极大提升排错效率。
  • 测试跟随业务同步开发,不是写完功能再补测试。写发布表单业务逻辑的时候同步写冒烟用例,每一个交互逻辑对应一条断言,改动业务可以快速验证是否破坏原有逻辑。

3. PSP 表格

表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 25
Estimate 估计这个任务需要多少时间 15 15
Development 开发 750 845
Analysis 需求分析(包括学习新技术) 60 75
Design Spec 生成设计文档 45 55
Design Review 设计复审 20 20
Coding Standard 代码规范 20 15
Design 具体设计 90 110
Coding 具体编码 330 400
Code Review 代码复审 40 60
Test 测试(自我测试,修改代码,提交修改) 90 110
Reporting 报告 160 190
Test Report 测试报告 40 45
Size Measurement 计算工作量 20 20
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 45 55
合计 1030 1155

4. 解题思路描述与设计实现说明

4.1 代码实现思路,文字描述

技术选型

继承主干确定的技术方案:原生 HTML+CSS+JavaScript,零依赖零构建,双击 index.html 直接 Chrome 打开运行。

  • UMD 模块包装 JS 脚本,一份代码同时支持浏览器挂载window.LF全局对象、Node 环境 require 导入用于单元冒烟测试。
  • 页面跳转全部使用原生<a href>,兼容file://本地文件协议,不使用 history API。
  • 存储层全部调用repository对外 API,不直接操作 localStorage,自动适配存储不可用降级为内存。

分工模块分层理解

主干已经完成:core通用工具函数、schema常量枚举、data数据模型与仓储、components通用 UI 组件(图标、布局、卡片);
我负责实现的页面层(pages):

  1. publish.js 发布页:表单 UI 渲染、寻物 / 招领切换、表单内存状态维护、字段级校验、提交调用item.create+repository.add完成新建信息;切换类型保留用户已经填写表单内容;提交失败定位到首个错误字段;记录发布者称呼用于我的发布身份识别。
  2. success.js 发布成功页:读取 url 参数 id,渲染发布信息摘要,提供三个跳转入口;id 不存在优雅降级。
  3. myposts.js 我的发布页:读取本地存储的发布者名字筛选本人发布记录;无本地记录进入演示模式展示全部数据;实现标记已找到 / 已归还、撤销标记;调用repository.updateStatus完成状态更新;支持类型筛选、空状态引导发布。

页面控制器尽量薄,业务校验、数据操作全部复用 core/data 层提供函数,不在页面内部重复写校验逻辑。页面只负责:渲染 DOM、绑定事件、收集表单输入、调用底层 API、处理页面跳转。

三个关键设计决策

① 表单内存状态与 DOM 分离,切换类型保留填写内容
如果直接操作 DOM 取值,切换寻物 / 招领卡片会造成输入丢失。维护一份内存 state 对象,所有表单修改同步更新 state;切换类型只修改 state.type,重渲染页面,state 其他字段保留不变,不会清空用户填写。

var state = {
    type: schema.TYPE.LOST,
    title: '',
    description: '',
    // 其他表单字段
};

② 我的发布不实现登录,使用本地存储发布者称呼做身份识别
作业不要求登录注册,使用发布表单提交的时候存入lf.myPublisher.v1本地存储的发布者名字。

  • 有记录:过滤列表,只展示 publisher.name 匹配本人称呼的数据。
  • 无记录:演示模式展示全部种子数据,页面给出明确文字提示,告知用户当前是演示。

③ 表单校验复用 item 模型 validate,不重复手写校验规则
不在页面写一堆 if 判断做校验,直接调用itemModel.validate()复用底层已经写好的字段约束。页面只负责把返回的 errors 集合渲染到界面。好处:底层修改字段长度、约束规则,发布页面不需要同步修改代码,避免两套校验逻辑不一致。

4.2 关键实现的流程图 / 数据流图

图 1:发布模块完整业务流程

flowchart TD A[首页点击我要寻物/招领跳转publish.html] --> B[初始化state,读取url预设type,时间默认填充当前时间] B --> C[用户切换寻物/招领卡片] C --> D[state.type更新,重渲染页面,保留其他表单state] D --> E[用户填写各个表单输入,输入同步写入state] E --> F{用户点击提交按钮} F --> G[调用itemModel.validate做字段校验] G -->|校验失败| H[渲染字段错误提示,滚动聚焦第一个错误字段] H --> E G -->|校验通过| I[调用itemModel.create生成完整Item对象,生成唯一ID] I --> J[repository.add写入数据层(内存+可选localStorage持久化)] J --> K[本地存储保存发布者称呼lf.myPublisher.v1] K --> L[页面跳转success.html?id=xxx] L --> M[发布成功页读取id,渲染发布信息摘要,提供跳转详情/我的发布/首页]

图 2:我的发布状态变更流程

flowchart TD A[进入myposts.html] --> B[读取本地存储lf.myPublisher.v1] B --> C{是否存在身份记录} C -->|存在| D[筛选repository.list,只匹配publisher.name等于本地记录] C -->|不存在| E[演示模式,获取全部信息,开启演示提示] D & E --> F[渲染统计概览、分段筛选、信息卡片] F --> G{用户操作卡片按钮} G -->|标记已找到/已归还| H[repository.updateStatus设置resolved=true,写入resolvedAt时间戳] G -->|撤销标记| I[repository.updateStatus设置resolved=false,清空resolvedAt] H & I --> J[重新渲染页面,状态徽章同步更新] J --> K[首页、搜索、详情读取repository自动拿到最新状态]

图 3:发布表单数据流

flowchart LR U[用户页面输入] --> P[publish.js页面state内存对象] P -->|提交| V[item.validate校验] V -->|合法| C[item.create创建Item] C --> R[repository.add] R --> M[内存缓存] M --> LS[localStorage 可选持久化,失败退回内存] R -->|对外输出| P1[首页渲染] R -->|对外输出| P2[搜索检索] R -->|对外输出| P3[详情页查看] R -->|对外输出| P4[我的发布列表]

4.3 贴出重要的代码片段并解释

片段一:发布页切换类型保留表单状态

function setType(type) {
    if (type !== schema.TYPE.LOST && type !== schema.TYPE.FOUND) return;
    if (state.type === type) return;
    syncFromDom(); // 先把当前DOM输入同步到内存state
    state.type = type;
    errors = {}; // 切换类型清空旧错误提示
    render(); // 重渲染页面,state其他字段不会被清空
}

解释:syncFromDom()读取 DOM 当前输入同步到内存 state;修改 type 之后直接重渲染页面,title、location 等用户已经填写内容保存在 state 对象中不会丢失,仅仅修改标签文案、占位符、提交按钮文字,解决切换类型表单内容清空的糟糕体验。

片段二:发布页表单校验复用底层 item 模型,页面只负责渲染错误

function validateForm() {
    var result = itemModel.validate({
      type: state.type,
      title: state.title,
      description: state.description,
      category: state.category,
      location: state.location,
      lostAt: state.lostAt,
      contactType: state.contactType,
      contactValue: state.contactValue
    }, { now: Date.now() });
    errors = {};
    Object.keys(result.errors).forEach(function (key) { errors[key] = result.errors[key]; });
    // 补充表单独有的发布者称呼校验
    if (!util.normalize(state.publisherName)) {
      errors.publisherName = '请填写你的称呼,方便同学联系你';
    }
    return Object.keys(errors).length === 0;
}

解释:表单校验不自己写一大堆 if 条件,复用itemModel.validate(),底层 schema 定义的全部约束(长度、时间不能为未来、枚举合法)全部复用。页面仅补充发布者称呼这个表单层特有校验,拿到 errors 集合直接渲染到各个表单字段。后续修改字段限制只需要修改 data 层,不需要修改发布页代码。

片段三:我的发布身份识别逻辑,区分正常模式与演示模式

function myItems() {
    var all = repository.list({ type: state.type });
    var name = state.publisherName;
    if (!name) {
      state.demoMode = true;
      return all; // 无身份记录,演示模式返回全部信息
    }
    state.demoMode = false;
    var mine = all.filter(function (item) {
      return item.publisher.name === name;
    });
    return mine;
}

解释:没有登录系统,读取浏览器本地存储的发布者名字;没有记录打开页面直接展示全部种子数据,同时页面展示demo‑tip提示文字告知用户当前是演示模式;当用户真正发布一条信息写入本地称呼之后,自动切换正常模式只展示本人发布记录。

片段四:提交发布成功,记录发布者身份用于我的发布识别

repository.add(created.item);
// 将本次发布者称呼存入本地存储,作为我的发布识别依据
util.storage.set('lf.myPublisher.v1', created.item.publisher.name);
layout.go('success', 'id=' + encodeURIComponent(created.item.id));

解释:发布成功之后,把填写的发布者称呼存入 storage;进入我的发布页面读取这个 key 的值做数据过滤,实现 “只看我发布” 的效果。

片段五:冒烟测试,模拟发布完整流程

fill(h, {
    type: 'found',
    title: '捡到一只黑色保温杯',
    description: '杯身有划痕,贴着星星贴纸',
    category: 'bottle',
    location: '三食堂二楼',
    locationDetail: '二楼服务台',
    lostAt: '2026-09-25T12:30',
    contactType: 'wechat',
    contactValue: 'finder_wx',
    publisherName: '测试同学',
    publisherDept: '计算机学院'
});
page.submit();
assert.strictEqual(repo.list().length, before + 1, '应新增 1 条信息');
assert.strictEqual(calls.length, 1, '应发生一次跳转');
assert.strictEqual(calls[0].name, 'success');

解释:在 dom‑harness 测试桩环境,模拟完整填写表单、提交,断言仓储新增数据,跳转 success 成功页,在无浏览器环境验证发布全链路流程。


5. 附加特点设计与展示(本人负责模块相关)

5.1 特点一:发布页切换寻物 / 招领,保留用户已经填写表单内容

设计的创意独到之处与意义
用户填了一半表单,切换 “寻物 / 招领”,如果直接清空全部输入,会严重打击用户体验。很多同类表单切换类型直接清空所有输入,需要用户重新填写。这里做到切换类型,只修改标签文案、占位提示文字,物品名称、地点、描述、联系方式等用户输入全部保留,用户只需要修改少量不匹配内容。

实现思路
表单数据和 DOM 解耦,全部维护在页面内存state对象。切换类型之前,调用syncFromDom()把当前 DOM 输入同步到内存 state,仅仅修改 state.type,清空旧的校验错误,然后调用 render 重渲染页面。内存 state 保存用户填写的其他字段,渲染的时候回写到 input value,实现内容保留。

代码片段

/** 从DOM读取全部表单值写入state(重绘前调用,避免丢输入) */
function syncFromDom() {
  var fields = document.querySelectorAll('[data-field]');
  Array.prototype.forEach.call(fields, function (el) {
    var key = el.getAttribute('data-field');
    if (key && Object.prototype.hasOwnProperty.call(state, key)) {
      state[key] = el.value;
    }
  });
}
function setType(type) {
  if (type !== schema.TYPE.LOST && type !== schema.TYPE.FOUND) return;
  if (state.type === type) return;
  syncFromDom();
  state.type = type;
  errors = {};
  render();
}

实现成果说明

  • 用户填写物品名称、地点、联系方式,切换寻物 / 招领卡片,已填写内容不会丢失;
  • 仅动态切换:丢失 / 拾取时间、丢失 / 拾取地点标签、输入框 placeholder、底部提交按钮文案;
  • 切换同时清空之前表单校验错误提示,避免旧错误残留。

实现结果展示

1

2

5.2 特点二:表单字段级校验,提交失败自动滚动并聚焦第一个错误项

设计的创意独到之处与意义
表单提交全部不合法的时候,不能只是弹一句 “表单有误”,用户不知道哪里填错。需要每一个字段展示对应的错误文字;并且自动滚动视图定位到第一个出错输入框,光标聚焦,直接引导用户修改第一项错误,减少用户查找错误的成本。

实现思路

  1. 提交表单调用 validateForm,得到 errors 对象;
  2. render 页面,每个表单字段读取 errors,渲染.has‑error样式和field‑error错误提示;
  3. 获取 errors 第一个 key,用document.querySelector拿到对应data‑fieldDOM 元素;执行scrollIntoView平滑滚动,并且调用 focus 聚焦输入框。

代码片段

/** 把首个出错的字段滚动到可视区并聚焦 */
function focusFirstError() {
  var names = Object.keys(errors);
  if (!names.length) return;
  var el = document.querySelector('[data-field="' + names[0] + '"]');
  if (!el) return;
  if (el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
  if (el.focus) el.focus();
}

实现成果说明

全部留空直接提交发布,页面逐项标红报错,页面平滑滚动到第一个必填项(物品名称输入框),输入框自动获得焦点。

实现结果展示

3


6. 目录说明和使用说明

项目完整目录同仓库 README,本人主要负责开发文件清单

102402120‑102402119/
├── publish.html           发布页(本人开发)
├── success.html           发布成功页(本人开发)
├── myposts.html           我的发布页面(本人开发)
│
├── js/pages/
│   ├── publish.js         发布页控制器:表单、校验、提交(本人开发)
│   ├── success.js         发布成功页控制器(本人开发)
│   └── myposts.js         我的发布控制器:身份识别、状态标记撤销(本人开发)
│
├── tests/
│   ├── smoke‑publish.js   发布页冒烟测试(本人编写)
│   ├── smoke‑myposts.js   成功页、我的发布冒烟测试(本人编写)
│
├── README.md             项目使用文档(本人补充完善)

测试人员如何运行

环境:谷歌 Chrome 浏览器,无需 Node、npm,双击 index.html 直接打开

  1. git clone 或者下载 zip 解压整个仓库;
  2. 双击index.html打开首页;
  3. 核心操作走查表(本人负责模块相关)

表格

步骤 操作 预期结果
1 首页点击我要寻物,填写部分表单,切换 “我要招领” 已填写表单保留,标签文字切换
2 发布页全部留空直接提交 逐项标红报错,滚动聚焦第一个错误,不会新增数据
3 填写完整表单提交 跳转发布成功页,展示发布信息摘要卡片
4 发布成功页点击前往我的发布 进入我的发布,看到刚刚发布的条目
5 我的发布点击标记已找到 卡片状态更新,统计数字同步变化,按钮变成撤销标记
6 点击撤销标记 恢复为寻找中
7 清除浏览器本地存储打开我的发布 展示全部演示数据,顶部演示模式提示
8 直接访问 success.html 不带 id 不白屏,显示发布成功通用提示

边界说明:
本项目纯前端演示,没有后端登录;我的发布依靠浏览器本地存储记录发布称呼;file:// 协议 localStorage 不可用时数据刷新重置。


7. 单元冒烟测试(本人负责模块部分)

7.1 测试工具

复用项目统一方案,Node 内置node:test+node:assert,搭配dom‑harness.jsDOM 桩,不启动真实浏览器,在 Node 环境模拟页面初始化、DOM 渲染。
本人编写:smoke‑publish.js、smoke‑myposts.js两套冒烟测试,覆盖发布全链路。

7.2 代表性冒烟测试代码片段

用例 1:发布页切换类型,保留已经填写的输入

/* ---- 6. 类型切换保留已填内容 ---- */
{
  const h = buildPage('');
  const page = h.global.LF.pages.publish;
  fill(h, { title: '已填写的名称', location: '已填写的地点' });
  page.setType('found');
  assert.strictEqual(page.state.title, '已填写的名称', '切换类型不应丢失名称');
  assert.strictEqual(page.state.location, '已填写的地点', '切换类型不应丢失地点');
  assert.strictEqual(page.state.type, 'found');
  console.log('类型切换        -> 保留输入 + 拒绝非法值 OK');
}

用例 2:发布页表单全部为空提交,校验失败,不写入仓储

/* ----3. 必填校验:全空提交应逐项报错且不入库 ---- */
{
  const h = buildPage('');
  const page = h.global.LF.pages.publish;
  const repo = h.global.LF.repository;
  const before = repo.list().length;
  page.state.title = '';
  page.state.location = '';
  page.state.contactValue = '';
  page.state.publisherName = '';
  page.state.lostAt = '';
  page.submit();
  const errs = page.errors();
  assert.ok(errs.title, '应提示物品名称必填');
  assert.ok(errs.location, '应提示地点必填');
  assert.ok(errs.contactValue, '应提示联系方式必填');
  assert.ok(errs.publisherName, '应提示称呼必填');
  assert.ok(errs.lostAt, '应提示时间必填');
  assert.strictEqual(repo.list().length, before, '校验失败不应写入数据');
  console.log('必填校验        -> 逐项报错且不入库 OK');
}

用例 3:我的发布状态闭环:标记已解决,撤销标记

/* ----5. 状态维护闭环:标记已解决 → 状态与统计同步 → 撤销 ---- */
{
  const h = buildPage('');
  const repo = h.global.LF.repository;
  const page = h.global.LF.pages.myposts;
  const target = repo.list().find((i) => !i.resolved && i.type === 'lost');
  page.toggleStatus(target.id, true);
  const after = repo.get(target.id);
  assert.strictEqual(after.resolved, true, '应已标记为已解决');
  assert.strictEqual(after.status, 'found', '寻物信息应变为已找到');
  assert.ok(after.resolvedAt, '应记录解决时间');
  page.toggleStatus(target.id, false);
  const reverted = repo.get(target.id);
  assert.strictEqual(reverted.resolved, false, '应已撤销标记');
  assert.strictEqual(reverted.status, 'ongoing', '应回到寻找中');
  assert.strictEqual(reverted.resolvedAt, null, '撤销后清空解决时间');
  console.log('状态维护闭环    -> 标记/撤销 + 统计同步 OK');
}

用例 4:我的发布无身份记录,进入演示模式

/* ----3. 无身份记录:演示模式展示全部信息并给出说明 ---- */
{
  const h = buildPage('');
  h.global.LF.pages.myposts.init();
  const html = h.document.getElementById('content').innerHTML;
  assert.ok(html.includes('本地还没有你的发布记录'), '应说明处于演示模式');
  console.log('我的发布(演示)  -> 全部信息 + 说明 OK');
}

7.3 测试设计思路与边界考虑

针对发布链路,重点覆盖等价类、边界、异常场景:

  1. 正常流程:完整填写表单提交,入库,跳转成功页;
  2. 表单边界:字段最大长度、时间不能选择未来;切换类型表单保留;
  3. 异常表单:全部为空提交;部分字段空白;未来时间提交;非法枚举;
  4. 我的发布边界:无本地身份演示模式;有身份但是零发布的空状态;标记、撤销状态闭环;操作不存在 id 不抛出异常;
  5. 页面降级:success 页面不带 id 参数,不会白屏;
  6. DOM 桩环境模拟,不依赖浏览器,运行node tests/run‑all.js即可执行冒烟。

局限:DOM‑harness 只是简易 DOM 桩,不会执行真实浏览器布局,剪贴板、真实交互需要人工浏览器验证。自动化测试保证业务逻辑、渲染结构正确。


8. GitHub 的代码签入记录截图

Screenshot 2026-10-08 193801

命令行等价输出(截至最新提交):

$ git log --oneline -13
511ef5f fix: 修复合并后丢失的页面样式与冒烟测试调度
57482d1 Merge pull request #1 from cf919/feat/publish-flow
6cede20 test: 新增发布流程冒烟测试用例
f24715f feat(flow): 实现发布成功页与我的发布页面
4dfddcd feat(publish): 实现发布页表单、字段级校验与提交入库
662b0bb docs: 补充 README 的目录说明与使用说明
1ef6232 feat(detail): 实现信息详情页与联系方式、状态处理
6019208 feat(search): 实现搜索页与关键词检索、相关性排序
4eac818 feat(home): 实现首页信息流、类型筛选与排序
283eb17 feat(data): 设计数据结构与数据访问层,并补充单元测试
027cc85 feat(structure): 搭建项目目录结构与公共框架
caf45df chore: 归档第一次结对作业的6页HTML原型
5235606 Merge branch 'main' of https://github.com/ACAct/102402120-102402119

$ git shortlog -sne
    13  ACAct <13505079463@163.com>
     4  cf919 <3468918156@qq.com>

commit 信息是如何合理记录的:

  1. 按功能划分,每完成一个可运行的功能提交一次,而不是攒到最后一次性提交。提交信息采用 type(scope): 摘要 格式(feat / fix / docs / chore / test)。
  2. 提交正文说明「为什么」而不只是「做了什么」。例如数据层那次提交在正文里写明「status 一律由 type + resolved 派生,杜绝『寻物 + 已归还』这类矛盾状态」,让复审者能直接看懂设计意图,不必反推代码。
  3. 修复类提交说明问题现象与影响范围。例如 511ef5f 的正文写明了「发布页、我的发布、发布成功页的样式在合并后全部丢失,三个页面渲染出来是没有任何样式的裸 HTML」,便于日后追溯。

9. 遇到的代码模块异常或结对困难及解决方法

问题一:切换寻物招领卡片,填写的表单输入全部丢失

  • 问题描述:一开始切换类型卡片,DOM 重渲染,用户已经输入全部清空。一开始只操作 DOM,每次 setType 之后直接清空重渲染 HTML,内存没有保存表单数据。
  • 尝试:一开始尝试只修改标签文字,不重渲染整个表单;但是标签、placeholder、提交按钮分散多处,改动复杂,容易遗漏。
  • 解决:把全部表单状态抽离到 JS 内存 state 对象;每次重渲染之前调用syncFromDom()把 DOM 输入同步到 state,渲染的时候再把 state 写回 input value。切换 type 仅仅修改 state.type,其他字段保留。
  • 收获:复杂表单,DOM 只做渲染,业务状态保存在 JS 内存对象,避免完全依赖 DOM 取值,是保留输入的关键。

问题二:表单校验写完,但是底层 item 模型校验和页面表单校验两套规则不一致

  • 问题描述:一开始我自己在页面写一堆 if 判断做表单校验,后来发现和item.js底层 validate 的约束出现冲突,比如最大长度一个改了另一个没改。
  • 尝试:把页面手写的 if 校验全部删掉,页面表单收集完输入,直接调用itemModel.validate()复用底层校验规则,页面只补充发布者称呼这个表单特有校验。
  • 解决:表单校验全部复用 data 层模型,页面只负责展示错误;后续修改字段约束,只需要修改一处,避免两套规则不一致。
  • 收获:业务校验逻辑下沉到领域模型,页面控制器尽量薄,不要重复实现业务规则。

结对协作上的困难

  • 接口约定的问题:两人分别实现不同页面,容易出现「我封装的函数对方不知道怎么用」。应对方式是把公共部件(状态栏、底部导航、卡片、路由表)全部收敛到 js/components/,页面只调用不重写;数据访问统一走 repository,页面不直接读写存储。
  • 合并时机的选择:队友的分支存在时间较长,导致样式文件与主干长期分叉。教训是公共文件(尤其是样式)的改动应尽早合入主干,或者开工前先同步一次主干。
  • 问题复现比描述更有效:样式丢失问题如果只说「页面变丑了」,很难定位;先在本地用 git diff 定位到具体文件与行数差异,沟通效率完全不同。

10. 评价队友

值得学习的地方

  • 队友搭建了完整底层框架、数据模型、公共组件、首页搜索详情页面,设计分层架构 core/data/components/pages,做好接口封装,让我可以专注开发发布模块,不用从零搭建底层。
  • 设计非常多健壮性处理:数据 normalize 降级处理脏数据、派生 status 避免矛盾状态、存储自动降级、DOM‑harness 测试桩,工程意识很强。
  • PR 合并后出现样式冲突,快速定位 git diff,新增样式完整性检查补齐自动化测试,快速修复主干缺陷。

需要改进的地方

  • 开发过程中部分接口文档只有代码没有单独文档,需要读源码看懂 API;后续可以补充简单的对外接口注释,降低阅读框架成本。

附:本人交付模块规模

表格

项目 数量
负责页面 publish.html、success.html、myposts.html,共 3 个页面
JS 页面控制器 publish.js、success.js、myposts.js
冒烟测试脚本 smoke‑publish.js、smoke‑myposts.js
文档 README.md 目录与运行指引补充
对应自动化断言 发布 + 成功 + 我的发布合计80 + 处冒烟断言
运行依赖 0,Chrome 浏览器直接打开
posted @ 2026-10-09 11:12  罗cf  阅读(13)  评论(0)    收藏  举报