04-从0到1实战上

简介: 《SDD规范驱动开发实操手册》第4篇:手把手带你从0到1落地真实需求!涵盖定义验收标准、编写项目宪法、生成18条可测FR、识别5个高危模糊点(含2个上线才爆的坑)、输出技术方案,并用analyze发现3处关键断层。每步均配代码级指令与检查清单,强调“先验再做”,拒绝混乱开发。(239字)

SDD 规范驱动开发实操手册 · 第 4 篇 / 共 8 篇

全系列按「说明 → 介绍 → 使用 → 实操 → 排障 → 收益」推进,每篇都可独立阅读,收藏专栏后按序看不会断。

现在开始真刀真枪。这篇走 Step 0 到 Step 5:定「做完了」的标准、写项目宪法、用 specify 产出 18 条 FR 的 spec.md、用 clarify 逼出 5 个模糊点(其中 2 个是上线才会炸的)、写技术方案,最后用 analyze 抓出 3 个真实断层。

第四章 具体实操:从 0 到 1 走完一个真实需求

本章是全书的操作核心。请打开你的项目跟做,替换成你自己的需求。

Step 0 准备:定义"做完了"是什么样

项目现状:云集商城后台,6 人团队,Spring Boot 3.2 + MySQL 8.0 + Redis 7 + Vue 3。已有订单、商品、用户模块,没有优惠券。

这一句话需求(产品原话):"双十一要发券,用户能领、下单能用,别超发。"

这句话不能直接开工。先把它翻译成三个可验收的目标:

目标 验收方式 负责人
用户能领到券,且不超过库存 压测 500 并发领 100 张券,成功数恰为 100 开发
用户下单能核销,金额算对 8 组金额用例(含叠加、门槛、抹零)全通过 测试
退款能把券正确退回 4 组退款用例(有效期内/已过期/已核销未支付/重复退款) 测试

时间盒:spec 60 分钟、clarify 30 分钟、plan 45 分钟、tasks 20 分钟、实施 2 天、验收半天。

关键动作:这一步不要打开 AI。先自己想清楚"做完了怎么验"。想不清楚就说明需求还没就绪,此时写 spec 只会把混乱固化。

Step 1 写宪法(如果项目还没有)

新建项目第一次走 SDD 时做;已有宪法则跳过,只检查它与本次需求是否冲突。分工见 3.8:红线由人给、条文由 AI 起草、验收由人拍板。下面这四项是技术负责人已经拍板过的结论,不是让 AI 自己发明原则。

/speckit.constitution 建立本项目宪法。聚焦四点:
1) 涉及资金/库存/权限的计算,必须覆盖正常、边界、并发三类单测;
2) 数据库变更只走迁移脚本,禁止手工改表;
3) 所有写接口必须幂等,接受客户端幂等键;
4) 日志禁止输出手机号、券码、身份证原文。
每条原则必须可验证,总量不超过一页。

产出:.specify/memory/constitution.md。检查点:能在一屏内读完;每条都能对应到一个检查动作;没有出现具体功能名。

⚠️ 常见错误:把宪法写成技术方案("优惠券用 Redis 扣减库存")。宪法只写跨功能恒定的约束,具体方案进 plan。

Step 2 写行为规范(specify)

这一步的输入质量决定后面所有环节的质量。给 AI 的指令要包含三样东西:业务背景、已知约束、明确的结构要求。

/speckit.specify 为电商后台新增"优惠券领取与核销"能力。

业务背景:双十一大促发券。券由运营在后台创建模板(面额、门槛、总量、每人限领、
有效期),用户在小程序领取,下单时选择使用。

已知约束(来自产品与运营,不要自行扩展):
- 同一用户对同一模板的限领数默认 1,可配置为 1-5;
- 券不可叠加使用,一单最多一张;
- 券过期不自动退库存(运营口径:过期即作废);
- 支持"仅限新用户"开关(注册 7 天内且无已支付订单)。

结构要求:
1. 问题陈述(3 句以内)
2. 用户故事(US-1 起)
3. 功能需求,EARS 句式,FR-001 起,一句一条,禁形容词
4. 边界与异常表(条件/期望行为/返回码)
5. 非功能需求(性能/安全/可观测/兼容性,带数字)
6. 不在范围内
7. 术语表

