@robsun/create-keystone-app 0.2.13 → 0.4.0

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.
Files changed (88) hide show
  1. package/README.md +46 -43
  2. package/dist/create-keystone-app.js +347 -10
  3. package/dist/create-module.js +1219 -607
  4. package/package.json +22 -23
  5. package/template/.claude/skills/keystone-implement/SKILL.md +113 -0
  6. package/template/.claude/skills/keystone-implement/references/CHECKLIST.md +91 -0
  7. package/template/.claude/skills/keystone-implement/references/PATTERNS.md +1088 -0
  8. package/template/.claude/skills/keystone-implement/references/SCHEMA.md +135 -0
  9. package/template/.claude/skills/keystone-implement/references/TESTING.md +231 -0
  10. package/template/.claude/skills/keystone-requirements/SKILL.md +296 -0
  11. package/template/.claude/skills/keystone-requirements/references/CONFIRM_TEMPLATE.md +170 -0
  12. package/template/.claude/skills/keystone-requirements/references/SCHEMA.md +135 -0
  13. package/template/.eslintrc.js +3 -0
  14. package/template/.github/workflows/ci.yml +30 -0
  15. package/template/.github/workflows/release.yml +32 -0
  16. package/template/.golangci.yml +11 -0
  17. package/template/README.md +81 -73
  18. package/template/apps/server/README.md +8 -0
  19. package/template/apps/server/cmd/server/main.go +27 -185
  20. package/template/apps/server/config.example.yaml +31 -1
  21. package/template/apps/server/config.yaml +31 -1
  22. package/template/apps/server/go.mod +60 -18
  23. package/template/apps/server/go.sum +183 -31
  24. package/template/apps/server/internal/frontend/embed.go +3 -8
  25. package/template/apps/server/internal/modules/example/README.md +18 -0
  26. package/template/apps/server/internal/modules/example/api/handler/handler_test.go +9 -0
  27. package/template/apps/server/internal/modules/example/api/handler/item_handler.go +468 -165
  28. package/template/apps/server/internal/modules/example/bootstrap/seeds/item.go +217 -8
  29. package/template/apps/server/internal/modules/example/domain/models/item.go +40 -7
  30. package/template/apps/server/internal/modules/example/domain/service/approval_callback.go +68 -0
  31. package/template/apps/server/internal/modules/example/domain/service/approval_schema.go +41 -0
  32. package/template/apps/server/internal/modules/example/domain/service/errors.go +20 -22
  33. package/template/apps/server/internal/modules/example/domain/service/item_service.go +267 -7
  34. package/template/apps/server/internal/modules/example/domain/service/item_service_test.go +281 -0
  35. package/template/apps/server/internal/modules/example/i18n/keys.go +32 -20
  36. package/template/apps/server/internal/modules/example/i18n/locales/en-US.json +30 -18
  37. package/template/apps/server/internal/modules/example/i18n/locales/zh-CN.json +30 -18
  38. package/template/apps/server/internal/modules/example/infra/exporter/item_exporter.go +119 -0
  39. package/template/apps/server/internal/modules/example/infra/importer/item_importer.go +77 -0
  40. package/template/apps/server/internal/modules/example/infra/repository/item_repository.go +99 -49
  41. package/template/apps/server/internal/modules/example/module.go +171 -97
  42. package/template/apps/server/internal/modules/example/tests/integration_test.go +7 -0
  43. package/template/apps/server/internal/modules/manifest.go +7 -7
  44. package/template/apps/web/README.md +4 -2
  45. package/template/apps/web/package.json +1 -1
  46. package/template/apps/web/src/app.config.ts +8 -6
  47. package/template/apps/web/src/index.css +7 -3
  48. package/template/apps/web/src/main.tsx +2 -5
  49. package/template/apps/web/src/modules/example/help/en-US/faq.md +27 -0
  50. package/template/apps/web/src/modules/example/help/en-US/items.md +30 -0
  51. package/template/apps/web/src/modules/example/help/en-US/overview.md +31 -0
  52. package/template/apps/web/src/modules/example/help/zh-CN/faq.md +27 -0
  53. package/template/apps/web/src/modules/example/help/zh-CN/items.md +31 -0
  54. package/template/apps/web/src/modules/example/help/zh-CN/overview.md +32 -0
  55. package/template/apps/web/src/modules/example/locales/en-US/example.json +99 -32
  56. package/template/apps/web/src/modules/example/locales/zh-CN/example.json +85 -18
  57. package/template/apps/web/src/modules/example/pages/ExampleItemsPage.tsx +840 -237
  58. package/template/apps/web/src/modules/example/services/exampleItems.ts +79 -8
  59. package/template/apps/web/src/modules/example/types.ts +14 -1
  60. package/template/apps/web/src/modules/index.ts +1 -0
  61. package/template/apps/web/vite.config.ts +9 -3
  62. package/template/docs/CONVENTIONS.md +10 -7
  63. package/template/package.json +4 -5
  64. package/template/pnpm-lock.yaml +76 -5
  65. package/template/scripts/build.bat +15 -3
  66. package/template/scripts/build.sh +9 -3
  67. package/template/scripts/check-help.js +249 -0
  68. package/template/scripts/compress-assets.js +89 -0
  69. package/template/scripts/test.bat +23 -0
  70. package/template/scripts/test.sh +16 -0
  71. package/template/.claude/skills/keystone-dev/SKILL.md +0 -103
  72. package/template/.claude/skills/keystone-dev/references/APPROVAL.md +0 -121
  73. package/template/.claude/skills/keystone-dev/references/CAPABILITIES.md +0 -261
  74. package/template/.claude/skills/keystone-dev/references/TEMPLATES.md +0 -532
  75. package/template/.claude/skills/keystone-dev/references/TESTING.md +0 -44
  76. package/template/.codex/skills/keystone-dev/SKILL.md +0 -103
  77. package/template/.codex/skills/keystone-dev/references/APPROVAL.md +0 -121
  78. package/template/.codex/skills/keystone-dev/references/CAPABILITIES.md +0 -261
  79. package/template/.codex/skills/keystone-dev/references/TEMPLATES.md +0 -532
  80. package/template/.codex/skills/keystone-dev/references/TESTING.md +0 -44
  81. package/template/apps/server/internal/app/routes/module_routes.go +0 -16
  82. package/template/apps/server/internal/app/routes/routes.go +0 -226
  83. package/template/apps/server/internal/app/startup/startup.go +0 -74
  84. package/template/apps/server/internal/frontend/handler.go +0 -122
  85. package/template/apps/server/internal/modules/registry.go +0 -145
  86. package/template/apps/web/src/modules/example/help/faq.md +0 -23
  87. package/template/apps/web/src/modules/example/help/items.md +0 -26
  88. package/template/apps/web/src/modules/example/help/overview.md +0 -25
