Skip to content

项目 A:需求分析与范围定义 ​

S5-001必学进阶约 60 分钟JDK 21项目实战 · 项目 A:测试管理平台

1. 本节定位 ​

从本节起进入 S5 项目实战。前面 161 节学的是"知识点",这一阶段要解决另一件事:

text
把它们串起来,做出一个能跑、能测、能部署、能讲清楚的东西 ✓

★ 项目 A 是一个真实的、可运行的图书借阅管理系统 —— 代码在 labs/project-a,30 个测试全部通过 ✓(含并发测试)。

★ 本节做第一件事:把需求说清楚,并画出范围边界 ✓✓

2. 学习目标 ​

  • 能把一段模糊的话,拆成可验收的需求 ★★★;
  • 能写出用例(谁、做什么、得到什么) ★★★;
  • 能明确"做什么"与"不做什么"(范围边界) ★★★;
  • 知道业务规则要抽成可配置的,而不是写死 ★★★;
  • 能写出可测试的验收标准 ★★★。

3. 前置知识检查 ​

4. 为什么需要这个知识 ​

先看一段真实的需求 ✗:

"我们要一个图书馆系统,能借书还书就行。"

★ 这句话没法直接开工 ✗✗ —— 因为它没说清:

  • 谁在借?读者(有卡号、有状态)
  • 一本书只剩 1 本时谁来借?
  • 一个读者能借几本?借多久?
  • 超期了怎么办?能续借吗?续几次?
  • 重复借同一本允许吗?

★ 需求分析的产出,就是把这些问题变成可执行的决定 ✓✓

5. 核心概念 ​

5.1 需求 → 用例(谁、做什么、得到什么)★★★ ​

★ 项目 A 的用例清单 ✓:

角色用例结果
读者 ★★★借书库存 -1,生成借阅记录(带应还日)
读者 ★★★还书库存 +1,记录标记为已归还
读者 ★★续借应还日延长,续借次数 +1
读者 / 管理员 ★★查图书(关键字 + 分页)返回分页列表
读者 ★★查自己的借阅记录含是否超期
管理员 ★★入库图书新增书目或增加副本
管理员 ★★看超期清单列出所有超期未还

★ 用例的写法 ✓✓:

"作为 <角色>,我要 <做什么>,以便 <得到什么价值>"

例:"作为读者,我要续借,以便在不方便到馆时延长借期" ✓

5.2 业务规则:本项目的六条 ★★★ ​

★ 这六条是需求分析的真正产出 ✓✓ —— 它们决定了数据库怎么设计、接口怎么写、测试怎么测:

#规则为什么重要
①一个读者最多同时借 3 本防止少数人借空库存
②借期 30 天,到期日为借出日 + 30决定 due_at 怎么算
③最多续借 1 次,每次延长 15 天防止无限续借
④已超期不能续借否则超期可以无限延后 ✗
⑤同一本书未还不能重复借防止一个人占多本同名书
⑥读者停用后不能借(R003 就是停用)挂失/违规的处理

★★ 关键设计:规则要可配置 ✓✓

yaml
library:
  rules:
    max-borrow-per-reader: 3
    borrow-days: 30
    max-renew-count: 1
    renew-days: 15

如果写死成 if (count > 3),业务方改一次就要改代码、测试、发版 ✗

抽成配置后,改配置就能生效 ✓✓

代码里对应 LibraryRules(@ConfigurationProperties)✓

★ 而且测试要断言"规则真的来自配置" ✓(见 《项目 A:测试方案与分层测试》)

5.3 范围边界:做什么、不做什么 ★★★ ​

★ 这是需求分析里最容易被跳过、也最值钱的一步 ✓✓:

内容
✅ 本期做图书 CRUD(含入库/加副本)、查询(关键字+分页)、借/还/续借、借阅历史、超期清单
❌ 本期不做登录与权限、罚金计算、预约、多分馆、图书推荐、消息通知、前端框架

★★★ 为什么"不做什么"必须写下来 ✓✓:

  1. 防止范围蔓延:做到一半有人加需求,你可以拿出这份清单说"这不在本期范围" ✓
  2. 让设计能收敛:不知道"不做登录",就会一直纠结要不要做用户表
  3. 面试时能讲清:能说清边界,说明你想清楚了 ✓✓