只写行为,不写技术实现。每条需求必须能被写成一个测试用例。

AI 产出的 spec.md(节选,这次产出共 2.1 万字符,18 条 FR):

# 规范:优惠券领取与核销

## 元信息
- 能力标识:001-coupon
- 状态:评审中
- 批准人:(待填)

## 1. 问题陈述
大促期间运营需要向指定用户群体发放优惠券以提升转化。当前系统无券能力,
运营只能通过人工改价,无法控制总量与风控,且事后无法审计。
不解决将导致大促无法按计划开展。

## 2. 用户故事
- US-1 作为运营,我希望创建券模板并设定总量与限领,以便控制成本。
- US-2 作为用户,我希望在小程序一键领券,以便下单时抵扣。
- US-3 作为用户,我希望下单时选择一张可用券,以便少付钱。
- US-4 作为财务,我希望每张券的发放与核销都有流水,以便对账。

## 3. 功能需求

### 领券
- FR-001 当用户点击"领取"且该模板剩余库存大于 0 时,系统应创建一张状态为
  "未使用"的券实例,并向用户返回券码。
- FR-002 如果用户已领取该模板的券数量达到每人限领上限,则系统应拒绝领取,
  返回 COUPON_LIMIT_REACHED,且不扣减库存。
- FR-003 当券模板不在有效期内时,系统应拒绝领取并返回 COUPON_NOT_ACTIVE。
- FR-004 如果同一用户在 1 秒内提交超过 3 次领取请求,则系统应拒绝第 4 次及
  之后的请求,返回 TOO_MANY_REQUESTS。
- FR-005 当启用"仅限新用户"配置时,系统应仅允许注册时间在 7 天内且无已支付
  订单的用户领取;否则返回 COUPON_NOT_ELIGIBLE。

### 核销
- FR-006 当订单提交且用户选择的券属于本人、状态为"未使用"、满足金额门槛时,
  系统应锁定该券并将订单应付金额扣减券面额(不低于 0)。
- FR-007 如果券面额大于订单金额,则系统应将应付金额置为 0,且不产生负数金额
  与找零。
- FR-008 如果券不满足门槛或不属于当前用户,则系统应拒绝核销,返回
  COUPON_NOT_APPLICABLE,且订单按原价提交。
- FR-009 当订单支付成功时,系统应将券状态由"已锁定"置为"已使用",并记录
  核销时间与操作人。
- FR-010 当订单一单使用多张券时,系统应拒绝并返回 COUPON_NOT_STACKABLE。

### 状态与过期
- FR-011 当处于"已锁定"状态时,系统应拒绝除"核销"与"解锁"外的所有状态变更。
- FR-012 当券实例的 expire_at 早于当前时间时,系统应在下一次读取时将其状态
  置为"已过期",且不允许再被锁定。
- FR-013 当订单在 30 分钟内未支付而关闭时,系统应将该订单锁定的券解锁为
  "未使用",有效期为原有效期。

### 审计与查询
- FR-014 系统应为每次库存扣减记录一条不可修改的流水,包含模板 ID、用户 ID、
  变更前后库存、幂等键、时间。
- FR-015 当用户查询"我的券"时,系统应按"未使用/已使用/已过期"分组返回,
  每张券包含面额、门槛、有效期、适用说明。

## 4. 边界与异常
| 条件 | 期望行为 | 返回码 |
|---|---|---|
| 库存为 0 | 拒绝领取,不创建实例 | COUPON_SOLD_OUT |
| 并发扣减导致库存不足 | 拒绝,回滚预占 | COUPON_SOLD_OUT |
| 券已过期仍尝试核销 | 拒绝,状态置为已过期 | COUPON_EXPIRED |
| 重复提交同一幂等键 | 返回首次结果,不重复发放 | —(幂等) |
| 退款成功且券未过期 | 券恢复"未使用" | — |
| 退款成功且券已过期 | 仅记流水,不恢复可用 | — |

## 5. 非功能需求
- NFR-001 领券接口在 500 并发、库存 100 的条件下,成功发放数应恰为 100,
  不出现负库存;P95 ≤ 300ms。