@@ -0,0 +1,135 @@
1
+ # Keystone Requirement Document Schema v1.1
2
+
3
+ > 需求收集与开发实现之间的交接契约。
4
+ > 此格式是两个 skill 之间唯一的交接文件标准。
5
+
6
+ ## 文件命名
7
+
8
+ ```
9
+ requirements/REQ-{序号}-{module-name}.yaml
10
+ ```
11
+
12
+ 示例: `requirements/REQ-002-order-management.yaml`
13
+
14
+ ## 完整 Schema
15
+
16
+ ```yaml
17
+ version: "1.1" # 必需: Schema 版本
18
+ type: module # 必需: 文档类型 (module | feature | bugfix)
19
+ completeness: "draft" # 必需: draft | review | approved
20
+
21
+ metadata:
22
+ name: "module-name" # 必需: kebab-case 模块名
23
+ chinese_name: "模块中文名" # 必需: 用于 UI 显示
24
+ description: "模块描述" # 必需: 一句话描述
25
+ author: "作者" # 必需: 需求提出者
26
+ created_at: "YYYY-MM-DD" # 必需: 创建日期
27
+ priority: "medium" # 必需: low | medium | high | critical
28
+
29
+ narrative:
30
+ summary: "业务摘要" # 必需: 1-2 句话概述
31
+ background: "背景说明" # 必需: 现状和痛点
32
+ problem: "问题陈述" # 必需: 需要解决的核心问题
33
+ goals: # 必需: 期望达成的目标
34
+ - "目标 1"
35
+ - "目标 2"
36
+ non_goals: # 可选: 明确不做的事项
37
+ - "不做的事项 1"
38
+ user_story: "As a..., I want..., so that..." # 必需: 用户故事
39
+ workflow: "操作流程描述" # 必需: 用户操作流程
40
+ acceptance_criteria: # 必需: 验收标准
41
+ - "验收条件 1"
42
+ - "验收条件 2"
43
+ open_questions: # 可选: 待确认问题
44
+ - "待确认问题 1"
45
+
46
+ module:
47
+ name: "module" # 必需: Go package 名 (小写, 无连字符)
48
+ resource:
49
+ singular: "entity" # 必需: 单数资源名 (小写)
50
+ plural: "entities" # 必需: 复数资源名 (小写)
51
+
52
+ entities: # 必需: 实体定义列表
53
+ - name: "EntityName" # 必需: PascalCase 实体名
54
+ chinese_name: "实体中文名" # 必需: 用于 UI 显示
55
+ description: "实体描述" # 可选: 实体说明
56
+ fields: # 必需: 字段列表
57
+ - name: "fieldName" # 必需: camelCase 或 snake_case
58
+ type: "string" # 必需: 见下方类型说明
59
+ chinese_name: "字段中文名" # 必需: 用于表单/表格标签
60
+ required: true # 必需: 是否必填
61
+ unique: false # 可选: 是否唯一
62
+ default: null # 可选: 默认值
63
+ values: [] # 条件: type=enum 时必需
64
+ validation: # 可选: 验证规则
65
+ min_length: null
66
+ max_length: null
67
+ min: null
68
+ max: null
69
+ pattern: ""
70
+
71
+ features:
72
+ with_crud: true # 必需: 基础 CRUD 功能
73
+ with_approval: false # 必需: 审批流程
74
+ with_import: false # 必需: 批量导入
75
+ with_export: false # 必需: 批量导出
76
+
77
+ assumptions: # 可选: 开发假设
78
+ - "假设说明 1"
79
+ ```
80
+
81
+ ## 字段类型
82
+
83
+ | 类型 | Go 类型 | TS 类型 | GORM 标签 | 说明 |
84
+ |------|---------|---------|-----------|------|
85
+ | `string` | `string` | `string` | `gorm:"size:200"` | 短文本 |
86
+ | `text` | `string` | `string` | `gorm:"type:text"` | 长文本 |
87
+ | `int` | `int` | `number` | - | 整数 |
88
+ | `uint` | `uint` | `number` | - | 无符号整数 |
89
+ | `float` | `float64` | `number` | - | 浮点数 |
90
+ | `bool` | `bool` | `boolean` | - | 布尔值 |
91
+ | `enum` | `string` (自定义类型) | `string` 联合类型 | `gorm:"size:20"` | 枚举 |
92
+ | `datetime` | `time.Time` | `string` | - | 日期时间 |
93
+ | `date` | `time.Time` | `string` | - | 仅日期 |
94
+ | `json` | `datatypes.JSON` | `object` | `gorm:"type:json"` | JSON 对象 |
95
+
96
+ ## 特殊字段约定
97
+
98
+ 以下字段由框架自动处理,无需在 entities 中定义:
99
+
100
+ | 字段 | 说明 |
101
+ |------|------|
102
+ | `id` | 主键,自动生成 |
103
+ | `tenant_id` | 租户 ID,自动注入 |
104
+ | `created_at` | 创建时间,自动设置 |
105
+ | `updated_at` | 更新时间,自动更新 |
106
+ | `created_by` | 创建人 ID(可选) |
107
+ | `updated_by` | 更新人 ID(可选) |
108
+
109
+ ## 审批流特殊字段
110
+
111
+ 当 `features.with_approval: true` 时,自动添加:
112
+
113
+ | 字段 | 说明 |
114
+ |------|------|
115
+ | `status` | 扩展为 draft/pending/approved/rejected |
116
+ | `approval_instance_id` | 审批实例 ID |
117
+ | `reject_reason` | 拒绝原因 |
118
+
119
+ ## Completeness 状态说明
120
+
121
+ | 状态 | 说明 | 允许操作 |
122
+ |------|------|----------|
123
+ | `draft` | 草稿,需求收集中 | 可继续编辑完善 |
124
+ | `review` | 评审中,等待确认 | 仅允许小修改 |
125
+ | `approved` | 已批准,可进入开发 | keystone-implement 可执行 |
126
+
127
+ ## 验证规则
128
+
129
+ 需求文档必须满足以下条件才能进入开发:
130
+
131
+ 1. `completeness` 必须为 `approved`
132
+ 2. 所有 `必需` 字段必须有值
133
+ 3. 每个 entity 至少有一个业务字段(不含 id/tenant_id 等)
134
+ 4. `values` 数组在 `type: enum` 时必须非空
135
+ 5. `open_questions` 应为空或已全部解决
@@ -0,0 +1,231 @@
1
+ # Keystone 测试指南
2
+
3
+ ---
4
+
5
+ ## 运行命令
6
+
7
+ ```bash
8
+ # 后端全量测试
9
+ go test ./...
10
+
11
+ # 后端指定模块
12
+ go test ./tests/unit/modules/{module}/...
13
+
14
+ # 前端测试
15
+ pnpm -C apps/web test
16
+
17
+ # 前端指定文件
18
+ pnpm -C apps/web test -- {module}
19
+ ```
20
+
21
+ ---
22
+
23
+ ## 后端单元测试
24
+
25
+ 位置:`tests/unit/modules/{module}/service_test.go`
26
+
27
+ ```go
28
+ package service_test
29
+
30
+ import (
31
+ "context"
32
+ "testing"
33
+
34
+ "github.com/stretchr/testify/assert"
35
+ "github.com/stretchr/testify/mock"
36
+
37
+ "your-app/apps/server/internal/modules/{module}/domain/models"
38
+ "your-app/apps/server/internal/modules/{module}/domain/service"
39
+ )
40
+
41
+ type MockRepository struct {
42
+ mock.Mock
43
+ }
44
+
45
+ func (m *MockRepository) Create(ctx context.Context, entity *models.Entity) error {
46
+ args := m.Called(ctx, entity)
47
+ return args.Error(0)
48
+ }
49
+
50
+ func TestService_Create(t *testing.T) {
51
+ tests := []struct {
52
+ name string
53
+ input service.CreateInput
54
+ wantErr bool
55
+ errType error
56
+ }{
57
+ {
58
+ name: "success",
59
+ input: service.CreateInput{Name: "Test", Status: models.StatusActive},
60
+ wantErr: false,
61
+ },
62
+ {
63
+ name: "empty name",
64
+ input: service.CreateInput{Name: "", Status: models.StatusActive},
65
+ wantErr: true,
66
+ errType: service.ErrNameRequired,
67
+ },
68
+ {
69
+ name: "invalid status",
70
+ input: service.CreateInput{Name: "Test", Status: "invalid"},
71
+ wantErr: true,
72
+ errType: service.ErrInvalidStatus,
73
+ },
74
+ }
75
+
76
+ for _, tt := range tests {
77
+ t.Run(tt.name, func(t *testing.T) {
78
+ mockRepo := new(MockRepository)
79
+ if !tt.wantErr {
80
+ mockRepo.On("Create", mock.Anything, mock.Anything).Return(nil)
81
+ }
82
+
83
+ svc := service.NewService(mockRepo)
84
+ _, err := svc.Create(context.Background(), 1, tt.input)
85
+
86
+ if tt.wantErr {
87
+ assert.Error(t, err)
88
+ if tt.errType != nil {
89
+ assert.ErrorIs(t, err, tt.errType)
90
+ }
91
+ } else {
92
+ assert.NoError(t, err)
93
+ mockRepo.AssertExpectations(t)
94
+ }
95
+ })
96
+ }
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 后端集成测试
103
+
104
+ 位置:`tests/integration/modules/{module}/api_test.go`
105
+
106
+ ```go
107
+ package api_test
108
+
109
+ import (
110
+ "net/http"
111
+ "net/http/httptest"
112
+ "strings"
113
+ "testing"
114
+
115
+ "github.com/stretchr/testify/assert"
116
+ )
117
+
118
+ func TestAPI_CRUD(t *testing.T) {
119
+ app := setupTestApp(t)
120
+
121
+ t.Run("Create", func(t *testing.T) {
122
+ body := `{"name": "Test", "status": "active"}`
123
+ req := httptest.NewRequest("POST", "/api/v1/{module}/items", strings.NewReader(body))
124
+ req.Header.Set("Content-Type", "application/json")
125
+ req.Header.Set("Authorization", "Bearer "+testToken)
126
+
127
+ resp := httptest.NewRecorder()
128
+ app.ServeHTTP(resp, req)
129
+
130
+ assert.Equal(t, http.StatusCreated, resp.Code)
131
+ })
132
+
133
+ t.Run("List", func(t *testing.T) {
134
+ req := httptest.NewRequest("GET", "/api/v1/{module}/items", nil)
135
+ req.Header.Set("Authorization", "Bearer "+testToken)
136
+
137
+ resp := httptest.NewRecorder()
138
+ app.ServeHTTP(resp, req)
139
+
140
+ assert.Equal(t, http.StatusOK, resp.Code)
141
+ })
142
+
143
+ t.Run("Update", func(t *testing.T) {
144
+ body := `{"name": "Updated"}`
145
+ req := httptest.NewRequest("PATCH", "/api/v1/{module}/items/1", strings.NewReader(body))
146
+ req.Header.Set("Content-Type", "application/json")
147
+ req.Header.Set("Authorization", "Bearer "+testToken)
148
+
149
+ resp := httptest.NewRecorder()
150
+ app.ServeHTTP(resp, req)
151
+
152
+ assert.Equal(t, http.StatusOK, resp.Code)
153
+ })
154
+
155
+ t.Run("Delete", func(t *testing.T) {
156
+ req := httptest.NewRequest("DELETE", "/api/v1/{module}/items/1", nil)
157
+ req.Header.Set("Authorization", "Bearer "+testToken)
158
+
159
+ resp := httptest.NewRecorder()
160
+ app.ServeHTTP(resp, req)
161
+
162
+ assert.Equal(t, http.StatusOK, resp.Code)
163
+ })
164
+
165
+ t.Run("Unauthorized", func(t *testing.T) {
166
+ req := httptest.NewRequest("GET", "/api/v1/{module}/items", nil)
167
+ resp := httptest.NewRecorder()
168
+ app.ServeHTTP(resp, req)
169
+
170
+ assert.Equal(t, http.StatusUnauthorized, resp.Code)
171
+ })
172
+ }
173
+ ```
174
+
175
+ ---
176
+
177
+ ## 前端测试
178
+
179
+ 位置:`apps/web/src/modules/{module}/__tests__/`
180
+
181
+ ```tsx
182
+ import { render, screen, waitFor, fireEvent } from '@testing-library/react'
183
+ import { describe, it, expect, vi } from 'vitest'
184
+ import { EntityPage } from '../pages/EntityPage'
185
+
186
+ vi.mock('../services/api', () => ({
187
+ listEntities: vi.fn().mockResolvedValue([
188
+ { id: 1, name: 'Test', status: 'active' }
189
+ ]),
190
+ createEntity: vi.fn().mockResolvedValue({ id: 2, name: 'New' }),
191
+ updateEntity: vi.fn().mockResolvedValue({ id: 1, name: 'Updated' }),
192
+ deleteEntity: vi.fn().mockResolvedValue(undefined),
193
+ }))
194
+
195
+ describe('EntityPage', () => {
196
+ it('renders list', async () => {
197
+ render(<EntityPage />)
198
+ await waitFor(() => {
199
+ expect(screen.getByText('Test')).toBeInTheDocument()
200
+ })
201
+ })
202
+
203
+ it('opens create modal', async () => {
204
+ render(<EntityPage />)
205
+ fireEvent.click(screen.getByText('新建'))
206
+ await waitFor(() => {
207
+ expect(screen.getByRole('dialog')).toBeInTheDocument()
208
+ })
209
+ })
210
+
211
+ it('validates required fields', async () => {
212
+ render(<EntityPage />)
213
+ fireEvent.click(screen.getByText('新建'))
214
+ fireEvent.click(screen.getByText('确定'))
215
+ await waitFor(() => {
216
+ expect(screen.getByText('名称不能为空')).toBeInTheDocument()
217
+ })
218
+ })
219
+ })
220
+ ```
221
+
222
+ ---
223
+
224
+ ## 覆盖要求
225
+
226
+ | 层级 | 必须测试 | 建议测试 |
227
+ |------|----------|----------|
228
+ | Service | 公开方法、错误路径、边界条件 | 并发场景 |
229
+ | API 集成 | CRUD 端点、权限检查、输入验证 | 分页、过滤、排序 |
230
+ | Repository | 基本 CRUD | 复杂查询、事务 |
231
+ | 前端 | 页面渲染、表单验证 | 交互流程、错误处理 |
@@ -0,0 +1,296 @@
1
+ ---
2
+ name: keystone-requirements
3
+ description: 当用户想要添加新功能、新模块、新业务时,使用此技能收集需求。通过逐步对话将模糊想法转化为标准化 YAML 需求文档,作为开发的唯一契约。触发词:添加功能、新模块、新增、我想做、客户管理、订单管理等业务模块。
4
+ ---
5
+
6
+ # Keystone Requirements
7
+
8
+ ## 技能概述
9
+
10
+ ### 核心职责
11
+
12
+ 1. 引导用户发现和表达业务需求
13
+ 2. 将模糊想法转化为结构化需求
14
+ 3. 生成符合 v1.1 schema 的 YAML 文档
15
+ 4. 验证需求完整性和可实现性
16
+
17
+ ### 输出物
18
+
19
+ **唯一输出**: `requirements/REQ-{序号}-{module-name}.yaml`
20
+
21
+ ### 引用文档
22
+
23
+ - `references/SCHEMA.md` — YAML 格式定义
24
+ - `references/CONFIRM_TEMPLATE.md` — 需求确认模板
25
+
26
+ ---
27
+
28
+ ## 工作流程
29
+
30
+ ```
31
+ ┌─────────────────────────────────────────────────────────────┐
32
+ │ Phase 0: 项目理解 │
33
+ │ ↓ 了解现有模块、关联关系、命名约定 │
34
+ ├─────────────────────────────────────────────────────────────┤
35
+ │ Phase 1: 需求收集 │
36
+ │ ↓ 通过 6 个核心问题收集关键业务信息 │
37
+ ├─────────────────────────────────────────────────────────────┤
38
+ │ Phase 2: 智能填充 │
39
+ │ ↓ 自动填充固定字段 + 推测填充其他必需字段 │
40
+ ├─────────────────────────────────────────────────────────────┤
41
+ │ Phase 3: 用户确认 │
42
+ │ ↓ 按模板展示完整需求,用户确认或修改 │
43
+ ├─────────────────────────────────────────────────────────────┤
44
+ │ Phase 4: 保存文档 │
45
+ │ → 生成 YAML 文件,交付给 keystone-implement skill │
46
+ └─────────────────────────────────────────────────────────────┘
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Phase 0: 项目理解
52
+
53
+ **目的**:在提问前了解项目上下文,提出更有针对性的问题。
54
+
55
+ ### 步骤 1: 检查现有需求
56
+
57
+ ```bash
58
+ ls requirements/
59
+ ```
60
+
61
+ - 了解已有哪些模块
62
+ - 识别命名模式和编号规则(确定下一个序号)
63
+ - 理解业务领域
64
+
65
+ ### 步骤 2: 阅读相关需求
66
+
67
+ 如果用户的需求与现有模块相关(如:订单管理 ↔ 客户管理),阅读相关 YAML:
68
+
69
+ ```bash
70
+ cat requirements/REQ-XXX-xxx.yaml
71
+ ```
72
+
73
+ **关注**:实体关系、字段命名风格、业务流程模式
74
+
75
+ ### 步骤 3: 理解项目结构
76
+
77
+ ```bash
78
+ ls apps/server/internal/modules/
79
+ ```
80
+
81
+ **了解**:现有模块列表、代码组织方式
82
+
83
+ ### 步骤 4: 提炼上下文
84
+
85
+ 准备以下信息用于后续提问:
86
+ - **类似模块**:可参考的现有设计
87
+ - **关联实体**:可能需要关联的现有实体
88
+ - **命名约定**:项目的命名风格
89
+
90
+ ---
91
+
92
+ ## Phase 1: 需求收集
93
+
94
+ **交互方式**:使用 `AskUserQuestion` 工具逐步询问,每个问题提供 2-4 个常见选项。
95
+
96
+ ### Q1: 业务问题
97
+
98
+ **问题**: "这个功能主要解决什么业务问题?"
99
+
100
+ ```javascript
101
+ {
102
+ question: "这个功能主要解决什么业务问题?",
103
+ header: "业务问题",
104
+ options: [
105
+ { label: "数据分散难管理", description: "如:Excel 到处都是,信息不同步" },
106
+ { label: "流程不规范", description: "如:审批靠口头,没有留痕" },
107
+ { label: "信息不透明", description: "如:不知道进度,查询困难" },
108
+ { label: "效率低下", description: "如:重复手工操作,容易出错" }
109
+ ]
110
+ }
111
+ ```
112
+
113
+ **收集信息**: `narrative.problem`, `narrative.background`
114
+
115
+ ---
116
+
117
+ ### Q2: 目标用户
118
+
119
+ **问题**: "主要使用者是谁?"
120
+
121
+ ```javascript
122
+ {
123
+ question: "主要使用者是谁?",
124
+ header: "目标用户",
125
+ options: [
126
+ { label: "管理层", description: "查看报表、做决策" },
127
+ { label: "业务人员", description: "日常操作、录入数据" },
128
+ { label: "财务/行政", description: "审核、归档" },
129
+ { label: "技术人员", description: "系统对接" }
130
+ ]
131
+ }
132
+ ```
133
+
134
+ **收集信息**: 用于生成 `narrative.user_story`
135
+
136
+ ---
137
+
138
+ ### Q3: 核心资源
139
+
140
+ **问题**: "你要管理的核心对象是什么?"
141
+
142
+ **引导示例**:
143
+ - 客户关系管理 → 客户 (Customer)
144
+ - 订单处理 → 订单 (Order)
145
+ - 库存管理 → 商品 (Product)
146
+
147
+ **直接询问**:
148
+ 1. "核心对象的中文名称是?(如:客户、订单)"
149
+ 2. "对应的英文单复数是?(如:customer/customers)"
150
+
151
+ **收集信息**: `metadata.name`, `metadata.chinese_name`, `module.name`, `module.resource`
152
+
153
+ ---
154
+
155
+ ### Q4: 关键属性
156
+
157
+ **Step 1**: 列举字段
158
+ "这个资源需要记录哪些信息?(如:客户的名称、联系方式、地址等)"
159
+
160
+ **Step 2**: 逐个确认类型
161
+
162
+ ```javascript
163
+ {
164
+ question: "{字段名} 的数据类型是?",
165
+ header: "字段类型",
166
+ options: [
167
+ { label: "文本", description: "姓名、标题等短文本" },
168
+ { label: "整数", description: "数量、年龄等" },
169
+ { label: "小数", description: "价格、金额等" },
170
+ { label: "日期", description: "创建时间、截止日期等" },
171
+ { label: "枚举/选项", description: "状态、类型等固定选项" },
172
+ { label: "布尔值", description: "是/否" }
173
+ ]
174
+ }
175
+ ```
176
+
177
+ **Step 3**: 如果是枚举,追问可选值
178
+ "有哪些可选值?(如:草稿、待审核、已完成)"
179
+
180
+ **收集信息**: `entities[].fields[]`
181
+
182
+ ---
183
+
184
+ ### Q5: 功能范围
185
+
186
+ **问题**: "需要哪些功能?(可多选)"
187
+
188
+ ```javascript
189
+ {
190
+ question: "需要哪些功能?(可多选)",
191
+ header: "功能",
192
+ multiSelect: true,
193
+ options: [
194
+ { label: "基础 CRUD", description: "增删改查列表" },
195
+ { label: "审批流程", description: "提交审批、审批通过/拒绝" },
196
+ { label: "批量导入", description: "Excel 导入" },
197
+ { label: "数据导出", description: "导出为 Excel" }
198
+ ]
199
+ }
200
+ ```
201
+
202
+ **收集信息**: `features.*`
203
+
204
+ ---
205
+
206
+ ### Q6: 验收标准
207
+
208
+ **问题**: "这个功能上线后,如何验证它是可用的?"
209
+
210
+ **引导示例**:
211
+ - 能成功创建并查看 XX 信息
212
+ - 审批流程能正常流转
213
+ - 导入 100 条数据不出错
214
+
215
+ **收集信息**: `narrative.acceptance_criteria[]`
216
+
217
+ ---
218
+
219
+ ## Phase 2: 智能填充
220
+
221
+ ### 自动填充(固定值)
222
+
223
+ | 字段 | 值 |
224
+ |------|-----|
225
+ | `version` | `"1.1"` |
226
+ | `type` | `"module"` |
227
+ | `completeness` | `"approved"`(用户确认后) |
228
+ | `metadata.author` | `"system"` |
229
+ | `created_at` | 当前日期 `YYYY-MM-DD` |
230
+ | `priority` | `"medium"`(除非用户提到紧急程度) |
231
+
232
+ ### 推测填充(基于收集信息)
233
+
234
+ | 字段 | 推测来源 |
235
+ |------|----------|
236
+ | `narrative.summary` | 从 `chinese_name` 生成,如 "客户信息管理系统" |
237
+ | `narrative.description` | 从 `chinese_name` + `problem` 组合 |
238
+ | `narrative.background` | 从 Q1 回答提取 |
239
+ | `narrative.problem` | 从 Q1 回答提取 |
240
+ | `narrative.goals` | 从功能范围推导 |
241
+ | `narrative.workflow` | 从功能和角色推导 |
242
+ | `narrative.user_story` | 标准格式:As a {角色}, I want {功能}, so that {价值} |
243
+
244
+ ### 优先级调整
245
+
246
+ - 用户提到"紧急"、"马上" → `priority: "high"`
247
+ - 用户提到"不急"、"有空" → `priority: "low"`
248
+
249
+ ---
250
+
251
+ ## Phase 3: 用户确认
252
+
253
+ ### 确认模板
254
+
255
+ **必须严格按 `references/CONFIRM_TEMPLATE.md` 模板展示**,包含:
256
+
257
+ 1. **元信息** — 模块名称、资源标识、作者、优先级、日期
258
+ 2. **业务背景** — 问题、背景、用户、价值
259
+ 3. **数据模型** — 实体名称、字段表格(名称、中文名、类型、必填、说明)
260
+ 4. **功能范围** — CRUD、审批、导入、导出的勾选状态
261
+ 5. **验收标准** — 编号列表
262
+ 6. **关联信息** — 与现有模块的关联(如有)
263
+
264
+ ### 用户响应处理
265
+
266
+ - **确认** → 进入 Phase 4
267
+ - **需要修改** → 根据反馈调整,重新展示确认单
268
+
269
+ ---
270
+
271
+ ## Phase 4: 保存文档
272
+
273
+ ### 保存步骤
274
+
275
+ 1. **分配序号**:检查 `requirements/` 目录,使用 N+1
276
+ 2. **生成文件名**:`REQ-{序号}-{module-name}.yaml`
277
+ 3. **写入文件**:按 `references/SCHEMA.md` 格式生成 YAML
278
+ 4. **提示下一步**:`"需求文档已保存为 requirements/REQ-00X-xxx.yaml,可直接交给 keystone-implement skill 开发"`
279
+
280
+ ### 保存前验证
281
+
282
+ **必需字段** (24 个):
283
+
284
+ | 分类 | 字段 |
285
+ |------|------|
286
+ | 元数据 | `version`, `type`, `completeness` |
287
+ | metadata | `name`, `chinese_name`, `description`, `author`, `created_at`, `priority` |
288
+ | narrative | `summary`, `background`, `problem`, `goals[]`, `user_story`, `workflow`, `acceptance_criteria[]` |
289
+ | module | `name`, `resource.singular`, `resource.plural` |
290
+ | entities | 至少 1 个实体,每个实体有 `name` 和至少 1 个业务字段 |
291
+ | features | `with_crud`, `with_approval` |
292
+
293
+ **字段级验证**:
294
+ - enum 类型字段必须有 `values`
295
+ - 所有字段必须有 `chinese_name`
296
+ - 所有字段必须有 `required` 属性