@wneng/create-keel 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -1
- package/dist/index.js +798 -33
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/feature/templates/arch/adr.md.eta +31 -0
- package/src/feature/templates/backend/design.md.eta +35 -0
- package/src/feature/templates/frontend/design.md.eta +34 -0
- package/src/feature/templates/pm/prd.md.eta +37 -0
- package/src/feature/templates/sample/user-signup/arch/adr.md.eta +48 -0
- package/src/feature/templates/sample/user-signup/backend/design.md.eta +77 -0
- package/src/feature/templates/sample/user-signup/frontend/design.md.eta +54 -0
- package/src/feature/templates/sample/user-signup/pm/prd.md.eta +46 -0
- package/src/feature/templates/sample/user-signup/test/test-plan.md.eta +40 -0
- package/src/feature/templates/test/test-plan.md.eta +23 -0
- package/src/templates/docs-skeleton/files/usage-quickstart.md +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wneng/create-keel",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Scaffolder for Contract First + Vibe Coding projects (keel conventions)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"scaffold",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
18
|
"src/templates",
|
|
19
|
+
"src/feature/templates",
|
|
19
20
|
"README.md",
|
|
20
21
|
"LICENSE"
|
|
21
22
|
],
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ADR-<%= it.feature.nextAdrNumber %>:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 状态:**Proposed**(<%= it.year %>)
|
|
4
|
+
> 决策范围:<%= it.feature.title %>
|
|
5
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
6
|
+
> 来源 PRD:[docs/01-背景与需求/prd-<%= it.feature.slug %>.md](../01-背景与需求/prd-<%= it.feature.slug %>.md)
|
|
7
|
+
|
|
8
|
+
## 背景
|
|
9
|
+
|
|
10
|
+
<!-- 为什么现在做出决定?什么约束或信号触发了这份 ADR? -->
|
|
11
|
+
|
|
12
|
+
## 决策
|
|
13
|
+
|
|
14
|
+
<!-- 一段话说清决策结论,不含歧义。 -->
|
|
15
|
+
|
|
16
|
+
## 备选方案
|
|
17
|
+
|
|
18
|
+
- **方案 A** —— 优点 / 缺点 / 为何不采用
|
|
19
|
+
- **方案 B** —— 优点 / 缺点 / 为何不采用
|
|
20
|
+
|
|
21
|
+
## 影响
|
|
22
|
+
|
|
23
|
+
### 正面
|
|
24
|
+
- 列出正面影响
|
|
25
|
+
|
|
26
|
+
### 负面 / 风险
|
|
27
|
+
- 列出风险与缓解措施
|
|
28
|
+
|
|
29
|
+
## 参考
|
|
30
|
+
|
|
31
|
+
- 契约:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 后端设计:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> PRD:[docs/01-背景与需求/prd-<%= it.feature.slug %>.md](../01-背景与需求/prd-<%= it.feature.slug %>.md)
|
|
5
|
+
|
|
6
|
+
## 契约
|
|
7
|
+
|
|
8
|
+
请求 / 响应形态的唯一真值源是 OpenAPI 文档。本设计文档与契约保持同步;
|
|
9
|
+
两者不一致时以契约为准。
|
|
10
|
+
|
|
11
|
+
- 路径:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
12
|
+
|
|
13
|
+
## 模块边界
|
|
14
|
+
|
|
15
|
+
<!-- 本特性涉及哪些模块?列出会变更的 controller / service / repository。 -->
|
|
16
|
+
|
|
17
|
+
## 数据模型
|
|
18
|
+
|
|
19
|
+
<!-- 新增表、字段、索引。引用 contracts/dictionaries/ 中的字典定义。 -->
|
|
20
|
+
|
|
21
|
+
## 错误处理
|
|
22
|
+
|
|
23
|
+
<!-- 错误码(DOMAIN.SUBDOMAIN.REASON)、HTTP 状态码映射、重试策略。 -->
|
|
24
|
+
|
|
25
|
+
## 时序
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
client -> controller -> service -> repository -> db
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 测试计划
|
|
32
|
+
|
|
33
|
+
- service 层单元测试
|
|
34
|
+
- 与 `contracts/openapi/api.yaml` 的契约测试
|
|
35
|
+
- 见:[docs/07-质量与测试/test-plans/<%= it.feature.slug %>.md](../07-质量与测试/test-plans/<%= it.feature.slug %>.md)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 前端设计:<%= it.feature.title %>(web)
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> 后端设计:[docs/04-后端详细设计/<%= it.feature.slug %>.md](../04-后端详细设计/<%= it.feature.slug %>.md)
|
|
5
|
+
|
|
6
|
+
## 用户流程
|
|
7
|
+
|
|
8
|
+
<!-- 本特性在 web 端的逐步用户旅程。 -->
|
|
9
|
+
|
|
10
|
+
## 页面 / 组件拆分
|
|
11
|
+
|
|
12
|
+
| 组件 | 职责 |
|
|
13
|
+
|------|------|
|
|
14
|
+
| `<%= it.feature.slug %>Page` | 顶层路由组件 |
|
|
15
|
+
| | |
|
|
16
|
+
|
|
17
|
+
## 状态管理
|
|
18
|
+
|
|
19
|
+
<!-- 本地状态、全局 store 切片、缓存失效规则。 -->
|
|
20
|
+
|
|
21
|
+
## API 调用
|
|
22
|
+
|
|
23
|
+
本特性通过 OpenAPI 契约调用后端。请由 `contracts/openapi/api.yaml` 生成
|
|
24
|
+
客户端,避免手写 fetch 代码。
|
|
25
|
+
|
|
26
|
+
- 契约:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
27
|
+
|
|
28
|
+
## 校验
|
|
29
|
+
|
|
30
|
+
<!-- 客户端校验规则;服务端是权威,但客户端校验能让错误 UX 更快。 -->
|
|
31
|
+
|
|
32
|
+
## 无障碍
|
|
33
|
+
|
|
34
|
+
<!-- 键盘导航、屏幕阅读器标签、对比度说明。 -->
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# PRD:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> 由 `create-keel feature add` 在 <%= it.generatedAt %> 创建
|
|
5
|
+
|
|
6
|
+
## 背景
|
|
7
|
+
|
|
8
|
+
<!-- 描述用户问题与业务背景。替换本段。 -->
|
|
9
|
+
|
|
10
|
+
## 目标
|
|
11
|
+
|
|
12
|
+
- [ ] 主要目标
|
|
13
|
+
- [ ] 次要目标
|
|
14
|
+
|
|
15
|
+
## 非目标
|
|
16
|
+
|
|
17
|
+
- 不在本次范围
|
|
18
|
+
|
|
19
|
+
## 用户故事
|
|
20
|
+
|
|
21
|
+
**作为** [角色],**我希望** [能力],**以便** [收益]。
|
|
22
|
+
|
|
23
|
+
## 验收标准(EARS)
|
|
24
|
+
|
|
25
|
+
下列标准使用 EARS 语法(WHEN / IF / WHILE / WHERE + THE \<system\> SHALL
|
|
26
|
+
\<response\>)。请把占位描述替换为真实行为;保留 EARS 句型,让
|
|
27
|
+
`governance-lint` 把这些条目识别为可测试规则。
|
|
28
|
+
|
|
29
|
+
1. WHEN [触发事件发生时],THE 系统 SHALL [可观察响应]。
|
|
30
|
+
2. IF [出现意外情况],THEN THE 系统 SHALL [错误处理 / 降级]。
|
|
31
|
+
3. WHILE [处于某状态时],THE 系统 SHALL [持续行为]。
|
|
32
|
+
|
|
33
|
+
## 关联引用
|
|
34
|
+
|
|
35
|
+
- 后端设计:[docs/04-后端详细设计/<%= it.feature.slug %>.md](../04-后端详细设计/<%= it.feature.slug %>.md)
|
|
36
|
+
- 前端设计:[docs/05-前端客户端详细设计/<%= it.feature.slug %>-web.md](../05-前端客户端详细设计/<%= it.feature.slug %>-web.md)
|
|
37
|
+
- 契约:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# ADR-<%= it.feature.nextAdrNumber %>:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 状态:**Proposed**(<%= it.year %>)
|
|
4
|
+
> 决策范围:账户注册流程 + 验证模型
|
|
5
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
6
|
+
> 来源 PRD:[docs/01-背景与需求/prd-<%= it.feature.slug %>.md](../01-背景与需求/prd-<%= it.feature.slug %>.md)
|
|
7
|
+
|
|
8
|
+
## 背景
|
|
9
|
+
|
|
10
|
+
PRD 要求自助注册账户并强制邮箱验证。本领域常见的实现模式有三种:应用内
|
|
11
|
+
验证 token、邮件 magic link、邮件数字验证码。三者在安全与 UX 上各有权衡,
|
|
12
|
+
选型会影响 `pending` 用户的存活时间以及鉴权模型的形态。
|
|
13
|
+
|
|
14
|
+
## 决策
|
|
15
|
+
|
|
16
|
+
采用 **基于服务端存储 token 的邮件 magic link**:
|
|
17
|
+
|
|
18
|
+
- 注册时在 `users` 表创建 `pending` 行,并在 `verification_tokens` 表创建
|
|
19
|
+
一条 UUIDv4 token,TTL 24 小时
|
|
20
|
+
- 验证邮件包含一条指向 `GET /auth/verify?token=...` 的链接
|
|
21
|
+
- 成功时把用户切到 `active`,token 标记为已消费(一次性),并下发会话 cookie
|
|
22
|
+
- 失败 / 过期时展示"重新申请链接"的 CTA
|
|
23
|
+
|
|
24
|
+
## 备选方案
|
|
25
|
+
|
|
26
|
+
- **数字验证码(6 位 OTP)**—— 在移动端输入更顺手,但桌面浏览器 UX 反而
|
|
27
|
+
更差,多了一步冗余输入。否决。
|
|
28
|
+
- **不验证,只发邮件提示**—— 上线最快,但会让用户表充斥拼写错误与垃圾。
|
|
29
|
+
与 PRD 目标 #2 冲突。否决。
|
|
30
|
+
- **仅联邦身份(Google / GitHub)**—— 适用场景下 UX 最佳,但会把没有这些
|
|
31
|
+
账户的用户挡在外面,且 PRD 把第三方登录明确列为 v1 非目标。否决。
|
|
32
|
+
|
|
33
|
+
## 影响
|
|
34
|
+
|
|
35
|
+
### 正面
|
|
36
|
+
- 标准成熟方案,每种后端栈都有现成库支持
|
|
37
|
+
- token 轮换策略简单(删除 + 新建一行)
|
|
38
|
+
- `pending` → `active` 状态机清晰可审计
|
|
39
|
+
|
|
40
|
+
### 负面 / 风险
|
|
41
|
+
- 邮件送达率成为硬依赖,必须监控退信率
|
|
42
|
+
- 24 小时 TTL 可能对一周才看一次邮箱的用户偏短;上线后根据转化遥测
|
|
43
|
+
数据再决定是否拉长
|
|
44
|
+
|
|
45
|
+
## 参考
|
|
46
|
+
|
|
47
|
+
- 契约:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
48
|
+
- 关联:未来的 `password-reset` ADR 会复用同一张 token 表的 schema
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# 后端设计:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> PRD:[docs/01-背景与需求/prd-<%= it.feature.slug %>.md](../01-背景与需求/prd-<%= it.feature.slug %>.md)
|
|
5
|
+
|
|
6
|
+
## 契约
|
|
7
|
+
|
|
8
|
+
请求 / 响应形态的唯一真值源是 OpenAPI 文档。本设计文档描述**实现**;两者
|
|
9
|
+
不一致时以契约为准。
|
|
10
|
+
|
|
11
|
+
- 路径:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
12
|
+
|
|
13
|
+
## 模块边界
|
|
14
|
+
|
|
15
|
+
本特性涉及三个模块:
|
|
16
|
+
|
|
17
|
+
- `auth/signup` —— 拥有 `POST /users/signup` 与 `GET /auth/verify`
|
|
18
|
+
- `email` —— 拥有验证邮件的发送(模板 + 传输)
|
|
19
|
+
- `users` —— 拥有 `users` 表与 `pending`/`active` 状态机
|
|
20
|
+
|
|
21
|
+
## 数据模型
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
users
|
|
25
|
+
id uuid PK
|
|
26
|
+
email text UNIQUE NOT NULL
|
|
27
|
+
password_hash text NOT NULL -- bcrypt cost 12
|
|
28
|
+
state text NOT NULL -- enum('pending','active','suspended')
|
|
29
|
+
created_at timestamptz DEFAULT now()
|
|
30
|
+
activated_at timestamptz NULL
|
|
31
|
+
|
|
32
|
+
verification_tokens
|
|
33
|
+
token uuid PK
|
|
34
|
+
user_id uuid FK -> users.id NOT NULL
|
|
35
|
+
expires_at timestamptz NOT NULL -- created_at + 24h
|
|
36
|
+
consumed_at timestamptz NULL
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`users` 表上加索引 `users_email_lower_unique`(基于 `lower(email)`),
|
|
40
|
+
保证大小写不敏感的唯一性。
|
|
41
|
+
|
|
42
|
+
## 错误处理
|
|
43
|
+
|
|
44
|
+
| 错误码 | HTTP | 触发场景 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `USER.AUTH.EMAIL_INVALID` | 400 | 邮箱不符合 RFC 5322 |
|
|
47
|
+
| `USER.AUTH.PASSWORD_TOO_SHORT` | 400 | 密码不足 8 字符 |
|
|
48
|
+
| `USER.AUTH.EMAIL_TAKEN` | 409 | 通用响应,不暴露邮箱是否存在 |
|
|
49
|
+
| `USER.AUTH.RATE_LIMITED` | 429 | 对应 PRD 验收标准 4 |
|
|
50
|
+
| `USER.AUTH.TOKEN_EXPIRED` | 410 | 验证链接超出 24 小时后被点击 |
|
|
51
|
+
| `USER.AUTH.TOKEN_INVALID` | 400 | token 不存在或已被消费 |
|
|
52
|
+
|
|
53
|
+
## 时序
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
POST /users/signup
|
|
57
|
+
controller 校验请求体
|
|
58
|
+
-> service.signup(email, password)
|
|
59
|
+
-> users.create(state='pending')
|
|
60
|
+
-> tokens.create(user_id, ttl=24h)
|
|
61
|
+
-> email.send_verification(user, token.token)
|
|
62
|
+
-> 201 Created { id, email, state }
|
|
63
|
+
|
|
64
|
+
GET /auth/verify?token=...
|
|
65
|
+
controller -> service.verify(token)
|
|
66
|
+
-> tokens.consume(token) # 拒绝过期 / 已用 token
|
|
67
|
+
-> users.activate(user_id)
|
|
68
|
+
-> session.create(user_id)
|
|
69
|
+
-> 302 重定向到 /welcome
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 测试计划
|
|
73
|
+
|
|
74
|
+
- `service.signup` 的单元测试覆盖全部 6 种错误码
|
|
75
|
+
- 针对 `contracts/openapi/api.yaml` 的契约测试
|
|
76
|
+
- 属性测试:用同一 token 重复 verify 应当幂等
|
|
77
|
+
- 见:[docs/07-质量与测试/test-plans/<%= it.feature.slug %>.md](../07-质量与测试/test-plans/<%= it.feature.slug %>.md)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 前端设计:<%= it.feature.title %>(web)
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> 后端设计:[docs/04-后端详细设计/<%= it.feature.slug %>.md](../04-后端详细设计/<%= it.feature.slug %>.md)
|
|
5
|
+
|
|
6
|
+
## 用户流程
|
|
7
|
+
|
|
8
|
+
1. 访客从市场站点跳转到 `/signup`
|
|
9
|
+
2. 填写邮箱 + 密码;客户端实时校验长度与格式
|
|
10
|
+
3. 提交 → 按钮进入 loading → 成功或失败 toast
|
|
11
|
+
4. 成功后页面切到"请查收邮件",提供"重新发送"按钮
|
|
12
|
+
5. 用户点击邮件中的链接 → 后端验证 → 302 跳到 `/welcome`
|
|
13
|
+
|
|
14
|
+
## 页面 / 组件拆分
|
|
15
|
+
|
|
16
|
+
| 组件 | 职责 |
|
|
17
|
+
|------|------|
|
|
18
|
+
| `SignupPage` | 顶层路由,持有表单状态与提交逻辑 |
|
|
19
|
+
| `EmailField` | 提交前内联校验 RFC-5322 邮箱语法 |
|
|
20
|
+
| `PasswordField` | 长度计 + 强度提示 |
|
|
21
|
+
| `SubmitButton` | 两个字段都合法前禁用;请求中显示 loading 旋转图 |
|
|
22
|
+
| `CheckEmailScreen` | 注册后确认页;带 60 秒冷却的"重新发送"按钮 |
|
|
23
|
+
| `WelcomePage` | 验证后落地页 |
|
|
24
|
+
|
|
25
|
+
## 状态管理
|
|
26
|
+
|
|
27
|
+
- 表单字段使用组件本地 state,表单层无需全局 store
|
|
28
|
+
- `useSignupMutation` 包装 API 调用,由 tanstack-query 处理重试策略
|
|
29
|
+
- 鉴权会话在 `/welcome` 加载**之后**填充,使用 verify 重定向时下发的
|
|
30
|
+
cookie,永远不做乐观更新
|
|
31
|
+
|
|
32
|
+
## API 调用
|
|
33
|
+
|
|
34
|
+
本特性使用 OpenAPI 生成的客户端,不要手写 fetch 调用。
|
|
35
|
+
|
|
36
|
+
- 契约:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
37
|
+
- 操作:`POST /users/signup`(提交表单)、`GET /auth/verify`(验证落地)
|
|
38
|
+
|
|
39
|
+
## 校验
|
|
40
|
+
|
|
41
|
+
客户端校验与后端接受的形态保持一致;服务端仍然是 `EMAIL_TAKEN` 与限流
|
|
42
|
+
响应的权威。
|
|
43
|
+
|
|
44
|
+
| 字段 | 规则 | UI 反馈 |
|
|
45
|
+
|------|------|---------|
|
|
46
|
+
| email | RFC 5322 语法 | 内联提示"请输入合法邮箱" |
|
|
47
|
+
| password | ≥ 8 字符 | 输入框下方显示强度计 + 字符计数 |
|
|
48
|
+
|
|
49
|
+
## 无障碍
|
|
50
|
+
|
|
51
|
+
- 表单可纯键盘操作;回车提交
|
|
52
|
+
- 错误信息使用 `role="alert"`,让屏幕阅读器即时朗读
|
|
53
|
+
- 校验状态绝不只用颜色表达,必须配图标 + 文字
|
|
54
|
+
- 密码字段的"显示 / 隐藏"切换可被 Tab 触达并被屏幕阅读器朗读
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# PRD:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> 这是 `create-keel --sample-feature` 附带的样例特性。把它当作可读的范例:
|
|
5
|
+
> 看一遍后用你产品自己的 PRD 替换它。
|
|
6
|
+
|
|
7
|
+
## 背景
|
|
8
|
+
|
|
9
|
+
终端用户需要能在平台上自助创建账户。当前由销售团队手动开通账户,这种方式
|
|
10
|
+
撑不过最初 50 个客户。自助注册表单去掉手动环节,把首次价值时间从数天
|
|
11
|
+
缩短到数分钟。
|
|
12
|
+
|
|
13
|
+
## 目标
|
|
14
|
+
|
|
15
|
+
- [ ] 新用户能用邮箱 + 密码在 60 秒内完成账户创建
|
|
16
|
+
- [ ] 验证邮箱前账户除了浏览公开文档外不能做任何事
|
|
17
|
+
- [ ] 销售团队不再为自助层用户手动开账户
|
|
18
|
+
|
|
19
|
+
## 非目标
|
|
20
|
+
|
|
21
|
+
- 第三方登录(Google / GitHub)—— v1 不做,单独排期
|
|
22
|
+
- SAML / SSO —— 企业层路线图
|
|
23
|
+
- 密码重置流程 —— 由姊妹特性 `password-reset` 覆盖
|
|
24
|
+
|
|
25
|
+
## 用户故事
|
|
26
|
+
|
|
27
|
+
**作为** 潜在用户,**我希望** 能用邮箱与密码自助注册账户,**以便** 不必
|
|
28
|
+
联系销售就能开始试用产品。
|
|
29
|
+
|
|
30
|
+
**作为** 产品负责人,**我希望** 新账户必须验证邮箱,**以便** 垃圾邮件与
|
|
31
|
+
拼写错误不会污染活跃用户指标。
|
|
32
|
+
|
|
33
|
+
## 验收标准(EARS)
|
|
34
|
+
|
|
35
|
+
1. WHEN 访客在注册表单中提交语法合法的邮箱与不少于 8 字符的密码,THE 系统 SHALL 在 30 秒内创建一条 `pending` 状态的用户记录并发送验证邮件。
|
|
36
|
+
2. IF 访客提交的邮箱已被注册,THEN THE 系统 SHALL 返回 HTTP 409 与不暴露邮箱存在性的通用错误信息。
|
|
37
|
+
3. WHILE 用户处于 `pending` 状态,THE 系统 SHALL 拒绝该用户发起的每一次已认证 API 调用,返回 HTTP 403 与错误码 `USER.AUTH.EMAIL_NOT_VERIFIED`。
|
|
38
|
+
4. WHERE 启用了限流,THE 系统 SHALL 允许同一 IP 每 15 分钟最多 5 次注册尝试,超出后返回 HTTP 429。
|
|
39
|
+
5. WHEN 用户在收到链接的 24 小时内点击验证链接,THE 系统 SHALL 把用户记录切换到 `active` 状态并重定向到注册后欢迎页。
|
|
40
|
+
|
|
41
|
+
## 关联引用
|
|
42
|
+
|
|
43
|
+
- 后端设计:[docs/04-后端详细设计/<%= it.feature.slug %>.md](../04-后端详细设计/<%= it.feature.slug %>.md)
|
|
44
|
+
- 前端设计:[docs/05-前端客户端详细设计/<%= it.feature.slug %>-web.md](../05-前端客户端详细设计/<%= it.feature.slug %>-web.md)
|
|
45
|
+
- 契约:[<%= it.feature.contractAnchor %>](../../<%= it.feature.contractAnchor %>)
|
|
46
|
+
- 测试计划:[docs/07-质量与测试/test-plans/<%= it.feature.slug %>.md](../07-质量与测试/test-plans/<%= it.feature.slug %>.md)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 测试计划:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> PRD:[docs/01-背景与需求/prd-<%= it.feature.slug %>.md](../../01-背景与需求/prd-<%= it.feature.slug %>.md)
|
|
5
|
+
|
|
6
|
+
## 覆盖矩阵
|
|
7
|
+
|
|
8
|
+
下表把每条 PRD 验收标准映射到一个或多个测试产物。
|
|
9
|
+
|
|
10
|
+
| PRD 标准 | 测试类型 | 位置 |
|
|
11
|
+
|---------|---------|------|
|
|
12
|
+
| 1. WHEN 合法注册 → 201 + 邮件已发送 | 单元 + 端到端 | `server/test/signup.test.ts` + `web/e2e/signup-happy.spec.ts` |
|
|
13
|
+
| 2. IF 邮箱已被占 → 409 通用 | 单元 | `server/test/signup.duplicate.test.ts` |
|
|
14
|
+
| 3. WHILE pending → 403 EMAIL_NOT_VERIFIED | 集成 | `contracts/tests/acceptance/<%= it.feature.slug %>.feature` |
|
|
15
|
+
| 4. WHERE 启用限流 → 5 次 / 15 分钟 | 集成 | `server/test/signup.rate-limit.test.ts` |
|
|
16
|
+
| 5. WHEN 24 小时内 verify → active + 重定向 | 单元 + 端到端 | `server/test/verify.test.ts` + `web/e2e/signup-verify.spec.ts` |
|
|
17
|
+
|
|
18
|
+
## 边界用例
|
|
19
|
+
|
|
20
|
+
- [ ] 邮箱 / 密码为空 → 400,两条错误同时返回
|
|
21
|
+
- [ ] 邮箱含首尾空格 → 查询前裁剪
|
|
22
|
+
- [ ] 密码恰好 8 字符 → 接受(边界)
|
|
23
|
+
- [ ] 验证链接在第 24 小时 - 1 秒被点击 → 接受(边界)
|
|
24
|
+
- [ ] 验证链接被点击两次 → 第二次返回 `TOKEN_INVALID`,不是 500
|
|
25
|
+
- [ ] 同一邮箱并发注册 → 仅一笔成功,其余 409
|
|
26
|
+
|
|
27
|
+
## 属性测试
|
|
28
|
+
|
|
29
|
+
`signup → verify` 流程具有天然的 round-trip 性质:
|
|
30
|
+
|
|
31
|
+
> 对任意合法的 (email, password),注册之后用收到的 token 完成 verify,
|
|
32
|
+
> 结果应当是一个状态为 `active` 且邮箱与输入相同的用户记录。
|
|
33
|
+
|
|
34
|
+
用 fast-check 实现,放在 `server/test/signup.properties.test.ts`。
|
|
35
|
+
|
|
36
|
+
## 缺陷追踪
|
|
37
|
+
|
|
38
|
+
每次缺陷在 `docs/07-质量与测试/defects/<%= it.feature.slug %>-bug-NNN.md`
|
|
39
|
+
立一份记录,关联回本测试计划。本特性提交后的第一个缺陷文件命名为
|
|
40
|
+
`<%= it.feature.slug %>-bug-001.md`。
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 测试计划:<%= it.feature.title %>
|
|
2
|
+
|
|
3
|
+
> 特性 slug:`<%= it.feature.slug %>`
|
|
4
|
+
> PRD:[docs/01-背景与需求/prd-<%= it.feature.slug %>.md](../../01-背景与需求/prd-<%= it.feature.slug %>.md)
|
|
5
|
+
|
|
6
|
+
## 覆盖矩阵
|
|
7
|
+
|
|
8
|
+
| 验收标准(PRD) | 测试类型 | 位置 |
|
|
9
|
+
|----------------|---------|------|
|
|
10
|
+
| 1. WHEN ... SHALL ... | 单元 | `server/test/<%= it.feature.slug %>.test.ts` |
|
|
11
|
+
| 2. IF ... THEN SHALL ... | 契约 | `contracts/tests/acceptance/<%= it.feature.slug %>.feature` |
|
|
12
|
+
| 3. WHILE ... SHALL ... | 端到端 | `web/e2e/<%= it.feature.slug %>.spec.ts` |
|
|
13
|
+
|
|
14
|
+
## 边界用例
|
|
15
|
+
|
|
16
|
+
- [ ] 空 / null 输入
|
|
17
|
+
- [ ] 边界值
|
|
18
|
+
- [ ] 并发 / 竞态(如适用)
|
|
19
|
+
|
|
20
|
+
## 缺陷追踪
|
|
21
|
+
|
|
22
|
+
每次缺陷在 `docs/07-质量与测试/defects/<%= it.feature.slug %>-bug-NNN.md`
|
|
23
|
+
立一份记录,关联回本测试计划。
|
|
@@ -145,6 +145,8 @@ mkdir server/apps/billing
|
|
|
145
145
|
|------|------|
|
|
146
146
|
| 治理 lint(本地) | `node tools/governance-lint/index.js`(如已加入仓库)|
|
|
147
147
|
| 单元测试 / 构建 | 见各模块 `package.json` / 工具栈说明 |
|
|
148
|
+
| 在已有项目里加特性 | `npx @wneng/create-keel feature add <slug>` |
|
|
149
|
+
| 创建带样例特性的项目 | `npx @wneng/create-keel create demo --yes --sample-feature` |
|
|
148
150
|
|
|
149
151
|
## D. 提 PR 前 60 秒自检
|
|
150
152
|
|