- NFR-002 券码应为 16 位大写字母数字,全局唯一,不可被顺序猜测。
- NFR-003 所有券状态变更须记录操作人、来源 IP、渠道;日志中券码脱敏为前 4 后 4。
- NFR-004 券能力须兼容现有订单流程,未使用券的订单行为不变。

## 6. 不在范围内
- 券的转赠与分享
- 券与会员等级、积分的联动
- 运营侧的券效果分析报表
- 过期自动退库存(运营确认不做)

## 7. 术语
- 券模板:运营创建的发券配置,定义面额、门槛、总量、有效期。
- 券实例:用户领取到的具体一张券,有唯一券码与状态。
- 核销:将券用于订单并扣减金额的动作。

这一步的检查点(Gate 1 的一半):

  • [ ] 每条 FR 都能立刻想出一个测试用例?
  • [ ] 边界表覆盖了"库存为 0 / 并发不足 / 已过期 / 重复提交 / 退款"五类?
  • [ ] Out of Scope 里至少有 3 条(说明你真的想过不做什么)?
  • [ ] 全文没有出现任何技术名词?

Step 3 消歧义(clarify)

这一步是最容易被跳过、但收益最高的一步。 让 AI 主动挑刺:

/speckit.clarify 逐条检查 spec.md。重点找四类问题:
1) 存在多种合理理解的需求;
2) 缺失的边界条件(尤其是并发、重复提交、外部依赖失败);
3) 没有数字的非功能指标;
4) 与 Out of Scope 可能冲突的需求。
把问题列成清单向我提问,每条给出你建议的默认答案。不要自行修改文件。

AI 提出的五个问题(原文)与我方回答:

# AI 的问题 我方回答 影响
Q1 限领上限按模板计还是按活动计? 按模板计 FR-002 明确口径
Q2 并发下库存不足时,是拒绝还是排队? 拒绝,前端提示"已抢完" 新增边界行 + FR-001 补充
Q3 退款时若券已过期,"退回"指什么? 仅记流水,不恢复可用 边界表第 6 行细化
Q4 订单取消(未支付)与退款(已支付)是否同一逻辑? 不同:取消→解锁,退款→恢复或记流水 新增 FR-013
Q5 多端(小程序/H5)限领计数是否共享? 共享,按用户 ID 计 FR-002 加"跨渠道"

由此产生的 spec 变更(diff 形式,团队评审时看这个就够):

- FR-002 如果用户已领取该模板的券数量达到每人限领上限,则系统应拒绝领取。
+ FR-002 如果用户已领取该模板的券数量(跨所有渠道累计)达到每人限领上限,
+        则系统应拒绝领取,返回 COUPON_LIMIT_REACHED,且不扣减库存。
+ FR-016 当订单取消(未支付超时关闭)时,系统应解锁该订单锁定的券,
+        与退款场景的区别是:取消必然解锁,退款视券是否过期而定。

这一步的实际价值:Q4 和 Q5 都是"上线后才会被发现"的问题。花 30 分钟在这里,省下的是两天返工和一次线上事故。如果预算只够做一件事,做 clarify。

Step 4 写技术方案(plan)

/speckit.plan 技术栈见宪法(Java 21 / Spring Boot 3.2 / MySQL 8.0 / Redis 7 / Vue 3)。
要求:
1. 数据模型:表结构、索引、DDL、Flyway 迁移脚本;
2. 接口契约:路径、方法、请求/响应、错误码;
3. 并发与幂等:如何保证不超发、如何做到重复提交安全;
4. 失败处理:Redis 不可用、DB 写失败、下游超时;
5. 灰度与回滚:开关设计、回滚步骤(含数据回滚);
6. 对每条 FR 说明由哪个模块承担。
不要写代码,只写方案。

产出的 plan.md(结构化摘要):

# 技术方案:优惠券领取与核销

## 1. 架构与数据流
小程序 → API 网关 → coupon-service
                        ├─ Redis:库存计数(DECR)+ 幂等键(SETNX,TTL 24h)
                        └─ MySQL:券模板、券实例、库存流水(最终一致)

领券主链路:校验资格 → Redis 原子扣减 → DB 落券实例 → 写流水 → 返回券码
           (扣减成功但落库失败 → 补偿任务按流水回补,见 §5)