★ 一条实用写法 ✓:把"不做"也写成将来可以做的形式:

text
本期不做:登录与权限
理由:本项目是内部系统,先不做鉴权;接口层已预留(S5-003 的错误码结构)
将来做:接入 Spring Security,或在网关层(S4-020)统一鉴权 ✓

5.4 验收标准:需求要能被测试 ★★★ ​

★ 每条需求都要写出"怎么算做完" ✓✓:

需求验收标准(可测)
借书库存 -1;记录 status=BORROWED;到期日 = 借出日 + 30 天 ✓
库存不足报 NO_AVAILABLE_COPY;库存保持 0,不能变负 ✓✓
读者停用报 READER_SUSPENDED;不产生任何写操作 ✓
重复归还第二次报 ALREADY_RETURNED;库存只加一次 ✓✓
并发借10 个线程抢 1 本,恰好 1 个成功 ✓✓✓

★★ 这些验收标准直接变成了测试用例 ✓✓ —— 项目的 BorrowFlowIntegrationTest 与 BorrowConcurrencyTest 就是照着这张表写的 ✓

★ 这就是"需求可测试"的价值 ✓:写完需求时,测试清单也就有了 ✓

5.5 从需求到设计的三张图 ★★ ​

★ 需求分析之后,下一步不是写代码,而是画三张图 ✓:

text
① 用例图 / 用例清单   → 本节(5.1)
② 实体关系(有哪些东西、它们什么关系)→ S5-002
③ 接口清单(对外提供什么)→ S5-003

★ 本项目的三个实体 ✓:

实体说明
Book(图书)有 ISBN、书名、作者、总量与可借量
Reader(读者)有卡号、姓名、状态(正常/停用)
BorrowRecord(借阅记录)一次借还是一条记录(含借出日、应还日、归还日)

★ 一个关键判断 ✓✓:

"借阅记录"是核心实体,不是附属信息 ✓

因为:借还这个动作本身要被留痕(谁、什么时候、还了没)✓✓

如果只在 Book 上记 "当前被谁借了",就查不到历史 ✗

6. 递进示例 ​

6.1 最小示例:一条需求的完整描述 ​

text
需求:借书

作为:读者
我要:借一本在馆的图书
以便:把它带回家看

前置条件:读者状态正常、未达借阅上限、该书有可借副本
后置条件:该书可借数 -1,生成一条借阅记录(到期日 = 今天 + 30 天)

异常:
  · 读者不存在 / 已停用 → 拒绝(READER_NOT_FOUND / READER_SUSPENDED)
  · 已达上限 → 拒绝(BORROW_LIMIT_REACHED)
  · 该书已借未还 → 拒绝(ALREADY_BORROWED)
  · 无可用副本 → 拒绝(NO_AVAILABLE_COPY)

6.2 业务示例:规则抽成配置 ​

java
@ConfigurationProperties(prefix = "library.rules")
public class LibraryRules {
    private int maxBorrowPerReader = 3;
    private int borrowDays = 30;
    // ...
}

6.3 反例:把规则写死在判断里 ​

java
// ✗ 业务方说"改成 5 本",你要改代码 + 发版
if (activeCount > 3) { throw new BusinessException("已达上限"); }

// ✓ 规则来自配置
if (activeCount >= rules.getMaxBorrowPerReader()) { ... }

7. 完整可运行代码与实测 ​

代码:labs/project-a(图书借阅管理系统)

启动:

powershell
cd labs
.\mvnw.cmd -pl project-a spring-boot:run
# 打开本机 18080 端口 → 前端页面
# 接口:/api/books

或用部署脚本(推荐):

powershell
pwsh -File scripts/run-project-a.ps1       # 打包 + 启动 + 等健康检查

为什么讲义里不写 http://... 这样的外链 ✗✓: VitePress 构建时会校验外链是否可达,localhost 在构建机上当然不可达 ✗ —— 于是整个构建失败(本项目就是这么踩到的 ✓)。写端口、写在代码块里即可。

★ 本节的需求在代码里的落点 ✓:

需求代码位置
六条业务规则application.yml 的 library.rules + LibraryRules
用例(借/还/续借)BorrowService
验收标准BorrowFlowIntegrationTest(12 个)、BorrowConcurrencyTest(3 个)

