@lark-apaas/coding-steering 0.1.6-alpha.8 → 0.1.6-beta.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 (55) hide show
  1. package/package.json +1 -1
  2. package/steering/design-stack/skills/.gitkeep +0 -0
  3. package/steering/nestjs-react-fullstack/skills/authz-guide/SKILL.md +174 -0
  4. package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md +627 -0
  5. package/steering/nestjs-react-fullstack/skills/authz-guide/references/management-page-spec.md +508 -0
  6. package/steering/nestjs-react-fullstack/skills/authz-guide/references/runtime-role-controller-spec.md +203 -0
  7. package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-examples.md +90 -0
  8. package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-types.md +216 -0
  9. package/steering/nestjs-react-fullstack/skills/client-add-aily-web-chat/SKILL.md +1 -1
  10. package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +431 -0
  11. package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +39 -127
  12. package/steering/nestjs-react-fullstack/skills/devops-guide/SKILL.md +119 -0
  13. package/steering/nestjs-react-fullstack/skills/feishu/SKILL.md +0 -1
  14. package/steering/nestjs-react-fullstack/skills/feishu/references/approval.md +1 -1
  15. package/steering/nestjs-react-fullstack/skills/feishu/references/attendance.md +1 -1
  16. package/steering/nestjs-react-fullstack/skills/feishu/references/bitable.md +3 -1
  17. package/steering/nestjs-react-fullstack/skills/feishu/references/calendar.md +1 -1
  18. package/steering/nestjs-react-fullstack/skills/feishu/references/contacts.md +1 -1
  19. package/steering/nestjs-react-fullstack/skills/feishu/references/doc.md +2 -1
  20. package/steering/nestjs-react-fullstack/skills/feishu/references/drive.md +2 -1
  21. package/steering/nestjs-react-fullstack/skills/feishu/references/events.md +2 -1
  22. package/steering/nestjs-react-fullstack/skills/feishu/references/id-convert.md +2 -2
  23. package/steering/nestjs-react-fullstack/skills/feishu/references/messaging.md +1 -1
  24. package/steering/nestjs-react-fullstack/skills/feishu/references/oauth.md +2 -1
  25. package/steering/nestjs-react-fullstack/skills/feishu/references/perm.md +2 -1
  26. package/steering/nestjs-react-fullstack/skills/feishu/references/wiki.md +3 -2
  27. package/steering/nestjs-react-fullstack/skills/openapi-guide/SKILL.md +1 -0
  28. package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +601 -0
  29. package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +360 -0
  30. package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +515 -0
  31. package/steering/nestjs-react-fullstack/skills/react-hook-best-practices/SKILL.md +118 -0
  32. package/steering/nestjs-react-fullstack/skills/server-builtins-file-storage-service/SKILL.md +177 -0
  33. package/steering/nestjs-react-fullstack/skills/trigger-guide/SKILL.md +1 -0
  34. package/steering/nestjs-react-fullstack/skills/user-identity/SKILL.md +1 -0
  35. package/steering/nestjs-react-fullstack/skills/user-management-best-practices/SKILL.md +142 -0
  36. package/steering/nestjs-react-fullstack/skills_local/code-fix/SKILL.md +42 -28
  37. package/steering/nestjs-react-fullstack/skills_local/coding-guide/SKILL.md +37 -149
  38. package/steering/design-stack/skills/client-add-aily-web-chat/SKILL.md +0 -139
  39. package/steering/design-stack/skills/client-builtins-user-service/SKILL.md +0 -628
  40. package/steering/design-stack/skills/code-fix/SKILL.md +0 -246
  41. package/steering/design-stack/skills/feishu/SKILL.md +0 -270
  42. package/steering/design-stack/skills/feishu/references/approval.md +0 -214
  43. package/steering/design-stack/skills/feishu/references/attendance.md +0 -163
  44. package/steering/design-stack/skills/feishu/references/bitable.md +0 -309
  45. package/steering/design-stack/skills/feishu/references/calendar.md +0 -190
  46. package/steering/design-stack/skills/feishu/references/contacts.md +0 -160
  47. package/steering/design-stack/skills/feishu/references/doc.md +0 -256
  48. package/steering/design-stack/skills/feishu/references/drive.md +0 -103
  49. package/steering/design-stack/skills/feishu/references/events.md +0 -198
  50. package/steering/design-stack/skills/feishu/references/id-convert.md +0 -128
  51. package/steering/design-stack/skills/feishu/references/messaging.md +0 -207
  52. package/steering/design-stack/skills/feishu/references/oauth.md +0 -164
  53. package/steering/design-stack/skills/feishu/references/perm.md +0 -90
  54. package/steering/design-stack/skills/feishu/references/wiki.md +0 -164
  55. package/steering/design-stack/skills/user-identity/SKILL.md +0 -300
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: server-builtins-file-storage-service
3
+ description: "Use when server-side code needs to upload or download files programmatically, such as file format conversion, automated report generation, or background tasks that produce files. NOT for frontend file operations. 触发词:服务端文件上传, 服务端文件下载, FileService, nestjs file, 后端文件处理, 文件格式转换, 生成文件上传"
4
+ steering: true
5
+ steering-topic: server_builtins_file_storage_service
6
+ match-template-name: nestjs-react-fullstack
7
+ ---
8
+
9
+ # NestJS File Service SDK
10
+
11
+ 在 NestJS 服务端代码中使用 `FileService` 进行文件上传、下载、删除和管理。
12
+
13
+ > **使用注意**: 仅用于服务端文件处理场景,非必要文件上传下载场景请使用前端 `client-builtins-file-storage-service` skill。
14
+ > CLI 调试场景请参考 `file-storage` skill。
15
+
16
+ ## 使用场景
17
+
18
+ | 场景 | 说明 |
19
+ |------|------|
20
+ | 服务端文件处理 | 下载文件到本地处理后重新上传,如图片格式转换、docx 格式转换等 |
21
+ | 自动化任务生成文件 | 定时任务或事件触发后,根据数据库/插件内容生成文件并上传,如定时生成 PDF 报告、图片海报等 |
22
+
23
+ ## Quick Reference
24
+
25
+ | 方法 | 说明 | 返回值 |
26
+ |------|------|--------|
27
+ | `upload(file, options?)` | 上传文件 | `FileMeta` |
28
+ | `download(path)` | 下载文件(≤50MB) | `FileDownloadBuilder` |
29
+ | `remove(filePaths)` | 删除文件 | `RemoveResponse` |
30
+ | `getFileMetadata(filePath)` | 获取文件元信息 | `FileMeta` |
31
+
32
+ ## 注入 FileService
33
+
34
+ ```typescript
35
+ import { FileService } from '@lark-apaas/fullstack-nestjs-core';
36
+
37
+ @Injectable()
38
+ export class MyService {
39
+ constructor(private readonly fileService: FileService) {}
40
+ }
41
+ ```
42
+
43
+ ## 公共类型
44
+
45
+ ```typescript
46
+ interface FileMeta {
47
+ id: string;
48
+ name: string;
49
+ filePath: string;
50
+ metadata: { contentLength: string; mimeType: string };
51
+ downloadURL: string;
52
+ createdAt: string;
53
+ updatedAt: string;
54
+ bucketID: string;
55
+ }
56
+ ```
57
+
58
+ ## 核心操作
59
+
60
+ ### 1. 上传文件
61
+
62
+ **入参:**
63
+
64
+ | 参数 | 类型 | 必需 | 说明 |
65
+ |------|------|------|------|
66
+ | `file` | `FileBody` | 是 | 文件内容(支持 `Buffer`、`ReadableStream`、`ArrayBuffer`、`Blob`、`string` 等) |
67
+ | `options` | `UploadOptions` | 否 | 上传选项 |
68
+
69
+ **UploadOptions:**
70
+
71
+ | 字段 | 类型 | 必需 | 说明 |
72
+ |------|------|------|------|
73
+ | `fileName` | `string` | 否 | 文件名称 |
74
+ | `contentType` | `string` | 否 | MIME 类型 |
75
+ | `cacheControl` | `string \| number` | 否 | 缓存控制 |
76
+ | `upsert` | `boolean` | 否 | 是否覆盖已有文件 |
77
+
78
+ **返回值:** `FileMeta`
79
+
80
+ **示例:**
81
+
82
+ ```typescript
83
+ // 基本上传
84
+ const result = await this.fileService.upload(buffer);
85
+
86
+ // 带选项上传
87
+ const result = await this.fileService.upload(fileBody, {
88
+ fileName: 'report.pdf',
89
+ contentType: 'application/pdf',
90
+ upsert: false,
91
+ });
92
+ ```
93
+
94
+ ### 2. 下载文件
95
+
96
+ **入参:**
97
+
98
+ | 参数 | 类型 | 必需 | 说明 |
99
+ |------|------|------|------|
100
+ | `path` | `string` | 是 | downloadURL 或文件存储路径 |
101
+
102
+ **返回值 `DownloadResult`:**
103
+
104
+ | 字段 | 类型 | 说明 |
105
+ |------|------|------|
106
+ | `content` | `Blob`(默认)/ `ReadableStream`(流式) | 文件内容 |
107
+ | `metadata` | `FileMeta` | 文件元信息 |
108
+
109
+ > **推荐始终使用 `.asStream()`**,避免大文件导致内存溢出。直接 `await download()` 有 50MB 限制。
110
+
111
+ **示例:**
112
+
113
+ ```typescript
114
+ // 推荐:使用 downloadURL + 流式下载
115
+ const downloadURL = fileMeta.downloadURL; // 从上传返回值或数据库获取
116
+ const { content, metadata } = await this.fileService
117
+ .download(downloadURL)
118
+ .asStream();
119
+
120
+ // 也可以使用文件存储路径
121
+ const { content, metadata } = await this.fileService
122
+ .download('file.pdf')
123
+ .asStream();
124
+
125
+ // 不推荐:Blob 下载(限制 50MB,会将整个文件加载到内存)
126
+ const { content, metadata } = await this.fileService.download(downloadURL);
127
+ ```
128
+
129
+ ### 3. 删除文件
130
+
131
+ **入参:**
132
+
133
+ | 参数 | 类型 | 必需 | 说明 |
134
+ |------|------|------|------|
135
+ | `filePaths` | `string[]` | 是 | downloadURL 或文件存储路径数组 |
136
+
137
+ **返回值:** `FileMeta[]` - 删除成功的文件元信息数组
138
+
139
+ **示例:**
140
+
141
+ ```typescript
142
+ // 推荐:使用 downloadURL
143
+ const result = await this.fileService.remove([fileMeta.downloadURL]);
144
+
145
+ // 也可以使用文件存储路径
146
+ const result = await this.fileService.remove(['file1.pdf', 'file2.png']);
147
+ ```
148
+
149
+ ### 4. 获取文件元信息
150
+
151
+ **入参:**
152
+
153
+ | 参数 | 类型 | 必需 | 说明 |
154
+ |------|------|------|------|
155
+ | `filePath` | `string` | 是 | downloadURL 或文件存储路径 |
156
+
157
+ **返回值:** `FileMeta | null` - 文件元信息或 `null` 表示文件不存在或删除失败
158
+
159
+ **示例:**
160
+
161
+ ```typescript
162
+ // 推荐:使用 downloadURL
163
+ const meta = await this.fileService.getFileMetadata(fileMeta.downloadURL);
164
+
165
+ // 也可以使用文件存储路径
166
+ const meta = await this.fileService.getFileMetadata('file.pdf');
167
+ ```
168
+
169
+ ## Common Mistakes
170
+
171
+ | 错误 | 正确做法 |
172
+ |------|----------|
173
+ | 直接 `await download()` 导致内存溢出 | 始终使用 `.asStream()` 流式下载 |
174
+ | 忘记设置 `contentType` 导致浏览器无法预览 | 上传时明确指定 `contentType` |
175
+ | 直接 `new FileService()` 手动实例化 | 通过 NestJS DI 注入 `FileService` |
176
+ | 在异步回调中调用 `download()` 导致上下文丢失 | 在请求处理函数中立即调用,SDK 内部已处理上下文捕获 |
177
+ | 删除时传单个字符串 | `remove()` 参数为 `string[]` 数组 |
@@ -86,6 +86,7 @@ interface WebhookEvent {
86
86
  ```
87
87
 
88
88
  ### 指定值限制
89
+
89
90
  1. Webhook 触发器不可以设置指定值,并且告知用户。
90
91
 
91
92
  ### 代码示例
@@ -208,6 +208,7 @@ const UserInfoPanel = () => {
208
208
  ```
209
209
 
210
210
  **注意**:
211
+
211
212
  - `lark_user_id` 通过额外异步请求获取,可能晚于 `user_id` 等基础字段就绪
212
213
  - 请求失败或用户无对应飞书账号时值为 `undefined`,**必须用条件渲染**
213
214
  - `useCurrentUserProfile()` 的返回值里**不存在** `open_id`、`feishu_id`、`openId` 字段——在这个 Hook 的消费代码里飞书 ID 唯一字段名是 `lark_user_id`(仅约束本 Hook;项目其他场景调 spark `id_convert` / 通讯录 API 出现 `open_id` 是正常的)
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: user-management-best-practices
3
+ description: "Use when 决定是否创建用户表、选择用户唯一标识(user_id vs email/手机号)、设计 user_profile 字段与 UNIQUE 约束、处理防重复提交/upsert 去重,或涉及问卷、抽奖、报名、投票等用户提交场景的表结构设计。触发词:用户管理, 用户表设计, user_id, email, user_profile, 去重, 防重复提交, 批量导入用户, upsert, UNIQUE约束, UserSelect, UserDisplay, user management, deduplication"
4
+ steering: true
5
+ steering-topic: user_management_best_practices
6
+ match-template-name: nestjs-react-fullstack
7
+ ---
8
+
9
+ # 用户管理最佳实践指南
10
+
11
+ ## 概述
12
+
13
+ 本指南适用于生成涉及用户管理、问卷、抽奖、报名、投票等用户提交场景的代码。
14
+
15
+ **核心问题:**
16
+
17
+ - 妙搭 Agent 无法获取企业的所有用户数据,需要构建应用维度的用户表
18
+ - 企业无法显式感知飞书的 user_id(不透明的数字字符串,如 "1847292357012580"),无法批量导入飞书用户到应用的用户表
19
+ - 推荐使用邮箱或者手机号作为业务层唯一标识(不是 user_id)
20
+ - UserSelect 返回的 IUserProfile 对象已包含 email 和 phone_number 字段,可直接使用
21
+
22
+ **本指南解决:**
23
+
24
+ - 何时使用 UserSelect 组件 vs 何时创建用户表
25
+ - 如何设计用户业务表(正确的唯一约束)
26
+ - 防止重复提交的正确实现方式
27
+
28
+ ---
29
+
30
+ ## 核心原则:禁止事项
31
+
32
+ | 禁止操作 | 说明 | 正确做法 |
33
+ | ------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
34
+ | 不推荐实现批量导入用户的功能 | Agent 无法获取企业全量用户,批量 INSERT users 表行不通 | 推荐使用 UserSelect 组件动态选择用户 |
35
+ | 不推荐用 user_id 作为去重逻辑 | 企业不感知飞书用户 id,无法感知是谁 | 使用 email 或者手机号做唯一约束(UNIQUE(email)),不要使用姓名,可能会重复 |
36
+ | 禁止编造 user_id | Mock 数据中不能随意编造 user_id 值 | 必须使用 SQL skill 规定的测试用户列表 |
37
+ | 禁止全量查询用户列表 | 全量拉取数据会导致接口超时或 OOM | 使用后端分页查询(skip/take) |
38
+ | 禁止前端分页用户列表 | 前端分页需要全量拉取数据,导致性能问题 | 使用后端分页,前端仅控制页码 |
39
+ | 禁止只存 user_id 不存 email or 手机号 | 业务层无法用 user_id 做防重和查询 | 存储 user_profile 字段(已包含 email/phone),并在表中单独添加 email 或 phone 列用于唯一约束 |
40
+
41
+ ---
42
+
43
+ ## 决策树示例:何时创建用户表 vs 何时使用 user_profile
44
+
45
+ ```text
46
+ 需要记录用户相关业务数据?
47
+
48
+ ├─ 仅需要识别"谁"执行操作(发帖人、评论者)
49
+ │ └─ ✅ 直接使用 user_profile 类型字段
50
+ │ 示例:posts.author(user_profile)
51
+
52
+ └─ 需要存储业务特定的用户数据
53
+
54
+ ├─ 数据与具体业务资源强绑定(问卷提交、抽奖记录)
55
+ │ └─ ✅ 创建业务表 + user_profile 字段 + email 唯一约束
56
+ │ 示例:questionnaire_responses (id, respondent, email, UNIQUE(questionnaire_id, email))
57
+
58
+ └─ 数据代表"用户在系统中的身份"(员工、学生、会员)
59
+ └─ ✅ 创建人员实体表,user_profile 作为主键
60
+ 示例:employees (employee_profile PRIMARY KEY, department, position)
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 用户列表查询与性能注意事项
66
+
67
+ ### UserSelect vs 分页列表的选择
68
+
69
+ 根据场景选择正确的用户选择方式:
70
+
71
+ | 场景 | 使用组件 | 数据加载方式 | 典型数量 |
72
+ | ------------------ | ---------------- | ------------ | -------- |
73
+ | 表单选择用户 | UserSelect | 搜索驱动 | 1-10 |
74
+ | 用户管理后台 | Table + 后端分页 | 分页加载 | 100+ |
75
+ | 问卷提交记录列表 | Table + 后端分页 | 分页加载 | 100+ |
76
+ | 抽奖参与者名单展示 | Table + 后端分页 | 分页加载 | 100+ |
77
+
78
+ - **UserSelect 组件**:搜索驱动,返回完整 IUserProfile 对象(含 email、phone_number)。详细用法参考 **client-builtins-user-service**
79
+ - **Table + 后端分页**:详细实现参考 **table-skill**
80
+
81
+ ### 分页实现
82
+
83
+ 用户列表必须使用后端分页,禁止前端全量拉取。具体实现参考 **table-skill**。
84
+
85
+ ### 动态创建用户记录
86
+
87
+ **何时动态创建:**
88
+
89
+ 1. **首次提交场景**:用户首次提交问卷/参与抽奖时,使用 `upsert` 方法
90
+ 2. **首次登录场景**:用户首次登录系统时,创建用户记录
91
+
92
+ ## 常见错误速查表
93
+
94
+ | 错误模式 | 问题 | 正确做法 | 影响 |
95
+ | ----------------------- | ---------------------- | ------------------------------------------------------------ | ----------- |
96
+ | 批量 INSERT users | Agent 无法获取全量用户 | 使用 UserSelect 组件 | 🔴 CRITICAL |
97
+ | `UNIQUE(user_id)` | 企业不认识 user_id | `UNIQUE(email)` | 🔴 CRITICAL |
98
+ | 编造 user_id | Mock 数据无效 | 使用测试用户列表 | 🔴 CRITICAL |
99
+ | 全量查询用户列表 | 接口超时或 OOM | 使用后端分页(skip/take) | 🔴 CRITICAL |
100
+ | 前端分页(全量拉取) | 首次加载超时 | 使用后端分页 | 🔴 CRITICAL |
101
+ | 只存 user_id 不存 email | 无法做业务层防重 | 存储 user_profile(已含 email),并添加 email 列用于唯一约束 | 🟠 BUG |
102
+ | 直接显示 user_id | 用户体验差 | 使用 UserDisplay 组件 | 🟡 UX |
103
+ | 创建 users 基础表 | 无法同步飞书数据 | 使用 user_profile 类型 | 🟠 DESIGN |
104
+
105
+ ## 相关技能参考
106
+
107
+ - **client-builtins-user-service**: UserSelect 详细用法、IUserProfile 类型定义、UserDisplay 组件、useCurrentUserProfile hook
108
+ - **sql**: user_profile 类型约束、JSONB 验证规则、测试用户列表
109
+ - **table-skill**: Table 组件使用、后端分页实现、前端分页最佳实践
110
+ - **authz-guide**: 结合基于角色的访问控制(如:只允许用户查看/编辑自己的提交)
111
+
112
+ ## 自查清单
113
+
114
+ ### 表设计
115
+
116
+ - [ ] 判断是否需要创建用户表(参考决策树)
117
+ - [ ] 人员实体表使用 `user_profile PRIMARY KEY`
118
+ - [ ] 业务表使用 `id UUID PRIMARY KEY` + `user_profile` 字段
119
+ - [ ] 业务表包含 `email VARCHAR(255)` or `phone VARCHAR(255)` 字段
120
+ - [ ] 设置了正确的唯一约束(使用 email or 手机号,如 `UNIQUE(resource_id, email)`)
121
+
122
+ ### 前端实现
123
+
124
+ - [ ] 使用 UserSelect 组件而非手动输入
125
+ - [ ] 使用 UserDisplay 展示用户(不直接显示 user_id)
126
+ - [ ] 用户列表使用后端分页(不全量拉取)
127
+ - [ ] Table 组件的 onChange 触发后端请求
128
+
129
+ ### 后端实现
130
+
131
+ - [ ] Entity 定义了正确的唯一约束(`@Unique(['resourceId', 'email'])` or `@Unique(['resourceId', 'phone'])`)
132
+ - [ ] 错误处理包含唯一约束冲突(409 Conflict)
133
+ - [ ] 存储 user_profile 字段(已包含 email/phone)
134
+ - [ ] 在表中单独添加 email 或 phone 列,用于唯一约束和索引查询
135
+ - [ ] 列表接口使用 skip/take 分页查询
136
+ - [ ] 返回 pagination 对象(page, pageSize, total)
137
+
138
+ ### Mock 数据
139
+
140
+ - [ ] user_id 使用测试用户列表(不编造)
141
+ - [ ] user_profile 中的 email/phone 与 user_id 对应正确
142
+ - [ ] 测试了重复提交场景(唯一约束生效)
@@ -1,9 +1,6 @@
1
1
  ---
2
2
  name: code-fix
3
- description: Use when encountering code errors such as import failures, TypeScript/Dto type mismatches, JSX syntax issues, API call exceptions (traceid troubleshooting), production log troubleshooting that should route through miaoda-cli, route 404 errors, or **lucide-react icon not found / duplicate identifier / barrel-export naming conflicts**. 触发词:导入错误, 模块解析失败, 类型错误, Dto不匹配, JSX语法, API异常, traceid, 线上日志, 线上日志查询, 查询线上日志, 路由404, code fix, debugging, lucide-react import error, icon not found, 图标不存在, Cannot find name, 标识符重复, no-redeclare, export 冲突, 桶导出冲突, dual export, "请修复错误" 通用排错
4
- steering: true
5
- steering-topic: code_fix
6
- match-template-name: nestjs-react-fullstack
3
+ description: Use when encountering code errors such as import failures, TypeScript/Dto type mismatches, JSX syntax issues, API call exceptions, route 404 errors, PostgresError connection verification failed, or **lucide-react icon not found / duplicate identifier / barrel-export naming conflicts**. 触发词:导入错误, 模块解析失败, 类型错误, Dto不匹配, JSX语法, API异常, 路由404, code fix, debugging, lucide-react import error, icon not found, 图标不存在, Cannot find name, 标识符重复, no-redeclare, export 冲突, 桶导出冲突, dual export, "请修复错误" 通用排错, please re-obtain a valid database connection
7
4
  ---
8
5
 
9
6
 
@@ -70,7 +67,7 @@ import { ListPlus, Cake, Home, Building, Twitter } from "lucide-react";
70
67
 
71
68
  1. **不确定就查**:图标名不在你已知列表里 → 先 `Read packages/client/lucide-react/iconMappings.json`(或 lucide-react 包的 d.ts 导出列表)确认存在,再写 import
72
69
  2. **替换图标必须连同 import 列表一起检查**:把旧图标替换成新图标时,**必须先 grep 当前文件 import 列表**确认新名未已 import,避免触发 LSP "标识符重复" / `no-redeclare`
73
- 3. **LSP 警告是硬约束**:multi_edit / 写代码后看到 LSP 任意 "Cannot find name" / "标识符重复" → **必须先修完警告才能 commit**,禁止带 LSP 错误跑 `commit_task`
70
+ 3. **LSP 警告是硬约束**:写代码后看到 LSP 任意 "Cannot find name" / "标识符重复" → **必须先修完警告才能 commit**,禁止带 LSP 错误提交
74
71
 
75
72
  ### 同名 export 重复(修复 SOP)
76
73
 
@@ -82,30 +79,38 @@ import { ListPlus, Cake, Home, Building, Twitter } from "lucide-react";
82
79
  2. 预防规则(跨子组件常量前缀化、行内/桶导出二选一)见 `coding-guide` 的「TypeScript 规范 · 命名约定」与「文件命名约定 · 导入导出」
83
80
 
84
81
  ### 前端 Dto 类型使用错误
82
+
85
83
  **问题描述**:代码中使用的 Dto 类型属性与实际定义不一致,导致 TypeScript 类型检查报错
86
84
 
87
85
  **核心原则**:遇到类型错误,先查 `@client/src/api/gen/types.gen.ts` 确认定义,再修改代码
88
86
 
89
87
  **错误示例**:
88
+
90
89
  - 示例1: 赋值时缺失必需属性
90
+
91
91
  ```
92
92
  Property 'dueDate' is missing in type {...} but required in type 'CreateBorrowRecordDto'
93
93
  ```
94
94
 
95
95
  - 示例2:访问不存在的属性
96
+
96
97
  ```
97
98
  Property 'avatar' does not exist on type 'LotteryParticipantResponseDto'
98
99
  ```
99
100
 
100
101
  - 示例3:导入不存在的类型
102
+
101
103
  ```
102
104
  Module '"@client/src/api/gen"' has no exported member named 'DiscrepancyResponseDto'
103
105
  ```
106
+
104
107
  **解决步骤**:
108
+
105
109
  1. 在 `@client/src/api/gen/types.gen.ts` 中搜索目标Dto实际定义
106
110
  2. 确认类型名称是否正确,明确完整定义
107
111
 
108
112
  **检查清单**:
113
+
109
114
  - [ ] 已查看 `@client/src/api/gen/types.gen.ts` 中的类型定义
110
115
  - [ ] 已对比代码使用与实际定义的差异
111
116
  - [ ] 已根据实际定义修正代码
@@ -156,22 +161,11 @@ Module '"@client/src/api/gen"' has no exported member named 'DiscrepancyResponse
156
161
 
157
162
  ## 调用生成的 API 异常
158
163
 
159
- ### 需要查询线上日志时使用 miaoda-cli
160
-
161
- **适用场景**:仅当用户提供 traceid、logid、线上报错、发布错误日志、线上运行日志、线上链路追踪,或排查必须依赖线上前端/后端日志。
164
+ ### 查看本地运行日志
162
165
 
163
- **处理要求**:引导并使用 `miaoda-cli` skill 查询线上日志。优先通过 `miaoda observability log/trace` 查询线上运行日志与链路追踪;排查发布错误时使用 `miaoda deploy error-log`。本地开发日志、构建日志、测试日志、浏览器控制台日志不在此范围内,按当前技能或对应工具排查。拿到线上日志后再回到本技能继续定位与修复代码问题。
166
+ **适用场景**:本地开发期 API 调用异常、控制台报错、后端进程行为异常。
164
167
 
165
- ### 用户提供了 traceid,排查错误
166
-
167
- **解决方案**:使用 `miaoda-cli` skill 读取线上前端与后端日志,获取详细错误
168
-
169
- **排查示例**:
170
-
171
- 用户输入:8ae6724e-277e-4d51-afbf-b524b654f27f 看看这个 trace 为什么报错了
172
- 排查路径:
173
- 1. 使用 `miaoda-cli` skill 查询线上服务端与 trace 日志
174
- 2. 根据服务端日志中对应的日志内容修复对应代码逻辑
168
+ **处理要求**:本地日志由 `scripts/dev.js` 落到 `logs/` 目录(`dev.log`、`server.log` 及对应的 `.std.log`),直接 `cat` / `tail -F` 查看;浏览器侧错误看 DevTools Console。本地版**不接管线上日志/traceid 排查链路**(那条路径由发布平台和 oncall 工具承接)。
175
169
 
176
170
  ### 调用 API 客户端时后端返回异常
177
171
 
@@ -195,25 +189,45 @@ Module '"@client/src/api/gen"' has no exported member named 'DiscrepancyResponse
195
189
  错误信息:服务内部错误
196
190
  错误堆栈(可选):Error: 这是测试异常:HelloController.getConfig方法故意抛出的错误\n at HelloController.getConfig (/home/gem/workspace/dist/server/modules/hello/hello.controller.js:17:15)\n at /home/gem/workspace/node_modules/@nestjs/core/router/router-execution-context.js:38:29\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
197
191
 
198
- 2. 如果错误堆栈存在,优先按照错误堆栈中的相关文件与行列号找到对应文件,读取内容并启发式分析
199
- 3. 如果错误堆栈不存在,按照请求的 URL 找到对应的 controller,检查其中的逻辑,启发式的分析依赖。如发现问题可以直接处理。若仍未发现问题,可以在 controller 抛出的错误对象上增加 `message` 属性,改变 message 内容方便 debug。你可以在增加完之后让用户重新请求触发问题。
192
+ 1. 如果错误堆栈存在,优先按照错误堆栈中的相关文件与行列号找到对应文件,读取内容并启发式分析
193
+ 2. 如果错误堆栈不存在,按照请求的 URL 找到对应的 controller,检查其中的逻辑,启发式的分析依赖。如发现问题可以直接处理。若仍未发现问题,可以在 controller 抛出的错误对象上增加 `message` 属性,改变 message 内容方便 debug。你可以在增加完之后让用户重新请求触发问题。
200
194
 
201
195
  ```typescript
202
196
  try {
203
- // some logic
197
+ // some logic
204
198
  } catch (err) {
205
199
  // 后端异常必须在后端打印日志
206
- this.logger.error('...')
207
- // 后端异常同时需要抛出到前端,方便修复
208
- // 构造为 http-errors compatible 的对象
209
- err.statusCode = 500;
210
- err.message = err.stack; // 抛出 stack 信息,保障有足够的错误内容透出
211
- throw err;
200
+ this.logger.error('...')
201
+ // 后端异常同时需要抛出到前端,方便修复
202
+ // 构造为 http-errors compatible 的对象
203
+ err.statusCode = 500;
204
+ err.message = err.stack; // 抛出 stack 信息,保障有足够的错误内容透出
205
+ throw err;
212
206
  }
213
207
  ```
214
208
 
215
209
  注意:优先让错误信息中包含堆栈信息,以更精确的定位错误发生位置。
216
210
 
211
+ ## 数据库连接问题
212
+
213
+ ### PG 连接串过期 / 失效
214
+
215
+ **问题描述**:`npm run dev` 启动后,后端访问数据库时报类似如下错误(关键特征是 `connection verification failed` + `expired or invalid connection link`,endpoint 域名以实际为准):
216
+
217
+ ```
218
+ PostgresError: connection verification failed for endpoint "<pg-endpoint>": expired or invalid connection link, please re-obtain a valid database connection
219
+ ```
220
+
221
+ **根因**:PG connection string 注入在 `.env.local` 中,由于安全策略,连接串 **5 天过期**。`npm run dev` 启动时只会读取一次 `.env.local`,过期后旧连接串就会触发上述报错。
222
+
223
+ **修复方案**:**重新运行 `npm run dev`** 即可。启动脚本会重新拉取并注入最新的 PG connection string 到 `.env.local`,新进程读取到的就是有效连接串。不要去改后端代码或 ORM 配置。
224
+
225
+ **注意**:
226
+
227
+ - 不要尝试手工编辑 `.env.local` 里的 PG connection string,连接串由启动流程统一管理
228
+ - 不要把这个错误当成业务代码 bug 去排查 controller / service / 数据库 schema
229
+ - 重启 `npm run dev` 后若仍报同样错误,再按其它通用排查路径处理
230
+
217
231
  ## 路由和导航问题
218
232
 
219
233
  ### 路由 404 错误诊断