接口设计 - xych5q/online-shop GitHub Wiki
编写:王振涛 | 状态:接口清单已完成,Java 版 17 个接口全部实现并通过实测(见 卖家端模块实现、买家端模块实现) 本次迭代成果物:本设计(接口清单与业务规则)。Java 正式实现的接口路径与本文保持一致。
| 分组 | 接口 | 方法 | 鉴权 | 说明 |
|---|---|---|---|---|
| 买家 | /api/product |
GET | 否 | 获取当前在售 / 交易中商品 |
| 买家 | /api/intents |
POST | 否 | 提交购买意向,返回口令码(仅此一次) |
| 买家 | /api/intents/lookup/{code} |
GET | 码 | 查位次与状态 |
| 买家 | /api/intents/lookup/{code} |
PUT | 码 | 改姓名 / 电话 |
| 买家 | /api/intents/lookup/{code}/cancel |
POST | 码 | 撤销意向 |
| 卖家 | /api/seller/login |
POST | 否 | 登录,返回 token |
| 卖家 | /api/seller/password |
POST | token | 修改密码 |
| 卖家 | /api/seller/overview |
GET | token | 后台首页数据 |
| 卖家 | /api/seller/products |
POST | token | 发布商品 |
| 卖家 | /api/seller/products/{id}/freeze |
POST | token | 手动冻结 |
| 卖家 | /api/seller/products/{id}/unfreeze |
POST | token | 手动解冻 |
| 卖家 | /api/seller/products/{id}/deal |
POST | token | 与队首进入交易 |
| 卖家 | /api/seller/products/{id}/deal/result |
POST | token | 标记交易结果 |
| 卖家 | /api/seller/products/{id}/offshelf |
POST | token | 手动下架 |
| 卖家 | /api/seller/intents/{id}/dispose |
POST | token | 处置失败意向(作废 / 重排) |
| 卖家 | /api/seller/history |
GET | token | 历史商品列表(分页) |
| 卖家 | /api/seller/history/{id} |
GET | token | 历史商品详情与意向流水 |
| 静态 | /uploads/{file} |
GET | 否 | 商品图片 |
卖家鉴权:
X-Token请求头或Authorization: Bearer <token>。买家侧以"持有口令码"作为唯一凭证,无账号体系。
出参:{ product: { id, name, description, price, imageUrl, status, publishedAt } | null }
规则:只返回 在售 或 已冻结 的商品;无商品返回 null,前端显示"暂无商品在售"。不返回任何意向或口令码信息。
入参:{ name, phone }(均必填,去空格后非空)
出参:{ code }
规则:
- 商品不存在 → 400「暂无商品在售」
- 商品状态非
在售→ 400「商品交易中,暂停接收新意向」 - 姓名或电话为空 → 400「姓名与联系电话均为必填」
- 生成 8 位大写字母数字口令码(字符集剔除 I/O/0/1 等易混淆字符),全库唯一
- 口令码只在此响应中返回一次,后续任何接口都不再返回
出参:{ intent: { name, phone, status, inTransaction, position, submittedAt }, product }
规则:口令码对应意向已处终态(成功 / 撤销 / 失败)或不存在 → 404「口令码无效或已失效」;已进入交易时 position 为 null、inTransaction 为 true。
入参:{ name, phone } 规则:同名校验同上;修改不改变排队位次。
规则:排队中与交易中均可撤销 → 置为 cancelled(终态,历史留痕);口令码随即失效。
- 排队中撤销:仅队列少一人,后续位次前移;
- 交易中(队首)撤销:交易终止 → 队列仍有排队意向则下一位自动递补进入交易、商品保持冻结,队列为空则商品自动恢复在售;(2026-10-07 拍板 + 2026-10-13 回改代码,见 需求规格说明书 4.2 与 测试用例清单 TC-A17 / TC-A18。早期版本此处为「交易中拒绝撤销」,已废弃。)
入参:{ username, password };失败 401「用户名或密码错误」;成功返回 { token, username } 并刷新 token(旧 token 立即失效)。
入参:{ oldPassword, newPassword };原密码错误 400;新密码 < 8 位 400;成功后 换盐重算哈希。
出参:{ detail: { product, queue[], currentDeal, pendingFailed[] }, canPublish }
规则:queue 按提交时间正序并带 position;currentDeal 与 pendingFailed 均不含口令码;canPublish 为 true 表示当前无在售商品。
入参:{ name, description, price, image? }(image 为 data:image/(jpeg|png);base64,...)
规则:已有在售或交易中商品 → 400「已有商品在售或交易中,需先下架」;名称 ≤100 字、描述 ≤2000 字、价格 > 0 且保留两位小数;图片 JPG/PNG ≤5MB,不传或失败时用占位图。发布成功后商品状态直接为 在售(无独立"上架"动作)。
规则:仅 在售 可手动冻结;置 frozenBy = 'manual'。
规则:仅 frozenBy = 'manual' 可解冻 → 400「交易中的商品不能手动解冻」(交易冻结必须先标记交易结果)。
规则:仅 在售 可发起;取队列首位(按提交时间,卖家不可挑人)置 in_transaction,商品自动置 已冻结 / frozenBy='auto';队列为空 → 400「当前没有排队的买家」。
口径已记录(2026-10-13):需求方第 8 条答复要求「买家提交意向后队首自动进入交易」,但 FR-B1 / FR-B2 规定「商品交易中不接收新意向」——若做成提交即冻结,第二位及之后的买家将无法提交,排队队列无法形成。本组决定:在需求方答复前保留本接口作为触发入口,队列可正常形成,队首进入交易时才冻结商品。该差异已登记 需求问题库 #10 提交澄清,答复到达后立即对齐。详见 需求规格说明书 4.1。
入参:{ result: 'success' | 'fail' }
规则:
-
success:当前意向 →success;队列剩余全部 →failed;商品 →已下架进入历史;所有口令码失效 -
fail:当前意向 →failed(待处置);队列有人 → 下一位自动递补进入交易(商品保持冻结);队列空 → 商品自动恢复在售
规则:交易冻结中的商品须先标记结果 → 400;手动下架时队列剩余意向转 failed,商品进入历史。
待需求方确认:手动下架时队列意向的处置方式,见需求问题库第 2 条。
入参:{ action: 'void' | 'requeue' }
规则:仅处置 failed 且未处置过的意向;requeue 生成新排队记录,沿用原口令码、submittedAt 刷新为当前时间(排到队尾)。
待需求方确认:重新排队由卖家发起还是买家自助,见需求问题库第 9 条。
出参:{ page, totalPages, total, items[] } 规则:仅含 已下架 商品,按成交时间倒序,每页 10 条。
出参:{ product, intents[] } 规则:意向流水按提交时间正序,不含口令码;只读,无修改 / 删除接口。
Product:id, name, description, price, image, status(on_sale|frozen|off_shelf), createdAt, frozenAt, frozenBy(manual|auto|null), closedAt
Intent:id, productId, name, phone, code, submittedAt, status(queued|in_transaction|success|cancelled|failed), disposed, requeuedFrom?
Seller:username, salt, hash, token
状态机:在售 →(手动冻结/进入交易)→ 已冻结 →(标记成功)→ 已下架;已冻结 →(标记失败且队列空)→ 在售;在售 / 已冻结(手动)→(手动下架)→ 已下架。
意向终态:成功 / 撤销 / 失败(三档)。
| 关注点 | 实现选择 |
|---|---|
| 接口层 | Spring Boot @RestController,共 17 个 REST 接口 |
| 持久化 | H2 关系库 + Spring Data JPA(表结构同第四节) |
| 密码存储 | Spring Security BCryptPasswordEncoder
|
| 单元测试 | JUnit 5 + @SpringBootTest / MockMvc(18 组全绿,2026-10-13) |
| 接口冒烟 |
test-cases/api-smoke.sh(curl,44 项断言全通过) |
| 端到端测试 | Selenium(迭代 2 补齐);UI 用例见 测试用例清单 第三节 TC-U01~U08 |
| 构建与运行 |
mvn package + java -jar
|
| 容器化部署 | Dockerfile(JDK/JRE 21 两阶段)+ docker-compose + deploy/docker-verify.sh;待在装了 Docker 的机器上完成实跑(见 构建与部署手册 第五节) |
接口路径、入参出参、业务规则与第三节清单保持一致,均在 Java 侧实现。