## 2. 数据模型
### coupon_template
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT PK | 模板 ID |
| name | VARCHAR(64) | 券名 |
| face_value | DECIMAL(10,2) | 面额 |
| threshold | DECIMAL(10,2) | 使用门槛 |
| total_stock | INT | 总库存 |
| per_user_limit | TINYINT | 每人限领,1-5 |
| new_user_only | TINYINT | 仅新用户,0/1 |
| start_at / end_at | DATETIME | 有效期 |
| status | TINYINT | 0 草稿 1 生效 2 停用 |

### coupon_instance
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT PK | |
| code | CHAR(16) UNIQUE | 券码 |
| template_id | BIGINT | 索引 (template_id, user_id) |
| user_id | BIGINT | |
| status | TINYINT | 0 未使用 1 已锁定 2 已使用 3 已过期 |
| order_no | VARCHAR(32) | 锁定/核销订单号 |
| locked_at / used_at / expire_at | DATETIME | |
| operator_id | BIGINT | 核销操作人(FR-009) |
| version | INT | 乐观锁 |

### coupon_stock_log(不可修改,只追加)
template_id / user_id / delta / stock_before / stock_after / idempotency_key / created_at

## 3. 接口契约
POST /api/coupons/{templateId}/claim
  Header: X-Idempotency-Key(必填)
  200: { "code": "ABCD1234EFGH5678", "expireAt": "2026-11-20T23:59:59+08:00" }
  4xx: COUPON_SOLD_OUT / COUPON_LIMIT_REACHED / COUPON_NOT_ACTIVE /
       COUPON_NOT_ELIGIBLE / TOO_MANY_REQUESTS

POST /api/orders/{orderNo}/coupon
  Body: { "code": "..." }
  200: { "payable": "89.00", "discount": "11.00" }
  4xx: COUPON_NOT_APPLICABLE / COUPON_NOT_STACKABLE / COUPON_EXPIRED

GET /api/coupons/mine?status=unused|used|expired

## 4. 并发与幂等
- 库存:Redis `DECR` 原子操作,返回 < 0 则 `INCR` 回补并拒绝(FR-001 覆盖)。
  预热时把 total_stock 写入 Redis,DB 为最终真源,每小时对账。
- 限领:Redis `HINCRBY coupon:limit:{templateId} {userId} 1`,超限则 `HINCRBY -1` 回滚(FR-002)。
- 幂等:`SETNX idem:{key}`,TTL 24h;命中已存在则返回首次结果(NFR + 边界表)。
- 状态变更:`UPDATE ... WHERE version = ?`,失败重试 3 次后返回冲突(FR-011)。

## 5. 失败处理
| 故障 | 表现 | 处理 |
|---|---|---|
| Redis 不可用 | 领券全失败 | 降级为"仅提示稍后重试",不直连 DB 扣减(防超发) |
| Redis 扣减成功、DB 落库失败 | 用户无券但库存已扣 | 补偿任务 5 分钟后按流水回补库存,并告警 |
| 支付回调重复 | 重复核销 | 幂等键 + 状态机拒绝非法转移 |
| 消息丢失(订单关闭事件) | 券长期锁定 | 每小时扫 locked_at > 30min 的券,主动解锁 |

## 6. 灰度与回滚
- 灰度:`coupon.enabled` 总开关 + `coupon.whitelist` 用户白名单,先内内部 20 人。
- 回滚:关开关即停新增;已发券保留可核销;数据不回滚(流水保证可对账)。
- 迁移:Flyway `V20261110__coupon.sql`,只新增表,不改存量表,可安全重放。

## 7. FR 覆盖对照
| FR | 承担模块 |
|---|---|
| FR-001/002/003/004 | CouponClaimService + Redis 原子操作 |
| FR-005 | EligibilityChecker |
| FR-006/007/008/010 | OrderCouponService(金额计算) |
| FR-009/011 | CouponStateMachine |
| FR-012 | CouponExpireJob(惰性 + 定时双保险) |
| FR-013/016 | OrderEventListener(关闭/取消) |
| FR-014 | CouponStockLogWriter |
| FR-015 | CouponQueryService |

