@h-ai/cli 0.1.0-alpha.10
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/LICENSE +202 -0
- package/README.md +167 -0
- package/dist/chunk-2H7UOFLK.js +10 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-LANIDJC7.js +659 -0
- package/dist/chunk-LANIDJC7.js.map +1 -0
- package/dist/chunk-YV7WPUI6.js +206 -0
- package/dist/chunk-YV7WPUI6.js.map +1 -0
- package/dist/config-templates-S5RI2MC6.js +5 -0
- package/dist/config-templates-S5RI2MC6.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1650 -0
- package/dist/index.js.map +1 -0
- package/dist/skill-templates-N5DU5FNX.js +5 -0
- package/dist/skill-templates-N5DU5FNX.js.map +1 -0
- package/dist/src-XJ6YXNAF.js +15030 -0
- package/dist/src-XJ6YXNAF.js.map +1 -0
- package/package.json +59 -0
- package/templates/README.md +126 -0
- package/templates/apps/admin/messages/en-US.json +33 -0
- package/templates/apps/admin/messages/zh-CN.json +33 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +30 -0
- package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -0
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +44 -0
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +45 -0
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +37 -0
- package/templates/apps/admin/src/routes/api/health/+server.ts +12 -0
- package/templates/apps/android-app/.env.hbs +3 -0
- package/templates/apps/android-app/capacitor.config.ts.hbs +12 -0
- package/templates/apps/android-app/messages/en-US.json +24 -0
- package/templates/apps/android-app/messages/zh-CN.json +24 -0
- package/templates/apps/android-app/src/lib/api.ts.hbs +30 -0
- package/templates/apps/android-app/src/lib/capacitor.ts +21 -0
- package/templates/apps/android-app/src/routes/+layout.svelte.hbs +42 -0
- package/templates/apps/android-app/src/routes/+layout.ts +7 -0
- package/templates/apps/android-app/src/routes/+page.svelte.hbs +27 -0
- package/templates/apps/api/src/routes/+layout.svelte +14 -0
- package/templates/apps/api/src/routes/+page.svelte +11 -0
- package/templates/apps/api/src/routes/api/v1/health/+server.ts +14 -0
- package/templates/apps/api/src/routes/api/v1/items/+server.ts +37 -0
- package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +30 -0
- package/templates/apps/h5/messages/en-US.json +37 -0
- package/templates/apps/h5/messages/zh-CN.json +37 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -0
- package/templates/apps/h5/src/routes/+page.svelte.hbs +27 -0
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +27 -0
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +27 -0
- package/templates/apps/website/messages/en-US.json +32 -0
- package/templates/apps/website/messages/zh-CN.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +66 -0
- package/templates/apps/website/src/routes/+page.svelte.hbs +33 -0
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +27 -0
- package/templates/apps/website/src/routes/api/health/+server.ts +12 -0
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +59 -0
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +63 -0
- package/templates/base/e2e/helpers.ts +49 -0
- package/templates/base/package.json.hbs +67 -0
- package/templates/base/playwright.config.ts +32 -0
- package/templates/base/src/app.css +2 -0
- package/templates/base/src/app.d.ts.hbs +18 -0
- package/templates/base/src/app.html.hbs +16 -0
- package/templates/base/src/hooks.server.ts.hbs +123 -0
- package/templates/base/src/lib/server/init.ts.hbs +190 -0
- package/templates/base/svelte.config.js.hbs +30 -0
- package/templates/base/tsconfig.json +8 -0
- package/templates/base/vite.config.ts.hbs +27 -0
- package/templates/features/ai/routes/api/chat/+server.ts +26 -0
- package/templates/features/datapipe/routes/api/datapipe/pipeline/+server.ts +41 -0
- package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +21 -0
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +98 -0
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +124 -0
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +98 -0
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +114 -0
- package/templates/features/iam/routes-shared/api/auth/login/+server.ts +32 -0
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +15 -0
- package/templates/features/iam/routes-shared/api/auth/me/+server.ts +12 -0
- package/templates/features/iam/routes-shared/api/auth/register/+server.ts +26 -0
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +30 -0
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +42 -0
- package/templates/features/payment/routes/api/v1/payment/query/[orderNo]/+server.ts +25 -0
- package/templates/features/storage/routes/api/upload/+server.ts +35 -0
- package/templates/features/vecdb/routes/api/vecdb/search/+server.ts +36 -0
- package/templates/i18n/project.inlang/settings.json +11 -0
- package/templates/skills/AGENTS.md +26 -0
- package/templates/skills/CLAUDE.md +12 -0
- package/templates/skills/copilot-instructions.md +61 -0
- package/templates/skills/hai-ai/SKILL.md +721 -0
- package/templates/skills/hai-api-client/SKILL.md +355 -0
- package/templates/skills/hai-app-create/SKILL.md +371 -0
- package/templates/skills/hai-app-review/SKILL.md +156 -0
- package/templates/skills/hai-app-tests/SKILL.md +468 -0
- package/templates/skills/hai-audit/SKILL.md +145 -0
- package/templates/skills/hai-build/SKILL.md +319 -0
- package/templates/skills/hai-cache/SKILL.md +190 -0
- package/templates/skills/hai-capacitor/SKILL.md +377 -0
- package/templates/skills/hai-core/SKILL.md +484 -0
- package/templates/skills/hai-crypto/SKILL.md +198 -0
- package/templates/skills/hai-datapipe/SKILL.md +220 -0
- package/templates/skills/hai-deploy/SKILL.md +216 -0
- package/templates/skills/hai-iam/SKILL.md +361 -0
- package/templates/skills/hai-kit/SKILL.md +484 -0
- package/templates/skills/hai-payment/SKILL.md +275 -0
- package/templates/skills/hai-reach/SKILL.md +315 -0
- package/templates/skills/hai-reldb/SKILL.md +465 -0
- package/templates/skills/hai-scheduler/SKILL.md +188 -0
- package/templates/skills/hai-storage/SKILL.md +276 -0
- package/templates/skills/hai-ui/SKILL.md +438 -0
- package/templates/skills/hai-vecdb/SKILL.md +279 -0
- package/templates/skills/opencode.json +11 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hai-app-create
|
|
3
|
+
description: 以 TDD 方式在应用中创建新功能:先编写测试定义行为,再编码实现直至测试通过;当需求涉及新增页面/路由、API、服务、数据模型、组件时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# hai-app-create — TDD 驱动的应用功能创建规范
|
|
7
|
+
|
|
8
|
+
> 面向 AI 助手的应用功能创建指南。**必须遵循 TDD**:先写测试(Red)→ 再实现(Green)→ 再重构(Refactor)。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 适用场景
|
|
13
|
+
|
|
14
|
+
- 新增页面(路由)
|
|
15
|
+
- 新增 API 端点
|
|
16
|
+
- 新增服务层模块(`$lib/server/services/`)
|
|
17
|
+
- 新增数据库表 / 数据模型
|
|
18
|
+
- 新增业务组件
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## TDD 开发流程(强制)
|
|
23
|
+
|
|
24
|
+
创建任何新功能时,必须按以下顺序执行:
|
|
25
|
+
|
|
26
|
+
### Step 1:需求分析与测试设计
|
|
27
|
+
|
|
28
|
+
1. 明确功能的输入、输出、边界条件、错误场景
|
|
29
|
+
2. 确定需要创建的文件(服务、路由、Schema、组件等)
|
|
30
|
+
3. 设计测试用例清单(参照 `hai-app-tests` 技能的覆盖范围要求)
|
|
31
|
+
|
|
32
|
+
### Step 2:编写测试(Red 阶段)
|
|
33
|
+
|
|
34
|
+
1. **创建单元测试**:在 `tests/` 下为服务层、Schema 编写 Vitest 测试
|
|
35
|
+
2. **创建 E2E 测试**:在 `e2e/` 下为 API 端点、页面交互编写 Playwright 测试
|
|
36
|
+
3. **创建空的实现文件**:仅包含函数签名和类型定义,不写实际逻辑(确保 import 不报错)
|
|
37
|
+
4. **运行测试确认全部失败**:
|
|
38
|
+
```bash
|
|
39
|
+
pnpm --filter <app-name> test # 应全部 FAIL
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 3:编写实现(Green 阶段)
|
|
43
|
+
|
|
44
|
+
按照下文的「创建指南」编写实际功能代码,每完成一个功能点立即运行测试确认通过。
|
|
45
|
+
|
|
46
|
+
### Step 4:重构与验证(Refactor 阶段)
|
|
47
|
+
|
|
48
|
+
1. 参照 `hai-app-review` 审查代码规范
|
|
49
|
+
2. 运行质量门禁:`pnpm typecheck && pnpm lint && pnpm test`
|
|
50
|
+
3. 运行 E2E:`pnpm --filter <app-name> test:e2e`
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 目录结构约定
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
src/
|
|
58
|
+
hooks.server.ts # Handle Hook 入口
|
|
59
|
+
app.css # 全局样式(TailwindCSS 4)
|
|
60
|
+
lib/
|
|
61
|
+
server/
|
|
62
|
+
init.ts # 应用初始化(模块 init 顺序)
|
|
63
|
+
services/ # 业务服务层(Server-only)
|
|
64
|
+
index.ts # 服务聚合导出
|
|
65
|
+
user.ts # 示例:用户服务
|
|
66
|
+
schemas/ # 请求校验 Schema(Zod)
|
|
67
|
+
components/ # 业务组件(非 @h-ai/ui 通用组件)
|
|
68
|
+
stores/ # 客户端 Store(Svelte 5 Runes)
|
|
69
|
+
paraglide/ # i18n 生成文件(禁止手动修改)
|
|
70
|
+
routes/
|
|
71
|
+
+layout.svelte # 根布局
|
|
72
|
+
+page.svelte # 首页
|
|
73
|
+
(auth)/ # 认证分组路由
|
|
74
|
+
login/+page.svelte
|
|
75
|
+
admin/ # 管理区域
|
|
76
|
+
users/
|
|
77
|
+
+page.svelte # 列表页
|
|
78
|
+
+page.server.ts # 列表数据加载
|
|
79
|
+
[id]/
|
|
80
|
+
+page.svelte # 详情页
|
|
81
|
+
+page.server.ts
|
|
82
|
+
api/ # API 端点
|
|
83
|
+
auth/+server.ts
|
|
84
|
+
health/+server.ts
|
|
85
|
+
iam/
|
|
86
|
+
users/+server.ts
|
|
87
|
+
messages/ # i18n 消息文件
|
|
88
|
+
en-US.json
|
|
89
|
+
zh-CN.json
|
|
90
|
+
config/ # 模块配置文件
|
|
91
|
+
_core.yml
|
|
92
|
+
_db.yml
|
|
93
|
+
_cache.yml
|
|
94
|
+
_iam.yml
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 创建页面
|
|
100
|
+
|
|
101
|
+
### 页面路由规则
|
|
102
|
+
|
|
103
|
+
使用 SvelteKit 文件路由,参考 `src/routes/` 约定:
|
|
104
|
+
|
|
105
|
+
| 类型 | 路径示例 | 说明 |
|
|
106
|
+
| -------- | ---------------------------------- | -------------------- |
|
|
107
|
+
| 静态页面 | `routes/about/+page.svelte` | 常规页面 |
|
|
108
|
+
| 动态路由 | `routes/users/[id]/+page.svelte` | URL 参数 |
|
|
109
|
+
| 分组路由 | `routes/(auth)/login/+page.svelte` | 共享布局,不影响 URL |
|
|
110
|
+
| API 端点 | `routes/api/users/+server.ts` | RESTful API |
|
|
111
|
+
|
|
112
|
+
### 页面文件模板
|
|
113
|
+
|
|
114
|
+
```svelte
|
|
115
|
+
<!-- +page.svelte -->
|
|
116
|
+
<script lang="ts">
|
|
117
|
+
// Svelte 5 Runes 语法
|
|
118
|
+
let { data } = $props()
|
|
119
|
+
let loading = $state(false)
|
|
120
|
+
|
|
121
|
+
async function handleAction() {
|
|
122
|
+
loading = true
|
|
123
|
+
try {
|
|
124
|
+
// 业务逻辑
|
|
125
|
+
} finally {
|
|
126
|
+
loading = false
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<div class="container mx-auto p-4">
|
|
132
|
+
<!-- 使用 @h-ai/ui 组件 -->
|
|
133
|
+
</div>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 数据加载(+page.server.ts)
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import type { PageServerLoad } from './$types'
|
|
140
|
+
import { kit } from '@h-ai/kit'
|
|
141
|
+
|
|
142
|
+
export const load: PageServerLoad = async (event) => {
|
|
143
|
+
// 权限守卫
|
|
144
|
+
const guard = kit.guard.requireAuth(event)
|
|
145
|
+
if (!guard.success)
|
|
146
|
+
return kit.response.redirect('/login')
|
|
147
|
+
|
|
148
|
+
// 加载数据
|
|
149
|
+
const result = await someService.list()
|
|
150
|
+
if (!result.success)
|
|
151
|
+
return kit.response.error(500, result.error.message)
|
|
152
|
+
|
|
153
|
+
return { items: result.data }
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 创建 API 端点
|
|
160
|
+
|
|
161
|
+
### RESTful 端点模板
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// src/routes/api/[resource]/+server.ts
|
|
165
|
+
import type { RequestHandler } from './$types'
|
|
166
|
+
import { kit } from '@h-ai/kit'
|
|
167
|
+
import { z } from 'zod'
|
|
168
|
+
|
|
169
|
+
const CreateSchema = z.object({
|
|
170
|
+
name: z.string().min(1).max(100),
|
|
171
|
+
description: z.string().optional(),
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// GET - 列表
|
|
175
|
+
export const GET = kit.handler(async ({ locals }) => {
|
|
176
|
+
kit.guard.requirePermission(locals.session, 'resource:read')
|
|
177
|
+
|
|
178
|
+
const result = await service.list()
|
|
179
|
+
if (!result.success)
|
|
180
|
+
return kit.response.error(500, result.error.message)
|
|
181
|
+
return kit.response.ok(result.data)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// POST - 创建
|
|
185
|
+
export const POST = kit.handler(async ({ request, locals }) => {
|
|
186
|
+
kit.guard.requirePermission(locals.session, 'resource:create')
|
|
187
|
+
|
|
188
|
+
const data = await kit.validate.formOrFail(request, CreateSchema)
|
|
189
|
+
|
|
190
|
+
const result = await service.create(data)
|
|
191
|
+
if (!result.success)
|
|
192
|
+
return kit.response.error(500, result.error.message)
|
|
193
|
+
return kit.response.created(result.data)
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 要点
|
|
198
|
+
|
|
199
|
+
- 所有端点**必须**用 `kit.handler(async ({ locals, request, ... }) => { ... })` 包裹,由 handler 统一处理 throw 的 Response
|
|
200
|
+
- 权限守卫使用 `kit.guard.requirePermission(locals.session, 'xxx:yyy')`,它本身就是 throw 模式(未通过时 throw 403 Response),**无需**检查返回值
|
|
201
|
+
- 输入校验使用 `await kit.validate.formOrFail(request, Schema)`,校验失败时 throw 400 Response,**无需**手动判断 valid
|
|
202
|
+
- 返回统一使用 `kit.response.*`
|
|
203
|
+
- HaiResult 错误直接透传,不重新包装
|
|
204
|
+
- 框架模块 API 不抛异常,不要用 `try/catch` 处理模块返回的错误,直接检查 `result.success`
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 创建服务层
|
|
209
|
+
|
|
210
|
+
服务层位于 `$lib/server/services/`,处理业务逻辑。
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import type { HaiResult } from '@h-ai/core'
|
|
214
|
+
// src/lib/server/services/article.ts
|
|
215
|
+
import { core } from '@h-ai/core'
|
|
216
|
+
import { reldb } from '@h-ai/reldb'
|
|
217
|
+
|
|
218
|
+
const logger = core.logger
|
|
219
|
+
|
|
220
|
+
/** 创建文章 */
|
|
221
|
+
export async function createArticle(input: CreateArticleInput): Promise<HaiResult<Article>> {
|
|
222
|
+
logger.debug('Creating article', { title: input.title })
|
|
223
|
+
|
|
224
|
+
const result = await reldb.crud.create('articles', {
|
|
225
|
+
id: crypto.randomUUID(),
|
|
226
|
+
...input,
|
|
227
|
+
created_at: new Date().toISOString(),
|
|
228
|
+
})
|
|
229
|
+
if (!result.success)
|
|
230
|
+
return result
|
|
231
|
+
|
|
232
|
+
logger.info('Article created', { id: result.data.id })
|
|
233
|
+
return result
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** 获取文章列表 */
|
|
237
|
+
export async function listArticles(params: ListParams): Promise<HaiResult<PaginatedResult<Article>>> {
|
|
238
|
+
logger.debug('Listing articles', { page: params.page })
|
|
239
|
+
return reldb.crud.paginate('articles', params)
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### 服务层规则
|
|
244
|
+
|
|
245
|
+
- 统一返回 `HaiResult<T>`
|
|
246
|
+
- 写操作需 `debug`(进入)+ `info`(成功)日志
|
|
247
|
+
- 读操作仅 `debug` 日志
|
|
248
|
+
- 禁止 `console.log`,使用 `core.logger`
|
|
249
|
+
- 服务层不处理 HTTP 响应格式,只返回 HaiResult
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 创建数据模型
|
|
254
|
+
|
|
255
|
+
### Schema 定义(SQL DDL)
|
|
256
|
+
|
|
257
|
+
数据库表 Schema 定义在 `$lib/server/init.ts` 中:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const BUSINESS_SCHEMA = `
|
|
261
|
+
CREATE TABLE IF NOT EXISTS articles (
|
|
262
|
+
id TEXT PRIMARY KEY,
|
|
263
|
+
title TEXT NOT NULL,
|
|
264
|
+
content TEXT,
|
|
265
|
+
author_id TEXT NOT NULL,
|
|
266
|
+
status TEXT DEFAULT 'draft',
|
|
267
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
268
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
CREATE INDEX IF NOT EXISTS idx_articles_author_id ON articles(author_id);
|
|
272
|
+
CREATE INDEX IF NOT EXISTS idx_articles_status ON articles(status);
|
|
273
|
+
`
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 类型定义
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// 在服务文件或独立类型文件中定义
|
|
280
|
+
interface Article {
|
|
281
|
+
id: string
|
|
282
|
+
title: string
|
|
283
|
+
content: string | null
|
|
284
|
+
authorId: string
|
|
285
|
+
status: 'draft' | 'published' | 'archived'
|
|
286
|
+
createdAt: string
|
|
287
|
+
updatedAt: string
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
interface CreateArticleInput {
|
|
291
|
+
title: string
|
|
292
|
+
content?: string
|
|
293
|
+
authorId: string
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 创建业务组件
|
|
300
|
+
|
|
301
|
+
业务组件放在 `$lib/components/`,通用 UI 使用 `@h-ai/ui`。
|
|
302
|
+
|
|
303
|
+
```svelte
|
|
304
|
+
<!-- src/lib/components/ArticleCard.svelte -->
|
|
305
|
+
<script lang="ts">
|
|
306
|
+
import type { Article } from '$lib/server/services/article'
|
|
307
|
+
|
|
308
|
+
let { article, onEdit }: { article: Article, onEdit?: () => void } = $props()
|
|
309
|
+
</script>
|
|
310
|
+
|
|
311
|
+
<div class="card bg-base-100 shadow">
|
|
312
|
+
<div class="card-body">
|
|
313
|
+
<h2 class="card-title">{article.title}</h2>
|
|
314
|
+
<p>{article.content ?? ''}</p>
|
|
315
|
+
{#if onEdit}
|
|
316
|
+
<div class="card-actions justify-end">
|
|
317
|
+
<button class="btn btn-primary" onclick={onEdit}>编辑</button>
|
|
318
|
+
</div>
|
|
319
|
+
{/if}
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### 组件规则
|
|
325
|
+
|
|
326
|
+
- 使用 Svelte 5 Runes 语法(`$props()`、`$state()`、`$derived()`、`$effect()`)
|
|
327
|
+
- 事件回调通过 props 传入(非 `createEventDispatcher`)
|
|
328
|
+
- 样式使用 TailwindCSS 4 + DaisyUI 5 class
|
|
329
|
+
- 通用 UI 组件优先使用 `@h-ai/ui`,不重复实现
|
|
330
|
+
- i18n 文本使用 `$lib/paraglide/messages.js` 中的 key
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 初始化顺序
|
|
335
|
+
|
|
336
|
+
在 `$lib/server/init.ts` 中管理模块初始化顺序:
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
core.init() → reldb.init() → cache.init() → iam.init() → createBusinessTables()
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
新增模块时需在此文件中按依赖顺序添加初始化调用。
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 检查清单
|
|
347
|
+
|
|
348
|
+
- [ ] **TDD 流程已执行**:先写测试 → 确认失败 → 再实现 → 确认通过
|
|
349
|
+
- [ ] 单元测试已编写(服务层、Schema)
|
|
350
|
+
- [ ] E2E 测试已编写(API 端点、页面交互)
|
|
351
|
+
- [ ] 页面使用 Svelte 5 Runes 语法
|
|
352
|
+
- [ ] API 端点有权限守卫与输入校验
|
|
353
|
+
- [ ] 服务层统一返回 `HaiResult<T>`
|
|
354
|
+
- [ ] 日志级别正确(读 debug / 写 debug+info / 失败 warn|error)
|
|
355
|
+
- [ ] 禁止 `console.log`、`any`、硬编码密钥
|
|
356
|
+
- [ ] 用户可见文本走 i18n
|
|
357
|
+
- [ ] 新增表有索引定义
|
|
358
|
+
- [ ] 组件优先使用 `@h-ai/ui`
|
|
359
|
+
- [ ] `pnpm typecheck && pnpm lint && pnpm test` 通过
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## 相关 Skills
|
|
364
|
+
|
|
365
|
+
- `hai-build`:项目架构与模块初始化顺序
|
|
366
|
+
- `hai-app-tests`:TDD 测试规范(Red 阶段详细指引)
|
|
367
|
+
- `hai-app-review`:代码审查规范(Refactor 阶段参照)
|
|
368
|
+
- `hai-kit`:SvelteKit 集成的完整 API
|
|
369
|
+
- `hai-ui`:UI 组件库使用
|
|
370
|
+
- `hai-reldb`:数据库操作详细 API
|
|
371
|
+
- `hai-iam`:认证与权限管理
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hai-app-review
|
|
3
|
+
description: 对应用代码进行审查:检查 TDD 合规性、架构分层、HaiResult 处理、权限守卫、日志规范、类型安全、测试覆盖与 i18n 合规;当需求涉及代码审查、规范化、质量检查时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# hai-app-review — 应用代码审查规范
|
|
7
|
+
|
|
8
|
+
> 面向 AI 助手的应用代码审查指南。用于审查使用 hai-framework 构建的 SvelteKit 应用。本技能也是 TDD Refactor 阶段的执行入口。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 适用场景
|
|
13
|
+
|
|
14
|
+
- 审查应用代码是否符合 hai-framework 规范
|
|
15
|
+
- TDD Refactor 阶段:在测试通过后进行代码重构与规范化
|
|
16
|
+
- 规范化现有代码(分层、命名、日志、i18n)
|
|
17
|
+
- 安全审查(权限、输入校验、密钥管理)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 工作准则
|
|
22
|
+
|
|
23
|
+
1. **先搜索再改动**:用全局检索确认引用关系,避免遗漏更新。
|
|
24
|
+
2. **最小变更**:除非测试暴露缺陷,不改业务行为。
|
|
25
|
+
3. **成套更新**:代码 / 测试 / 文档同步。
|
|
26
|
+
4. **重构后测试必须仍通过**:任何重构都不能破坏已有测试。
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 审查清单
|
|
31
|
+
|
|
32
|
+
### TDD 合规性
|
|
33
|
+
|
|
34
|
+
- [ ] 每个功能都有对应的单元测试(`tests/` 目录)
|
|
35
|
+
- [ ] 关键流程有对应的 E2E 测试(`e2e/` 目录)
|
|
36
|
+
- [ ] 单元测试覆盖四种路径:正常、边界、权限、错误
|
|
37
|
+
- [ ] E2E 测试覆盖 API 端点的未认证/认证场景
|
|
38
|
+
- [ ] 测试先于实现编写(TDD 流程已遵循)
|
|
39
|
+
- [ ] `pnpm test` 单元测试全部通过
|
|
40
|
+
- [ ] `pnpm test:e2e` E2E 测试全部通过
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 审查清单
|
|
45
|
+
|
|
46
|
+
### 架构分层
|
|
47
|
+
|
|
48
|
+
- [ ] `hooks.server.ts` 仅做中间件编排(初始化、i18n、Handle Hook),无业务逻辑
|
|
49
|
+
- [ ] `+page.server.ts` / `+server.ts` 仅做请求处理(守卫 → 校验 → 调用服务 → 响应)
|
|
50
|
+
- [ ] 业务逻辑集中在 `$lib/server/services/`
|
|
51
|
+
- [ ] 客户端组件不直接调用数据库或服务端模块
|
|
52
|
+
- [ ] 依赖方向:路由 → 服务 → 框架模块,不反向
|
|
53
|
+
|
|
54
|
+
### HaiResult 处理
|
|
55
|
+
|
|
56
|
+
- [ ] 所有框架模块调用都检查 `result.success`
|
|
57
|
+
- [ ] 上游 HaiResult 错误直接透传,不重新包装
|
|
58
|
+
- [ ] 不使用 `try/catch` 来处理框架模块返回的错误(模块 API 不抛异常,统一返回 `HaiResult<T>`)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// ❌ 重新包装
|
|
62
|
+
const result = await reldb.sql.query(sql)
|
|
63
|
+
if (!result.success) {
|
|
64
|
+
return { success: false, error: { code: 'QUERY_FAILED', message: result.error.message } }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ✅ 直接透传
|
|
68
|
+
const result = await reldb.sql.query(sql)
|
|
69
|
+
if (!result.success)
|
|
70
|
+
return result
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 权限与安全
|
|
74
|
+
|
|
75
|
+
- [ ] 所有 API 端点和 `load` 函数有 `kit.guard.*` 守卫
|
|
76
|
+
- [ ] 所有用户输入通过 Zod Schema 校验(`kit.validate.*`)
|
|
77
|
+
- [ ] 无硬编码 API Key / 密钥
|
|
78
|
+
- [ ] 日志不输出密码、token 明文
|
|
79
|
+
|
|
80
|
+
### 日志规范
|
|
81
|
+
|
|
82
|
+
- [ ] 禁止 `console.log`,统一使用 `core.logger`
|
|
83
|
+
- [ ] 日志级别正确:
|
|
84
|
+
|
|
85
|
+
| 场景 | 级别 |
|
|
86
|
+
| --------------------- | ------------------------------ |
|
|
87
|
+
| 读操作 / 查询 | `debug` |
|
|
88
|
+
| 写操作进入 | `debug` |
|
|
89
|
+
| 写操作成功 | `info` |
|
|
90
|
+
| 校验失败 | `warn` |
|
|
91
|
+
| 系统异常 | `error` |
|
|
92
|
+
| 初始化完成 / 关闭 | `info` |
|
|
93
|
+
| 安全操作(登录/登出) | `info`(成功)/ `warn`(失败) |
|
|
94
|
+
|
|
95
|
+
- [ ] 日志消息英文、简洁动宾结构
|
|
96
|
+
- [ ] 失败日志附带 `{ error }` 或 `{ reason }`
|
|
97
|
+
|
|
98
|
+
### 类型安全
|
|
99
|
+
|
|
100
|
+
- [ ] 禁止 `any`,不确定用 `unknown` + 缩窄
|
|
101
|
+
- [ ] 函数返回类型明确标注(`HaiResult<T>` / `Promise<HaiResult<T>>`)
|
|
102
|
+
|
|
103
|
+
### i18n
|
|
104
|
+
|
|
105
|
+
- [ ] 所有用户可见文本走 i18n key(`$lib/paraglide/messages.js`)
|
|
106
|
+
- [ ] 禁止直接修改 `src/lib/paraglide` 生成文件
|
|
107
|
+
- [ ] `@h-ai/ui` 组件内置翻译,不传入翻译 props
|
|
108
|
+
|
|
109
|
+
### 代码质量
|
|
110
|
+
|
|
111
|
+
- [ ] return 语句不包含复杂逻辑(条件判断、循环、多级调用链)
|
|
112
|
+
- [ ] 无超过 2 层的 if 嵌套(使用 Early Return)
|
|
113
|
+
- [ ] 单函数 ≤ 120 行(不含注释和空行)
|
|
114
|
+
- [ ] 错误创建用错误码 + i18n 消息,无硬编码字符串
|
|
115
|
+
|
|
116
|
+
### Svelte 5 规范
|
|
117
|
+
|
|
118
|
+
- [ ] 使用 Runes 语法(`$props()`、`$state()`、`$derived()`、`$effect()`)
|
|
119
|
+
- [ ] 事件回调通过 props 传入(非 `createEventDispatcher`)
|
|
120
|
+
- [ ] 样式使用 TailwindCSS 4 + DaisyUI 5
|
|
121
|
+
|
|
122
|
+
### 初始化
|
|
123
|
+
|
|
124
|
+
- [ ] 模块初始化顺序正确:`core → db → cache → iam → ...`
|
|
125
|
+
- [ ] 初始化在 `$lib/server/init.ts` 统一管理
|
|
126
|
+
- [ ] 配置文件路径遵循 `config/_<module>.yml` 约定
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 质量门禁
|
|
131
|
+
|
|
132
|
+
审查完成后按顺序执行:
|
|
133
|
+
|
|
134
|
+
1. `pnpm typecheck`
|
|
135
|
+
2. `pnpm lint`
|
|
136
|
+
3. `pnpm test`(单元测试)
|
|
137
|
+
4. `pnpm --filter <app-name> test:e2e`(E2E 测试)
|
|
138
|
+
|
|
139
|
+
优先使用 `pnpm --filter <app-name>` 指定应用。
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 输出要求
|
|
144
|
+
|
|
145
|
+
- 列出改动文件与原因
|
|
146
|
+
- 说明是否修复了缺陷或仅做规范化
|
|
147
|
+
- 明确测试(单元 + E2E)与 lint 结果
|
|
148
|
+
- 确认重构后所有测试仍然通过
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 相关 Skills
|
|
153
|
+
|
|
154
|
+
- `hai-build`:项目架构总览与 TDD 工作流导航
|
|
155
|
+
- `hai-app-create`:TDD 驱动的应用功能创建规范
|
|
156
|
+
- `hai-app-tests`:TDD 测试规范(Red 阶段详细指引)
|