sdd-skills 1.0.1 → 1.1.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.
- package/README.md +5 -5
- package/docs/workflow-guide.md +22 -12
- package/package.json +1 -1
- package/references/backend-review-guide.md +363 -0
- package/references/frontend-review-guide.md +504 -0
- package/references/sae-review-guide.md +213 -0
- package/references/tester-review-guide.md +459 -0
- package/skills/backend-engineer/SKILL.md +22 -3
- package/skills/frontend-engineer/SKILL.md +45 -12
- package/skills/sae/SKILL.md +26 -3
- package/skills/tester/SKILL.md +23 -4
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
# Frontend Engineer OpenSpec 评审指南
|
|
2
|
+
|
|
3
|
+
## 何时需要评审
|
|
4
|
+
|
|
5
|
+
**仅在以下情况下加载此评审指南**:
|
|
6
|
+
- Frontend OpenSpec 已生成,需要评审验证
|
|
7
|
+
- 用户明确要求对前端 OpenSpec 进行评审
|
|
8
|
+
- 代码实现完成,需要对照 OpenSpec 进行符合性评审
|
|
9
|
+
|
|
10
|
+
## 评审目标
|
|
11
|
+
|
|
12
|
+
确保前端 OpenSpec 的完整性、正确性、可实施性,验证代码实现符合 OpenSpec 要求和用户体验标准。
|
|
13
|
+
|
|
14
|
+
## 评审检查清单
|
|
15
|
+
|
|
16
|
+
### 1. 组件设计评审
|
|
17
|
+
|
|
18
|
+
- [ ] **组件划分**: 组件粒度是否合理?职责是否单一?
|
|
19
|
+
- [ ] **可复用性**: 组件是否可复用?是否过度抽象?
|
|
20
|
+
- [ ] **Props 设计**: Props 是否有完整的类型定义?
|
|
21
|
+
- [ ] **Emits 定义**: 事件是否明确定义?
|
|
22
|
+
- [ ] **组件命名**: 是否使用 PascalCase?命名是否语义化?
|
|
23
|
+
|
|
24
|
+
**Vue 组件检查**:
|
|
25
|
+
```vue
|
|
26
|
+
<!-- ✅ 正确:完整的类型定义 -->
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
interface Props {
|
|
29
|
+
userId: number
|
|
30
|
+
name: string
|
|
31
|
+
email?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface Emits {
|
|
35
|
+
(e: 'update', user: User): void
|
|
36
|
+
(e: 'delete', id: number): void
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const props = defineProps<Props>()
|
|
40
|
+
const emit = defineEmits<Emits>()
|
|
41
|
+
</script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**React 组件检查**:
|
|
45
|
+
```typescript
|
|
46
|
+
// ✅ 正确:完整的类型定义
|
|
47
|
+
interface UserCardProps {
|
|
48
|
+
userId: number
|
|
49
|
+
name: string
|
|
50
|
+
email?: string
|
|
51
|
+
onUpdate: (user: User) => void
|
|
52
|
+
onDelete: (id: number) => void
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const UserCard: React.FC<UserCardProps> = ({ userId, name, email, onUpdate, onDelete }) => {
|
|
56
|
+
// ...
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. 状态管理评审
|
|
61
|
+
|
|
62
|
+
**Vue (Pinia) 检查**:
|
|
63
|
+
- [ ] **Store 设计**: Store 划分是否合理?是否按模块组织?
|
|
64
|
+
- [ ] **State 定义**: 状态是否有完整类型?
|
|
65
|
+
- [ ] **Actions**: 异步操作是否在 actions 中?
|
|
66
|
+
- [ ] **Getters**: 是否使用 computed 而非重复计算?
|
|
67
|
+
- [ ] **持久化**: 是否需要状态持久化(localStorage)?
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// ✅ 正确的 Pinia Store
|
|
71
|
+
export const useUserStore = defineStore('user', () => {
|
|
72
|
+
// State
|
|
73
|
+
const users = ref<User[]>([])
|
|
74
|
+
const currentUser = ref<User | null>(null)
|
|
75
|
+
|
|
76
|
+
// Getters
|
|
77
|
+
const activeUsers = computed(() => users.value.filter(u => u.active))
|
|
78
|
+
|
|
79
|
+
// Actions
|
|
80
|
+
async function fetchUsers() {
|
|
81
|
+
const { data } = await api.get<User[]>('/users')
|
|
82
|
+
users.value = data
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { users, currentUser, activeUsers, fetchUsers }
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**React (Redux Toolkit / Zustand) 检查**:
|
|
90
|
+
- [ ] **Slice 设计**: Redux slice 划分是否合理?
|
|
91
|
+
- [ ] **Immutability**: 是否保持状态不可变?
|
|
92
|
+
- [ ] **Selectors**: 是否使用 reselect 避免重复计算?
|
|
93
|
+
- [ ] **Async Thunks**: 异步操作是否正确处理?
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// ✅ 正确的 Redux Slice
|
|
97
|
+
const userSlice = createSlice({
|
|
98
|
+
name: 'user',
|
|
99
|
+
initialState: { users: [], loading: false },
|
|
100
|
+
reducers: {
|
|
101
|
+
setUsers: (state, action) => {
|
|
102
|
+
state.users = action.payload
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
extraReducers: (builder) => {
|
|
106
|
+
builder
|
|
107
|
+
.addCase(fetchUsers.pending, (state) => {
|
|
108
|
+
state.loading = true
|
|
109
|
+
})
|
|
110
|
+
.addCase(fetchUsers.fulfilled, (state, action) => {
|
|
111
|
+
state.users = action.payload
|
|
112
|
+
state.loading = false
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 3. 路由设计评审
|
|
119
|
+
|
|
120
|
+
- [ ] **路由结构**: 路由层级是否清晰?嵌套是否合理?
|
|
121
|
+
- [ ] **路由守卫**: 是否有认证守卫?权限控制是否完善?
|
|
122
|
+
- [ ] **懒加载**: 是否使用路由级代码分割?
|
|
123
|
+
- [ ] **元信息**: 是否定义页面标题、面包屑等元信息?
|
|
124
|
+
- [ ] **404 处理**: 是否有 404 页面?
|
|
125
|
+
|
|
126
|
+
**Vue Router 检查**:
|
|
127
|
+
```typescript
|
|
128
|
+
// ✅ 正确的路由配置
|
|
129
|
+
const routes = [
|
|
130
|
+
{
|
|
131
|
+
path: '/dashboard',
|
|
132
|
+
component: () => import('@/views/Dashboard.vue'), // 懒加载
|
|
133
|
+
meta: { requiresAuth: true, title: 'Dashboard' }
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
path: '/:pathMatch(.*)*',
|
|
137
|
+
component: () => import('@/views/NotFound.vue')
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 4. UI/UX 评审
|
|
143
|
+
|
|
144
|
+
- [ ] **响应式设计**: 是否适配移动端和桌面端?
|
|
145
|
+
- [ ] **加载状态**: 是否有 loading 指示器?
|
|
146
|
+
- [ ] **错误提示**: 是否有友好的错误提示?
|
|
147
|
+
- [ ] **空状态**: 列表为空时是否有提示?
|
|
148
|
+
- [ ] **交互反馈**: 按钮点击、表单提交是否有反馈?
|
|
149
|
+
- [ ] **可访问性**: 是否考虑了 a11y(键盘导航、ARIA 属性)?
|
|
150
|
+
|
|
151
|
+
**交互反馈示例**:
|
|
152
|
+
```vue
|
|
153
|
+
<template>
|
|
154
|
+
<button @click="handleSubmit" :disabled="loading">
|
|
155
|
+
<span v-if="loading">提交中...</span>
|
|
156
|
+
<span v-else>提交</span>
|
|
157
|
+
</button>
|
|
158
|
+
|
|
159
|
+
<div v-if="users.length === 0" class="empty-state">
|
|
160
|
+
<p>暂无数据</p>
|
|
161
|
+
</div>
|
|
162
|
+
</template>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 5. 性能评审
|
|
166
|
+
|
|
167
|
+
- [ ] **组件懒加载**: 是否使用 `defineAsyncComponent` / `React.lazy`?
|
|
168
|
+
- [ ] **列表优化**: 大列表是否使用虚拟滚动?
|
|
169
|
+
- [ ] **图片优化**: 是否使用懒加载?是否压缩?
|
|
170
|
+
- [ ] **防抖节流**: 搜索、滚动是否使用防抖节流?
|
|
171
|
+
- [ ] **Memo 优化**: 是否使用 `computed` / `useMemo` 避免重复计算?
|
|
172
|
+
|
|
173
|
+
**性能优化示例**:
|
|
174
|
+
```vue
|
|
175
|
+
<!-- Vue: 异步组件 -->
|
|
176
|
+
<script setup>
|
|
177
|
+
const HeavyComponent = defineAsyncComponent(() =>
|
|
178
|
+
import('./HeavyComponent.vue')
|
|
179
|
+
)
|
|
180
|
+
</script>
|
|
181
|
+
|
|
182
|
+
<!-- 虚拟滚动 -->
|
|
183
|
+
<script setup>
|
|
184
|
+
import { useVirtualList } from '@vueuse/core'
|
|
185
|
+
|
|
186
|
+
const { list, containerProps, wrapperProps } = useVirtualList(
|
|
187
|
+
largeList,
|
|
188
|
+
{ itemHeight: 50 }
|
|
189
|
+
)
|
|
190
|
+
</script>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// React: useMemo
|
|
195
|
+
const expensiveValue = useMemo(() => {
|
|
196
|
+
return computeExpensiveValue(a, b)
|
|
197
|
+
}, [a, b])
|
|
198
|
+
|
|
199
|
+
// React.memo 避免不必要的渲染
|
|
200
|
+
const MemoizedComponent = React.memo(MyComponent)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 6. 安全性评审
|
|
204
|
+
|
|
205
|
+
- [ ] **XSS 防护**: 是否避免使用 `v-html` / `dangerouslySetInnerHTML`?
|
|
206
|
+
- [ ] **输入验证**: 表单输入是否有前端验证?
|
|
207
|
+
- [ ] **Token 管理**: JWT token 是否安全存储?
|
|
208
|
+
- [ ] **HTTPS**: API 调用是否使用 HTTPS?
|
|
209
|
+
- [ ] **敏感信息**: 是否避免在前端存储敏感信息?
|
|
210
|
+
|
|
211
|
+
**安全检查示例**:
|
|
212
|
+
```vue
|
|
213
|
+
<!-- ❌ 危险:XSS 风险 -->
|
|
214
|
+
<div v-html="userInput"></div>
|
|
215
|
+
|
|
216
|
+
<!-- ✅ 安全:自动转义 -->
|
|
217
|
+
<div>{{ userInput }}</div>
|
|
218
|
+
|
|
219
|
+
<!-- ✅ 如必须使用 HTML,先消毒 -->
|
|
220
|
+
<script setup>
|
|
221
|
+
import DOMPurify from 'dompurify'
|
|
222
|
+
const sanitizedHTML = computed(() => DOMPurify.sanitize(userInput.value))
|
|
223
|
+
</script>
|
|
224
|
+
<div v-html="sanitizedHTML"></div>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 7. TypeScript 类型安全评审
|
|
228
|
+
|
|
229
|
+
- [ ] **类型定义**: 组件 Props、State、API 响应是否有类型?
|
|
230
|
+
- [ ] **避免 any**: 是否避免使用 `any` 类型?
|
|
231
|
+
- [ ] **类型推导**: 是否充分利用类型推导?
|
|
232
|
+
- [ ] **接口定义**: 是否定义清晰的接口和类型?
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// ✅ 正确:完整的类型定义
|
|
236
|
+
interface User {
|
|
237
|
+
id: number
|
|
238
|
+
name: string
|
|
239
|
+
email: string
|
|
240
|
+
role: 'admin' | 'user'
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
interface ApiResponse<T> {
|
|
244
|
+
code: number
|
|
245
|
+
message: string
|
|
246
|
+
data: T
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function fetchUsers(): Promise<ApiResponse<User[]>> {
|
|
250
|
+
const response = await api.get<ApiResponse<User[]>>('/users')
|
|
251
|
+
return response.data
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 8. 测试覆盖评审
|
|
256
|
+
|
|
257
|
+
- [ ] **组件测试**: 覆盖率是否 >= 90%?
|
|
258
|
+
- [ ] **测试用例**: 是否覆盖正常和异常流程?
|
|
259
|
+
- [ ] **用户交互**: 是否测试按钮点击、表单提交等交互?
|
|
260
|
+
- [ ] **Store 测试**: 是否测试状态管理逻辑?
|
|
261
|
+
- [ ] **E2E 测试**: 是否有关键流程的端到端测试?
|
|
262
|
+
|
|
263
|
+
**Vue 测试示例**:
|
|
264
|
+
```typescript
|
|
265
|
+
import { describe, it, expect } from 'vitest'
|
|
266
|
+
import { render, screen, fireEvent } from '@testing-library/vue'
|
|
267
|
+
|
|
268
|
+
describe('LoginForm', () => {
|
|
269
|
+
it('submits form with valid data', async () => {
|
|
270
|
+
const { emitted } = render(LoginForm)
|
|
271
|
+
|
|
272
|
+
await fireEvent.update(screen.getByLabelText('Email'), 'test@example.com')
|
|
273
|
+
await fireEvent.update(screen.getByLabelText('Password'), 'password123')
|
|
274
|
+
await fireEvent.click(screen.getByRole('button', { name: /login/i }))
|
|
275
|
+
|
|
276
|
+
expect(emitted()).toHaveProperty('submit')
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## 评审标准
|
|
282
|
+
|
|
283
|
+
### ✅ 评审通过标准
|
|
284
|
+
|
|
285
|
+
1. **组件设计合理**: 职责单一,可复用性强
|
|
286
|
+
2. **类型安全**: 完整的 TypeScript 类型定义
|
|
287
|
+
3. **用户体验良好**: 响应式设计,交互反馈及时
|
|
288
|
+
4. **性能优秀**: 组件懒加载,大列表优化
|
|
289
|
+
5. **安全可靠**: 无 XSS 风险,输入验证完善
|
|
290
|
+
6. **测试充分**: >= 90% 覆盖率
|
|
291
|
+
|
|
292
|
+
### ❌ 评审不通过场景
|
|
293
|
+
|
|
294
|
+
1. **类型缺失**: 缺少 TypeScript 类型定义,大量使用 `any`
|
|
295
|
+
2. **XSS 风险**: 不当使用 `v-html` / `dangerouslySetInnerHTML`
|
|
296
|
+
3. **性能问题**: 未使用懒加载,大列表未优化
|
|
297
|
+
4. **用户体验差**: 无 loading 状态,无错误提示
|
|
298
|
+
5. **测试不足**: 覆盖率 < 90%,缺少关键测试
|
|
299
|
+
|
|
300
|
+
## 评审流程
|
|
301
|
+
|
|
302
|
+
### 1. OpenSpec 评审
|
|
303
|
+
|
|
304
|
+
**检查 OpenSpec 文档**:
|
|
305
|
+
```markdown
|
|
306
|
+
□ 页面组件列表完整
|
|
307
|
+
□ 可复用组件定义清晰
|
|
308
|
+
□ 状态管理设计合理
|
|
309
|
+
□ 路由配置完整
|
|
310
|
+
□ API 调用层定义明确
|
|
311
|
+
□ 测试策略清晰(覆盖率 >= 90%)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 2. 代码实现评审
|
|
315
|
+
|
|
316
|
+
**对照 OpenSpec 检查代码**:
|
|
317
|
+
```markdown
|
|
318
|
+
□ 所有页面组件已实现
|
|
319
|
+
□ 可复用组件符合设计
|
|
320
|
+
□ Pinia/Redux Store 实现正确
|
|
321
|
+
□ 路由配置与 OpenSpec 一致
|
|
322
|
+
□ API 调用层封装合理
|
|
323
|
+
□ 测试用例覆盖所有场景
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 3. 自动化检查
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# TypeScript 类型检查
|
|
330
|
+
npm run type-check
|
|
331
|
+
|
|
332
|
+
# ESLint 检查
|
|
333
|
+
npm run lint
|
|
334
|
+
|
|
335
|
+
# 测试覆盖率
|
|
336
|
+
npm run test:coverage
|
|
337
|
+
|
|
338
|
+
# 构建检查
|
|
339
|
+
npm run build
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 4. 手动测试
|
|
343
|
+
|
|
344
|
+
```markdown
|
|
345
|
+
□ 在不同浏览器测试(Chrome, Firefox, Safari)
|
|
346
|
+
□ 在不同屏幕尺寸测试(桌面、平板、手机)
|
|
347
|
+
□ 测试键盘导航
|
|
348
|
+
□ 测试错误场景(网络失败、无效输入)
|
|
349
|
+
□ 测试性能(Lighthouse 分数 >= 90)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## 评审记录模板
|
|
353
|
+
|
|
354
|
+
```markdown
|
|
355
|
+
## Frontend OpenSpec 评审记录
|
|
356
|
+
|
|
357
|
+
**评审日期**: YYYY-MM-DD
|
|
358
|
+
**评审人**: [Frontend Engineer 姓名]
|
|
359
|
+
**OpenSpec 版本**: v1.0
|
|
360
|
+
**框架**: Vue 3 / React 18
|
|
361
|
+
|
|
362
|
+
### 组件设计评审
|
|
363
|
+
|
|
364
|
+
- [ ] 组件划分: ✅ 合理 / ❌ 需调整
|
|
365
|
+
- [ ] TypeScript 类型: ✅ 完整 / ❌ 缺失
|
|
366
|
+
- [ ] Props/Emits: ✅ 定义清晰 / ❌ 需补充
|
|
367
|
+
|
|
368
|
+
**问题列表**:
|
|
369
|
+
1. [问题描述] - [修复建议]
|
|
370
|
+
|
|
371
|
+
### 状态管理评审
|
|
372
|
+
|
|
373
|
+
- [ ] Store 设计: ✅ 合理 / ❌ 需调整
|
|
374
|
+
- [ ] 类型定义: ✅ 完整 / ❌ 缺失
|
|
375
|
+
- [ ] 异步处理: ✅ 正确 / ❌ 有问题
|
|
376
|
+
|
|
377
|
+
**问题列表**:
|
|
378
|
+
1. [问题描述] - [修复建议]
|
|
379
|
+
|
|
380
|
+
### UI/UX 评审
|
|
381
|
+
|
|
382
|
+
- [ ] 响应式设计: ✅ 良好 / ❌ 需优化
|
|
383
|
+
- [ ] 加载状态: ✅ 完整 / ❌ 缺失
|
|
384
|
+
- [ ] 错误提示: ✅ 友好 / ❌ 需改进
|
|
385
|
+
- [ ] 可访问性: ✅ 良好 / ❌ 需改进
|
|
386
|
+
|
|
387
|
+
### 性能评审
|
|
388
|
+
|
|
389
|
+
- [ ] 组件懒加载: ✅ 已使用 / ❌ 未使用
|
|
390
|
+
- [ ] 大列表优化: ✅ 已优化 / ❌ 需优化
|
|
391
|
+
- [ ] Lighthouse 分数: [分数] (>= 90)
|
|
392
|
+
|
|
393
|
+
### 安全性评审
|
|
394
|
+
|
|
395
|
+
- [ ] XSS 防护: ✅ 安全 / ❌ 有风险
|
|
396
|
+
- [ ] 输入验证: ✅ 完善 / ❌ 不足
|
|
397
|
+
- [ ] Token 管理: ✅ 安全 / ❌ 有风险
|
|
398
|
+
|
|
399
|
+
### 测试评审
|
|
400
|
+
|
|
401
|
+
- 单元测试覆盖率: [X]%
|
|
402
|
+
- 组件测试数量: [N] 个
|
|
403
|
+
- E2E 测试: ✅ 完成 / ❌ 缺失
|
|
404
|
+
|
|
405
|
+
### 评审结论
|
|
406
|
+
|
|
407
|
+
- [ ] ✅ 通过,可以进入测试阶段
|
|
408
|
+
- [ ] ⚠️ 有条件通过,修复以下问题后可进入测试
|
|
409
|
+
- [ ] ❌ 不通过,需要重大修订
|
|
410
|
+
|
|
411
|
+
### 待修复问题
|
|
412
|
+
|
|
413
|
+
1. [严重] [问题描述] - [修复建议]
|
|
414
|
+
2. [中等] [问题描述] - [修复建议]
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## 常见问题和解决方案
|
|
418
|
+
|
|
419
|
+
### Q1: 组件拆分粒度如何把握?
|
|
420
|
+
|
|
421
|
+
**解决方案**:
|
|
422
|
+
- 单一职责:一个组件只做一件事
|
|
423
|
+
- 可复用性:被多次使用的逻辑抽取为组件
|
|
424
|
+
- 避免过度:不要为了复用而复用
|
|
425
|
+
|
|
426
|
+
### Q2: 如何优化大列表性能?
|
|
427
|
+
|
|
428
|
+
**解决方案**:
|
|
429
|
+
- 使用虚拟滚动(vue-virtual-scroller, react-window)
|
|
430
|
+
- 分页加载
|
|
431
|
+
- 使用 `v-memo` / `React.memo` 避免不必要渲染
|
|
432
|
+
|
|
433
|
+
### Q3: 如何处理全局状态和局部状态?
|
|
434
|
+
|
|
435
|
+
**解决方案**:
|
|
436
|
+
- 全局状态:用户信息、主题设置 → Pinia/Redux
|
|
437
|
+
- 局部状态:表单输入、弹窗显示 → 组件内 `ref`/`useState`
|
|
438
|
+
|
|
439
|
+
### Q4: 如何确保类型安全?
|
|
440
|
+
|
|
441
|
+
**解决方案**:
|
|
442
|
+
- 所有 API 响应定义类型
|
|
443
|
+
- 组件 Props 使用接口定义
|
|
444
|
+
- 避免使用 `any`,使用 `unknown` 或泛型
|
|
445
|
+
- 开启 `strict` 模式
|
|
446
|
+
|
|
447
|
+
## 质量标准
|
|
448
|
+
|
|
449
|
+
### 优秀的 Frontend 实现特征
|
|
450
|
+
|
|
451
|
+
1. **类型安全**: 完整的 TypeScript 类型定义
|
|
452
|
+
2. **组件设计**: 职责单一,可复用性强
|
|
453
|
+
3. **用户体验**: 响应式、加载状态、错误提示完善
|
|
454
|
+
4. **性能优秀**: Lighthouse 分数 >= 90
|
|
455
|
+
5. **测试充分**: >= 90% 覆盖率
|
|
456
|
+
|
|
457
|
+
### 需要改进的实现特征
|
|
458
|
+
|
|
459
|
+
1. **类型缺失**: 大量使用 `any`
|
|
460
|
+
2. **组件混乱**: 职责不清,难以复用
|
|
461
|
+
3. **体验不佳**: 无 loading,错误提示不友好
|
|
462
|
+
4. **性能差**: 未懒加载,大列表未优化
|
|
463
|
+
5. **测试不足**: 覆盖率 < 90%
|
|
464
|
+
|
|
465
|
+
## 工具推荐
|
|
466
|
+
|
|
467
|
+
### 开发工具
|
|
468
|
+
|
|
469
|
+
- **Vue**: Vite, Vue Devtools, Volar
|
|
470
|
+
- **React**: Vite/CRA, React DevTools, React Developer Tools
|
|
471
|
+
- **状态管理**: Pinia Devtools, Redux DevTools
|
|
472
|
+
|
|
473
|
+
### 测试工具
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
# 单元测试
|
|
477
|
+
npm run test:unit
|
|
478
|
+
|
|
479
|
+
# 覆盖率
|
|
480
|
+
npm run test:coverage
|
|
481
|
+
|
|
482
|
+
# E2E 测试
|
|
483
|
+
npm run test:e2e
|
|
484
|
+
|
|
485
|
+
# 性能测试
|
|
486
|
+
npx lighthouse http://localhost:3000
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### 代码质量工具
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
# ESLint
|
|
493
|
+
npm run lint
|
|
494
|
+
|
|
495
|
+
# TypeScript 检查
|
|
496
|
+
npm run type-check
|
|
497
|
+
|
|
498
|
+
# 格式化
|
|
499
|
+
npm run format
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
**记住**: 优秀的前端代码不仅功能完整,还要提供良好的用户体验和可维护性。
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# SAE 需求规格评审指南
|
|
2
|
+
|
|
3
|
+
## 何时需要评审
|
|
4
|
+
|
|
5
|
+
**仅在以下情况下加载此评审指南**:
|
|
6
|
+
- 需求规格文档已完成,需要评审验证
|
|
7
|
+
- 用户明确要求对需求规格进行评审
|
|
8
|
+
- OpenSpec proposal 完成,需要技术方案评审
|
|
9
|
+
|
|
10
|
+
## 评审目标
|
|
11
|
+
|
|
12
|
+
确保需求规格文档的完整性、准确性、可实施性,为后续开发提供清晰的指导。
|
|
13
|
+
|
|
14
|
+
## 评审检查清单
|
|
15
|
+
|
|
16
|
+
### 1. 需求完整性检查
|
|
17
|
+
|
|
18
|
+
- [ ] **业务背景**:是否清晰说明了为什么需要这个功能?
|
|
19
|
+
- [ ] **目标用户**:是否明确了使用该功能的用户角色?
|
|
20
|
+
- [ ] **核心价值**:是否阐述了功能解决的问题和带来的价值?
|
|
21
|
+
- [ ] **功能列表**:是否列出了所有必需的功能点?
|
|
22
|
+
- [ ] **非功能需求**:是否考虑了性能、安全、可维护性等?
|
|
23
|
+
|
|
24
|
+
### 2. 需求清晰度检查
|
|
25
|
+
|
|
26
|
+
- [ ] **功能描述**:每个功能点是否有明确、无歧义的描述?
|
|
27
|
+
- [ ] **用户故事**:是否使用"作为...我希望...以便..."的格式?
|
|
28
|
+
- [ ] **边界清晰**:功能范围和边界是否明确?不在范围内的也要说明
|
|
29
|
+
- [ ] **术语统一**:关键术语是否定义清晰且全文一致?
|
|
30
|
+
|
|
31
|
+
### 3. 技术方案可行性检查
|
|
32
|
+
|
|
33
|
+
- [ ] **架构设计**:系统架构是否合理?模块划分是否清晰?
|
|
34
|
+
- [ ] **技术选型**:所选技术栈是否成熟可靠?团队是否熟悉?
|
|
35
|
+
- [ ] **接口定义**:前后端 API 契约是否完整?包含请求/响应格式、错误码等
|
|
36
|
+
- [ ] **数据模型**:数据库设计是否合理?表结构、字段类型、索引是否明确?
|
|
37
|
+
- [ ] **依赖风险**:是否识别了外部依赖和第三方服务?
|
|
38
|
+
|
|
39
|
+
### 4. 性能和安全检查
|
|
40
|
+
|
|
41
|
+
- [ ] **性能指标**:是否定义了性能要求?(响应时间、并发量、吞吐量)
|
|
42
|
+
- [ ] **安全考虑**:是否考虑了认证、授权、数据加密、输入验证?
|
|
43
|
+
- [ ] **错误处理**:是否定义了异常情况的处理策略?
|
|
44
|
+
- [ ] **日志审计**:是否规划了日志记录和审计追踪?
|
|
45
|
+
|
|
46
|
+
### 5. 验收标准检查
|
|
47
|
+
|
|
48
|
+
- [ ] **可测试性**:验收标准是否可测试?有明确的预期结果?
|
|
49
|
+
- [ ] **覆盖完整**:验收标准是否覆盖了所有关键功能?
|
|
50
|
+
- [ ] **场景完整**:是否包含正常流程和异常流程?
|
|
51
|
+
- [ ] **量化指标**:是否有可量化的成功标准?
|
|
52
|
+
|
|
53
|
+
### 6. 测试策略检查
|
|
54
|
+
|
|
55
|
+
- [ ] **单元测试**:是否明确了覆盖率要求?(后端 90%, 前端 90%)
|
|
56
|
+
- [ ] **集成测试**:是否规划了 API 集成测试?
|
|
57
|
+
- [ ] **E2E 测试**:是否定义了关键用户流程的端到端测试?
|
|
58
|
+
- [ ] **性能测试**:是否需要性能测试?如需要,是否定义了测试场景?
|
|
59
|
+
|
|
60
|
+
## 评审标准
|
|
61
|
+
|
|
62
|
+
### ✅ 评审通过标准
|
|
63
|
+
|
|
64
|
+
1. **完整性**: 所有必需章节都已填写,无遗漏
|
|
65
|
+
2. **清晰性**: 描述清晰明确,无歧义,技术人员可直接实施
|
|
66
|
+
3. **可行性**: 技术方案成熟可靠,团队有能力实现
|
|
67
|
+
4. **可测试**: 验收标准清晰,可测试验证
|
|
68
|
+
5. **风险可控**: 已识别主要风险,有缓解措施
|
|
69
|
+
|
|
70
|
+
### ❌ 评审不通过场景
|
|
71
|
+
|
|
72
|
+
1. **需求不清**:功能描述模糊,存在多种理解
|
|
73
|
+
2. **技术风险高**:使用不成熟技术,团队不熟悉,无备选方案
|
|
74
|
+
3. **接口不完整**:前后端 API 定义缺失关键信息
|
|
75
|
+
4. **缺少验收标准**:无法判断功能是否完成
|
|
76
|
+
5. **性能/安全考虑不足**:未考虑性能瓶颈或安全漏洞
|
|
77
|
+
|
|
78
|
+
## 评审流程
|
|
79
|
+
|
|
80
|
+
### 1. 自检阶段
|
|
81
|
+
|
|
82
|
+
在提交评审前,SAE 应自行检查:
|
|
83
|
+
```markdown
|
|
84
|
+
□ 阅读整个文档,确保逻辑连贯
|
|
85
|
+
□ 使用评审检查清单逐项核对
|
|
86
|
+
□ 模拟 Backend/Frontend Engineer 视角,是否能直接开发
|
|
87
|
+
□ 模拟 Tester 视角,是否能设计测试用例
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 2. 同行评审阶段
|
|
91
|
+
|
|
92
|
+
邀请其他技术角色评审:
|
|
93
|
+
- **Backend Engineer**: 评审后端技术方案、API 设计、数据模型
|
|
94
|
+
- **Frontend Engineer**: 评审前端交互设计、组件规划、状态管理
|
|
95
|
+
- **Tester**: 评审测试策略、验收标准的可测试性
|
|
96
|
+
|
|
97
|
+
### 3. 修订阶段
|
|
98
|
+
|
|
99
|
+
根据评审反馈修订:
|
|
100
|
+
```markdown
|
|
101
|
+
1. 记录所有评审意见
|
|
102
|
+
2. 对每条意见进行分类:接受/拒绝/需讨论
|
|
103
|
+
3. 更新需求规格文档
|
|
104
|
+
4. 回复评审意见,说明修订内容
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 4. 最终确认
|
|
108
|
+
|
|
109
|
+
所有关键问题解决后:
|
|
110
|
+
- 标记文档状态为"已评审"
|
|
111
|
+
- 通知 Backend/Frontend Engineer 开始实施
|
|
112
|
+
- 将文档纳入版本管理
|
|
113
|
+
|
|
114
|
+
## 评审记录模板
|
|
115
|
+
|
|
116
|
+
```markdown
|
|
117
|
+
## 评审记录
|
|
118
|
+
|
|
119
|
+
**评审日期**: YYYY-MM-DD
|
|
120
|
+
**评审人**: [SAE姓名] + [其他角色]
|
|
121
|
+
**文档版本**: v1.0
|
|
122
|
+
|
|
123
|
+
### 评审意见
|
|
124
|
+
|
|
125
|
+
#### 意见 1
|
|
126
|
+
- **类型**: 重大问题 / 中等问题 / 建议
|
|
127
|
+
- **描述**: [具体问题描述]
|
|
128
|
+
- **建议**: [修复建议]
|
|
129
|
+
- **状态**: 已修复 / 待修复 / 不修复(说明原因)
|
|
130
|
+
|
|
131
|
+
#### 意见 2
|
|
132
|
+
...
|
|
133
|
+
|
|
134
|
+
### 评审结论
|
|
135
|
+
|
|
136
|
+
- [ ] 通过,可以进入开发阶段
|
|
137
|
+
- [ ] 有条件通过,修复以下问题后可进入开发
|
|
138
|
+
- [ ] 不通过,需要重大修订后重新评审
|
|
139
|
+
|
|
140
|
+
### 后续行动
|
|
141
|
+
|
|
142
|
+
1. [行动项 1]
|
|
143
|
+
2. [行动项 2]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 常见问题和解决方案
|
|
147
|
+
|
|
148
|
+
### Q1: 需求描述过于技术化,业务人员看不懂怎么办?
|
|
149
|
+
|
|
150
|
+
**解决方案**:
|
|
151
|
+
- 增加"业务背景"章节,用业务语言描述
|
|
152
|
+
- 技术实现细节放在"技术方案"章节
|
|
153
|
+
- 使用流程图、时序图等可视化方式辅助说明
|
|
154
|
+
|
|
155
|
+
### Q2: 前后端接口设计分歧怎么办?
|
|
156
|
+
|
|
157
|
+
**解决方案**:
|
|
158
|
+
- 召集 Backend/Frontend Engineer 进行设计评审会议
|
|
159
|
+
- 讨论各方案的优劣,达成一致
|
|
160
|
+
- 在需求规格中明确记录最终决策和理由
|
|
161
|
+
|
|
162
|
+
### Q3: 性能要求无法量化怎么办?
|
|
163
|
+
|
|
164
|
+
**解决方案**:
|
|
165
|
+
- 参考行业标准(如响应时间 < 200ms P95)
|
|
166
|
+
- 基于用户体验设定指标(如页面加载 < 3秒)
|
|
167
|
+
- 与产品/业务方确认可接受的性能水平
|
|
168
|
+
|
|
169
|
+
### Q4: 技术方案存在多个可行选项怎么办?
|
|
170
|
+
|
|
171
|
+
**解决方案**:
|
|
172
|
+
- 使用决策矩阵对比各方案(成熟度、性能、成本、团队熟悉度)
|
|
173
|
+
- 在需求规格中记录对比结果和选择理由
|
|
174
|
+
- 保留备选方案作为风险缓解措施
|
|
175
|
+
|
|
176
|
+
## 评审质量标准
|
|
177
|
+
|
|
178
|
+
### 优秀的需求规格特征
|
|
179
|
+
|
|
180
|
+
1. **完整详尽**: 涵盖所有功能和非功能需求
|
|
181
|
+
2. **清晰无歧义**: 技术人员读完即可开始设计
|
|
182
|
+
3. **可实施**: 技术方案成熟,团队有能力实现
|
|
183
|
+
4. **可验证**: 验收标准清晰,可测试
|
|
184
|
+
5. **前瞻性**: 考虑了扩展性和未来演进
|
|
185
|
+
|
|
186
|
+
### 需要改进的需求规格特征
|
|
187
|
+
|
|
188
|
+
1. **描述模糊**: 使用"可能"、"大概"等不确定词汇
|
|
189
|
+
2. **技术方案缺失**: 没有说明如何实现
|
|
190
|
+
3. **接口不完整**: API 定义缺少关键字段或错误码
|
|
191
|
+
4. **缺少边界**: 没有明确功能范围
|
|
192
|
+
5. **验收标准不可测**: 无法判断是否完成
|
|
193
|
+
|
|
194
|
+
## 工具和技巧
|
|
195
|
+
|
|
196
|
+
### 推荐工具
|
|
197
|
+
|
|
198
|
+
- **流程图**: Mermaid, draw.io
|
|
199
|
+
- **API 文档**: Swagger/OpenAPI, Apifox
|
|
200
|
+
- **数据库设计**: dbdiagram.io, DrawSQL
|
|
201
|
+
- **协作评审**: GitLab Comments, Google Docs
|
|
202
|
+
|
|
203
|
+
### 评审技巧
|
|
204
|
+
|
|
205
|
+
1. **换位思考**: 从开发者、测试者角度阅读
|
|
206
|
+
2. **提出问题**: "如果...怎么办?" "为什么...?"
|
|
207
|
+
3. **检查一致性**: 术语、命名、格式是否统一
|
|
208
|
+
4. **验证可测**: 能否根据文档编写测试用例
|
|
209
|
+
5. **风险识别**: 主动寻找潜在问题和边界情况
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
**记住**: 好的需求规格是成功开发的基础。花时间做好评审,能避免后期返工,节约整体时间。
|