第1组 · 嵇宇锋 · 第1次评审详情 - sebestp/coursewise GitHub Wiki

嵇宇锋 · 第1次评审详情(第1组 · 组员)

基本信息

  • 报告期次:第1次 | 报告日期:2026-10-08 | 项目组号:第1组 | 项目名称:在线购物系统

工作任务与成果物

  • 已完成工作:完成前端页面与文档:商品详情页、意向提交页、口令码展示页、卖家后台共 5 个页面按接口契约开发并置于 static/(启动实测 HTTP 200、静态资源加载正常);编写《前端页面说明》(页面清单、买卖双方操作流程、空状态、接口对应表),附 8 张运行截图
  • 成果物:前端页面说明

成果物 · 前端页面说明

前端页面说明

负责人:嵇宇锋 | 状态:前端页面已接入 Java 后端(静态资源就位),页面说明与运行截图已产出 成果物:server/src/main/resources/static/ 下 5 个前端文件 + 本说明 + 8 张运行截图。 前端页面按 接口设计 约定的接口契约编写,调用统一使用相对路径,置于 Spring Boot 的 static/ 目录下与后端同源部署,因此无需跨域配置即可联调。


一、页面清单
页面名 文件路径 用途 调用了哪些接口
买家首页 server/src/main/resources/static/index.html + buyer.js + style.css 展示当前在售 / 交易中商品;提交购买意向;凭口令码查询位次、改联系信息、撤销意向 GET /api/product、POST /api/intents、GET /api/intents/lookup/{code}、PUT /api/intents/lookup/{code}、POST /api/intents/lookup/{code}/cancel
卖家后台 server/src/main/resources/static/admin.html + admin.js + style.css 卖家登录;发布商品;查看排队;进入交易;标记结果;下架;历史记录;修改密码 POST /api/seller/login、GET /api/seller/overview、POST /api/seller/products、POST /api/seller/products/{id}/deal、POST /api/seller/products/{id}/deal/result、POST /api/seller/products/{id}/freeze、POST /api/seller/products/{id}/unfreeze、POST /api/seller/products/{id}/offshelf、POST /api/seller/intents/{id}/dispose、GET /api/seller/history、GET /api/seller/history/{id}、POST /api/seller/password
样式表 server/src/main/resources/static/style.css 两个页面共用的全部样式(卡片、按钮、表格、弹窗、徽标、Toast) —

页面访问地址(后端启动后):

地址 页面
http://localhost:8080/ 买家首页(Spring Boot 默认把 index.html 作为欢迎页)
http://localhost:8080/admin.html 卖家后台

说明:买家首页右上角「卖家入口」链接已由 /admin 修正为 /admin.html(唯一一处在搬运时修改的代码);buyer.js、admin.js 中所有请求本来就是相对路径(/api/xxx),未发现写死 localhost:3000 的情况,故未修改。


二、买家流程说明

买家免注册,全程以「口令码」为唯一凭证。