★ 实测 ✓:30 个测试全部通过 ✓✓

8. Debug 观察点 ​

  1. 把 max-borrow-per-reader 改成 5:观察借阅上限测试是否跟着变 ✓✓;
  2. 试着加一条新规则(如"一本书最多借 2 次"):看要改几个地方(规则/校验/SQL/测试)✓;
  3. 删掉"不做"清单里的一项(如登录):观察它要牵动多少设计 ✗✓;
  4. 挑一条验收标准,把它改成不可测的描述(如"系统要好用"):体会差别 ✓。

9. 常见错误 ​

#错误说明
1需求模糊就开工 ✗✗会返工,且返工成本随进度指数上升 ✓
2只写"做什么",不写"不做什么" ✗✗范围蔓延 ✓✓
3业务规则写死在代码里 ✗✗每次调整都要发版 ✓
4验收标准不可测(如"要好用")✗✗无法判断做没做完 ✓✓
5需求分析就想把表设计好 ✗顺序错了(先需求 → 再实体 → 再表)✓
6忽略异常分支 ✗✗异常才是要实现的大头(本例 4 种异常)✓✓

10. 动手实验 ​

目标:把一个模糊需求,做成能开工的清单。

步骤:

  1. 挑一个你熟悉的小系统(如"会议室预约")写出用例清单 ✓;
  2. 列出它的业务规则,并标出哪些数字将来会变 ★★★;
  3. 写出"不做什么"清单(至少 4 条)✓✓;
  4. 给每条需求写验收标准(要能被测试验证)★★★;
  5. 对比本项目:看你的清单缺了什么(多半是"异常分支"与"不做的事")✓。

验收方式:能说出"你的需求里哪三条最可能被误解"。

扩展任务:给本项目加一个真实需求「预约」(书被借走后可以预约):

  • 写出它的用例、规则、验收标准 ✓;
  • 说明:新增这个需求要改哪些地方(表/接口/规则/测试)✓✓;
  • 判断:它和现有的"超期清单"有没有冲突 ★★

11. 练习题 ​

  1. 判断:需求分析阶段就应该把数据库表定下来。(对 / 错)
  2. 填空:用例的标准句式是「作为 ______,我要 ______,以便 ______」。
  3. 简答:为什么"不做什么"的清单很重要?
  4. (改写)把这句话改成可验收的需求:
text
"借书要快,不能出错。"
  1. (设计)给"会议室预约系统"写出用例清单(至少 4 条)与"不做什么"(至少 3 条)。
参考答案(要点)
  1. 错。顺序是需求 → 实体 → 表 ✗ —— 先定表会导致"表反过来约束需求"(明明需要的功能因为表不支持而被砍掉)✓★
  2. 角色;做什么;得到什么价值 ✓
  3. ① 防止范围蔓延(有人加需求时你能拿出依据)✓; ② 让设计收敛(不纠结"要不要做 X")✓✓; ③ 说明你想清楚了(面试时能讲清边界是很大的加分项)✓★
  4. 示例:"借书接口 P99 < 200 ms;库存不足时返回 NO_AVAILABLE_COPY 且库存不变; 并发 10 个请求抢 1 本,恰好 1 个成功" ✓✓ —— 关键是每条都要能被测出来 ✓
  5. 要点:用例:预约、取消预约、签到(防占不用)、查询可约时段 ✓; 不做:跨楼层、设备借用、会议通知、与企业微信打通 ✓; 规则:最多提前几天约、单次最长时间、爽约几次停用 ✓✓

12. 掌握标准 ​

  • [ ] 能把模糊需求拆成可验收的条目 ★★★;
  • [ ] 能写出用例(角色 + 动作 + 价值) ★★★;
  • [ ] 能画出范围边界(做什么 / 不做什么) ★★★;
  • [ ] 知道业务规则要可配置 ★★★;
  • [ ] 能写出可测试的验收标准 ★★★;
  • [ ] 知道异常分支才是实现的大头 ★★★。

13. 扩展资源 ​

14. 下一步学习 ​

《项目 A:数据库设计》:把三个实体变成三张表,并决定约束与索引。

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引