## 8. 风险与权衡
- 风险:Redis 与 DB 双写不一致。取舍:接受秒级不一致,用对账任务收敛,
  因为强一致会牺牲领券性能(NFR-001 要求 P95 ≤ 300ms)。
- 风险:券码 16 位存在碰撞。取舍:`code` 唯一索引 + 生成时重试 3 次,
  碰撞概率可忽略(62^16 空间)。

Gate 2 检查点:数据模型是否覆盖了每条 FR 提到的字段(尤其 FR-009 的 operator_id)?回滚方案是否真的可执行?性能假设有没有压测依据?

Step 5 一致性分析(analyze)

这一步专门抓"人眼容易漏、AI 容易忘"的错位。 它在写代码之前跑,成本最低。

/speckit.analyze 检查 spec.md / plan.md / tasks.md 三者一致性。输出问题清单,
每条标注严重级别(阻断/建议),不要直接改文件。

本次发现三个问题:

# 问题 级别 修正
A1 FR-013 要求"退款退回券",plan 只有订单关闭监听,没有退款事件 阻断 plan §4 增加 OrderRefundedListener,tasks 增加 T009
A2 FR-009 要求记录核销操作人,coupon_instance 有 operator_id,但接口契约未传递 阻断 接口增加 X-Operator-Id(内部调用),plan §3 补充
A3 FR-015 要求"我的券"分三组返回,tasks 只写了列表接口,无分组与空态 建议 tasks 的 T012 补充空态与过期态验收

A2 是最典型的一类问题:需求写了、模型建了、链路断了。 这类断层在纯聊天式开发里 100% 会漏,因为 AI 每次只看局部上下文。analyze 的价值就是用一次全量扫描换掉这三天的联调排查。


下一篇:规范定稿只是纸面。下一篇把 18 条 FR 拆成带并行标记的任务,分批实施、跑验证命令、做验收测试,并演示需求变更怎么走「先改规范再改代码」。

系列目录见专栏。如果这篇对你有用,点个赞让更多被 AI 写崩过项目的人看到。

相关文章
|
14天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)
6210 45
|
2天前
|
云安全 人工智能 安全
|
15天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
5134 60
|
9天前
|
Linux 开发工具 iOS开发
2026最新codex下载安装保姆级使用教程(下载+安装+登录+使用,含所有历史版本)
本文详解OpenAI Codex国内快速上手指南:提供Win/macOS/Linux全平台免安装包、npm/Homebrew等多方式安装、账号/API Key双登录、简体中文切换,覆盖下载、安装、汉化、使用及高频避坑(如微软商店失败、npm超时等),10分钟即可跑通。
2487 6
|
2天前
|
JSON 前端开发 API
阿里云最新Qwen4-Max大模型介绍:模型定位、特征、能力、适用场景解析
2026 年 11 月 11 日,阿里发布第四代旗舰模型Qwen4-Max预览版,首发千问 AI 平台 Token Plan,并同步接入千问办公、Qoder 等入口。该版本聚焦知识理解、世界感知、编程与 Agentic 四项升级,主打复杂编程、专业办公、前端设计与专业软件操作。预览期至 11 月 25 日 24:00,到期后模型标识下线;限时 2 折、新用户再享 5 折,最低 1 折可体验。
|
7天前
|
人工智能 安全 API
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(dsh)是DeepSeek AI于2026年开源的轻量级Agent运行框架,基于Cordis内核,践行“一切皆插件”理念。支持Web/桌面双端,开箱即用;内置多模型接入、丰富插件生态(超3.5万),适配编程、办公、创作等场景。(239字)
1565 1
|
2天前
|
人工智能 前端开发 API
阿里云Qwen4-Max 大模型详解:定位、特征、能力、适用场景与相对Qwen3-Max差异解析
2026年11月11日,阿里发布第四代旗舰模型Qwen4-Max预览版,首发千问AI平台Token Plan并接入千问办公、Qoder等入口。该版本聚焦知识理解、世界感知、编程与Agentic四项升级,主打复杂编程、专业办公、前端设计与专业软件操作等真实任务。预览期为11月11日10:00至11月25日24:00,仅支持Token Plan调用,到期后模型标识下线;限时2折、新用户再享5折,最低1折可体验。

热门文章

最新文章