1. 浏览商品
  • 页面元素:首页商品卡(#productCard / #productArea)——图片(或占位图)、商品名、状态徽标(在售中 / 商品交易中)、价格、描述、发布时间、「我要购买」按钮。
  • 行为:页面加载即调用 loadProduct() → GET /api/product,并每 10 秒轮询一次,保证商品被冻结 / 下架后状态及时刷新。
  • 接口:GET /api/product(buyer.js → loadProduct())。

01 买家首页(有商品在售)

2. 提交购买意向
  • 页面元素:点击「我要购买」弹出弹窗 #buyMask,含「姓名」「联系电话」两个必填项(#buyName / #buyPhone)、错误提示区 #buyError、「确认提交,加入排队」按钮(#buySubmit)。
  • 校验:姓名或电话为空时不发请求,直接提示「姓名与联系电话均为必填」。
  • 接口:POST /api/intents(buyer.js → submitIntent()),入参 {name, phone};若商品已被冻结,后端返回 400「商品交易中,暂停接收新意向」,弹窗内显示该错误并刷新商品状态。

02 提交购买意向弹窗

3. 拿到口令码(仅显示这一次)
  • 页面元素:提交成功后关闭意向弹窗,弹出 #codeMask:口令码大字展示(#codeText)、「复制口令码」按钮(#copyCode)、风险提示文案、「我已保存好口令码」按钮(#codeDone)。
  • 行为:口令码只在 POST /api/intents 的响应中返回一次,之后任何接口都不再返回;点「我已保存好口令码」会自动把口令码填入查询框并立即查询。
  • 接口:响应体 {code} 来自 POST /api/intents。

03 提交成功后的口令码展示页

4. 凭口令码查位次 / 改信息 / 撤销
  • 页面元素:「我的排队」卡片中的口令码输入框(#codeInput,自动转大写)+「查询」按钮(#lookupBtn),结果区 #lookupResult:
    • 位次条:你当前排第 N 位;已进入交易时显示 🎉 已进入交易!…;
    • 键值行:商品、提交时间;
    • 可编辑的「姓名」「联系电话」输入框 + 「保存修改」(#saveInfo)+「撤销我的意向」(#cancelIntent)。
  • 接口:
    • 查询:GET /api/intents/lookup/{code}(lookup());
    • 改信息:PUT /api/intents/lookup/{code}(保存后位次不变,renderLookup() 内 #saveInfo 回调);
    • 撤销:POST /api/intents/lookup/{code}/cancel(二次确认后调用,成功后口令码立即失效)。
  • 失败提示:口令码无效 / 已失效 / 已撤销时,结果区显示后端错误原文,并附提示「口令码可能在交易结束后失效,或你已撤销意向」。

04 凭口令码查询到的排队位次


三、卖家流程说明

卖家为系统指定的唯一账号(默认 admin / seller123,首次启动自动创建,无注册)。

1. 登录
  • 页面元素:#loginCard 中「用户名」(#loginUser)、「密码」(#loginPass)、错误提示(#loginError)、「登录」按钮(#loginBtn,密码框回车同样可登录)。
  • 接口:POST /api/seller/login,成功返回 {token, username};token 存 localStorage.seller_token,之后所有卖家接口通过请求头 X-Token 携带;登录会刷新 token(旧 token 立即失效)。token 失效(401)时自动退回登录页。

05 卖家后台登录页

2. 发布商品
  • 页面元素:无在售商品时,「当前商品」页展示发布表单:「商品名称」「商品描述(选填)」「价格(元)」「商品图片(选填,JPG/PNG ≤5MB)」+「发布商品」按钮。
  • 前端校验:名称必填;价格必须为大于 0 的数字;图片仅 JPG/PNG 且 ≤5MB;图片以 FileReader 读成 data:image/...;base64,... 随请求提交。
  • 接口:POST /api/seller/products(publishProduct())。发布后商品状态直接为「在售」,同一时刻仅允许一件在售。
3. 查看排队
  • 页面元素:「当前商品」卡片(状态徽标、商品图片/占位图、名称、价格、描述、发布/冻结时间)+「意向购买人」表格(位次 / 姓名 / 联系电话 / 意向提交时间 / 状态);交易中的买家单独置顶一行并标「交易中」。
  • 行为:enterAdmin() 与 5 秒轮询调用 GET /api/seller/overview;轮询数据无变化时不重渲染,且发布表单正在填写时不会被刷新清空。
  • 接口:GET /api/seller/overview(loadOverview())。

06 后台「当前商品」页(含排队列表)

4. 进入交易 / 冻结 / 下架
  • 页面元素:商品操作按钮随状态变化——在售时「与队首进入交易」(队列为空时禁用)「手动冻结」「手动下架」;手动冻结后为「手动解冻」「手动下架」;交易中显示交易对象信息与「标记交易成功」「标记交易失败」。
  • 接口(均带二次确认):
    • 进入交易:POST /api/seller/products/{id}/deal;
    • 手动冻结:POST /api/seller/products/{id}/freeze;手动解冻:POST /api/seller/products/{id}/unfreeze;
    • 手动下架:POST /api/seller/products/{id}/offshelf。
5. 标记交易结果
  • 页面元素:交易中的商品卡片下方两个按钮——「标记交易成功(下架进历史)」「标记交易失败(自动递补)」,均有不可回退的确认提示。
  • 接口:POST /api/seller/products/{id}/deal/result,入参 {result: 'success' | 'fail'}。
  • 失败后处置:若存在「交易失败的买家」待处理卡片,可对指定意向点「重新排队」或「作废」→ POST /api/seller/intents/{id}/dispose({action: 'requeue' | 'void'}),重新排队沿用原口令码。
6. 查看历史
  • 页面元素:顶部 Tab「当前商品 / 历史记录」;历史表(名称 / 价格 / 发布时间 / 交易时间 / 意向数 /「查看意向」),底部分页(「上一页」「下一页」,每页 10 条);点击行弹出意向明细弹窗 #histMask,列姓名、电话、提交时间与终态徽标(成功 / 已撤销 / 失败)。
  • 接口:GET /api/seller/history?page=n(loadHistory())、GET /api/seller/history/{id}(showHistoryDetail())。

07 后台「历史记录」页

7. 修改密码
  • 页面元素:右上角「修改密码」链接(#changePwdLink)→ 弹窗「原密码」「新密码(不少于 8 位)」「确认新密码」+「确认修改」。
  • 前端校验:三项均必填;新密码不足 8 位、两次输入不一致时在弹窗内提示。
  • 接口:POST /api/seller/password(changePassword())。

08 修改密码弹窗


四、三种空状态的显示方式
空状态 触发条件 前端表现(位置 / 文案)
无商品在售 GET /api/product 返回 product: null(买家端) 买家首页商品卡内居中显示「暂无商品在售」+ 灰色副文案「稍后再来看看吧」,不显示购买按钮(buyer.js → renderProduct(null))。卖家侧同理:无在售商品时「意向购买人」卡片显示「暂无在售商品」,同时「当前商品」卡片自动切换为发布表单。
商品没有图片 商品 imageUrl 为空 不使用 <img>,改用内联 SVG 占位图(灰色圆角方框 + 山峦图标),买家端 90×90、后台 70×70,卡片布局与有图时完全一致(buyer.js / admin.js 中 pic 三元表达式)。
商品描述为空 商品 description 为空串 通过三元表达式整段跳过描述节点,不渲染空的 .desc 元素、也不留空白占位,价格与发布时间之间自然衔接(buyer.js 商品卡、admin.js 当前商品与历史详情)。

补充:卖家后台另有两处空状态——「意向购买人」无记录时显示「暂无买家提交意向」;历史记录为空时「历史商品」卡片显示「暂无历史记录」;历史详情中该商品无意向时显示「该商品无任何意向记录」。


五、与后端接口的对应表
接口路径 方法 前端调用位置(static/ 下文件)
/api/product GET buyer.js → loadProduct()(页面加载 + 每 10 秒轮询)
/api/intents POST buyer.js → submitIntent()(意向弹窗「确认提交,加入排队」)
/api/intents/lookup/{code} GET buyer.js → lookup()(「查询」按钮、口令码输入框回车、口令码弹窗「我已保存好口令码」)
/api/intents/lookup/{code} PUT buyer.js → renderLookup() 内 #saveInfo 点击回调
/api/intents/lookup/{code}/cancel POST buyer.js → renderLookup() 内 #cancelIntent 点击回调
/api/seller/login POST admin.js → login()(「登录」按钮、密码框回车)
/api/seller/password POST admin.js → changePassword()(修改密码弹窗「确认修改」)
/api/seller/overview GET admin.js → loadOverview()(进入后台 + 每 5 秒轮询)
/api/seller/products POST admin.js → publishProduct()(发布表单「发布商品」)
/api/seller/products/{id}/deal POST admin.js → renderOverview() 内 #enterDeal
/api/seller/products/{id}/deal/result POST admin.js → renderOverview() 内 #markSuccess / #markFail
/api/seller/products/{id}/freeze POST admin.js → renderOverview() 内 #freezeBtn
/api/seller/products/{id}/unfreeze POST admin.js → renderOverview() 内 #unfreezeBtn
/api/seller/products/{id}/offshelf POST admin.js → renderOverview() 内 #offshelfBtn
/api/seller/intents/{id}/dispose POST admin.js → renderOverview() 内 [data-requeue] / [data-void] 按钮
/api/seller/history GET admin.js → loadHistory()(「历史记录」Tab、分页按钮)
/api/seller/history/{id} GET admin.js → showHistoryDetail()(历史表行「查看意向」)
/uploads/{file} GET 由商品数据中的 imageUrl 直接作为 <img src> 使用(买家商品卡、后台商品卡)

统一封装:buyer.js / admin.js 各自有一个 api(path, opts) 帮助函数,负责拼 Content-Type: application/json、JSON 序列化请求体、解析响应与统一抛错;卖家侧额外附加 X-Token 请求头。


六、运行截图与说明
编号 截图 对应内容
01 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/01-买家首页.png 买家首页,商品「iPhone 14 128G 星光色 ¥3599.00」在售中,含状态徽标、「我要购买」按钮与「我的排队」查询区
02 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/02-提交购买意向弹窗.png 点击「我要购买」后的意向弹窗,已填写姓名与联系电话
03 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/03-口令码展示页.png 提交成功后的口令码弹窗,含 8 位口令码、「复制口令码」与「仅显示这一次」的保存提示
04 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/04-凭口令码查询排队位次.png 用口令码查询得到的位次页:显示「你当前排第 3 位」、商品与提交时间,以及改信息 / 撤销意向入口
05 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/05-卖家后台登录页.png /admin.html 未登录时的卖家登录页(用户名 + 密码 + 登录按钮)
06 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/06-后台当前商品页.png 登录后「当前商品」页:商品信息 + 操作按钮 +「意向购买人」排队列表(3 位买家按提交时间正序)
07 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/07-后台历史记录页.png 「历史记录」Tab:已下架商品 AirPods Pro 2(¥899.00,意向数 2)与分页控件
08 https://github.com/xych5q/online-shop/raw/main/docs/design/screenshots/08-修改密码弹窗.png 右上角「修改密码」弹窗:原密码、新密码(不少于 8 位)、确认新密码
截图环境说明(重要)
  • 前端文件本身已在 Java 后端上验证:本地打包运行 server 后,http://localhost:8080/ 与 http://localhost:8080/admin.html 均正常返回页面(HTTP 200)。
  • 买家端操作链路(浏览、提交意向、凭码查询/修改/撤销)的正确性由 16 组 JUnit 用例覆盖,另有人工全链路验证记录,见 第1次测试记录。
  • 由于前端调用的是相对路径,同一套页面在 8080(Java 后端)下无需任何改动即可直接工作,已由组长启动实测确认。

教师点评

(评审后填写)

⚠️ **GitHub.com Fallback** ⚠️