@lark-apaas/coding-steering 0.1.6-alpha.8 → 0.1.6-alpha.9
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/package.json +1 -1
- package/steering/design-stack/skills/.gitkeep +0 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/SKILL.md +174 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md +621 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/management-page-spec.md +505 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/runtime-role-controller-spec.md +203 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-examples.md +90 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-types.md +216 -0
- package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +405 -0
- package/steering/nestjs-react-fullstack/skills/devops-guide/SKILL.md +119 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +582 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +357 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +513 -0
- package/steering/nestjs-react-fullstack/skills/react-hook-best-practices/SKILL.md +118 -0
- package/steering/nestjs-react-fullstack/skills/server-builtins-file-storage-service/SKILL.md +177 -0
- package/steering/nestjs-react-fullstack/skills/user-management-best-practices/SKILL.md +142 -0
- package/steering/design-stack/skills/client-add-aily-web-chat/SKILL.md +0 -139
- package/steering/design-stack/skills/client-builtins-user-service/SKILL.md +0 -628
- package/steering/design-stack/skills/code-fix/SKILL.md +0 -246
- package/steering/design-stack/skills/feishu/SKILL.md +0 -270
- package/steering/design-stack/skills/feishu/references/approval.md +0 -214
- package/steering/design-stack/skills/feishu/references/attendance.md +0 -163
- package/steering/design-stack/skills/feishu/references/bitable.md +0 -309
- package/steering/design-stack/skills/feishu/references/calendar.md +0 -190
- package/steering/design-stack/skills/feishu/references/contacts.md +0 -160
- package/steering/design-stack/skills/feishu/references/doc.md +0 -256
- package/steering/design-stack/skills/feishu/references/drive.md +0 -103
- package/steering/design-stack/skills/feishu/references/events.md +0 -198
- package/steering/design-stack/skills/feishu/references/id-convert.md +0 -128
- package/steering/design-stack/skills/feishu/references/messaging.md +0 -207
- package/steering/design-stack/skills/feishu/references/oauth.md +0 -164
- package/steering/design-stack/skills/feishu/references/perm.md +0 -90
- package/steering/design-stack/skills/feishu/references/wiki.md +0 -164
- package/steering/design-stack/skills/user-identity/SKILL.md +0 -300
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
---
|
|
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: design-stack
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# 代码问题诊断与修复指南
|
|
11
|
-
|
|
12
|
-
## 概述
|
|
13
|
-
|
|
14
|
-
本文档提供开发过程中常见问题的诊断与修复流程,涵盖导入错误、语法问题、API 调用异常、路由配置等。规范性内容请参考 `coding-guide`,React Hook 相关问题请参考 `react-hook-best-practices`。
|
|
15
|
-
|
|
16
|
-
## 导入和模块错误
|
|
17
|
-
|
|
18
|
-
### 缺少导入声明
|
|
19
|
-
|
|
20
|
-
**问题描述**:使用组件或图标时没有正确导入
|
|
21
|
-
|
|
22
|
-
**解决方案**:
|
|
23
|
-
|
|
24
|
-
- 使用前务必验证技术栈中组件的可用性
|
|
25
|
-
- 添加正确的导入语句
|
|
26
|
-
- 检查组件是否在当前技术栈中可用
|
|
27
|
-
|
|
28
|
-
**代码示例**:
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
// 正确的导入方式
|
|
32
|
-
import { Button } from '@client/src/components/ui/button';
|
|
33
|
-
import { Input } from "@client/src/components/ui/input"
|
|
34
|
-
import { ListPlus, Cake, Home, Building, Twitter } from "lucide-react";
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 导入路径错误诊断
|
|
38
|
-
|
|
39
|
-
**问题描述**:错误的导入路径导致模块解析失败
|
|
40
|
-
|
|
41
|
-
**诊断步骤**:
|
|
42
|
-
|
|
43
|
-
1. 检查是否使用了项目别名(`@client/`、`@server/`、`@shared/`),而非相对路径 `../`
|
|
44
|
-
2. 确认别名对应的实际路径是否正确(如 `@client/` 对应 `client/`)
|
|
45
|
-
3. 验证目标文件是否存在于指定路径
|
|
46
|
-
|
|
47
|
-
> 路径别名的完整定义请参考 `coding-guide` 的"全局编码约定"部分。
|
|
48
|
-
|
|
49
|
-
### 组件可用性验证
|
|
50
|
-
|
|
51
|
-
**问题描述**:使用不存在的组件或错误的导入路径
|
|
52
|
-
|
|
53
|
-
**解决方案**:
|
|
54
|
-
|
|
55
|
-
- 严格遵循技术栈文档中的可用组件列表
|
|
56
|
-
- 验证组件是否存在于指定路径
|
|
57
|
-
- 检查组件参数是否匹配
|
|
58
|
-
|
|
59
|
-
**检查清单**:
|
|
60
|
-
|
|
61
|
-
- [ ] 导入路径是否正确
|
|
62
|
-
- [ ] 组件是否存在
|
|
63
|
-
- [ ] 组件参数是否匹配
|
|
64
|
-
|
|
65
|
-
### lucide-react 图标存在性核查
|
|
66
|
-
|
|
67
|
-
**问题描述**:从 `lucide-react` 导入的图标名拼错或臆造,渲染时得到 `undefined`,触发 React "type is invalid" 或 LSP "Cannot find name"。
|
|
68
|
-
|
|
69
|
-
**预防规则(写 import 时主动核查,而非事后救援)**:
|
|
70
|
-
|
|
71
|
-
1. **不确定就查**:图标名不在你已知列表里 → 先 `Read packages/client/lucide-react/iconMappings.json`(或 lucide-react 包的 d.ts 导出列表)确认存在,再写 import
|
|
72
|
-
2. **替换图标必须连同 import 列表一起检查**:把旧图标替换成新图标时,**必须先 grep 当前文件 import 列表**确认新名未已 import,避免触发 LSP "标识符重复" / `no-redeclare`
|
|
73
|
-
3. **LSP 警告是硬约束**:multi_edit / 写代码后看到 LSP 任意 "Cannot find name" / "标识符重复" → **必须先修完警告才能 commit**,禁止带 LSP 错误跑 `commit_task`
|
|
74
|
-
|
|
75
|
-
### 同名 export 重复(修复 SOP)
|
|
76
|
-
|
|
77
|
-
**症状**:LSP 报 "标识符 X 重复" / `no-redeclare`。
|
|
78
|
-
|
|
79
|
-
**修复步骤**:
|
|
80
|
-
|
|
81
|
-
1. 整个项目 `grep "export.*\bX\b"` 找所有同名 export 一并处理,**不能只改单点**——首次出 bug 时同类常已在多处复制粘贴
|
|
82
|
-
2. 预防规则(跨子组件常量前缀化、行内/桶导出二选一)见 `coding-guide` 的「TypeScript 规范 · 命名约定」与「文件命名约定 · 导入导出」
|
|
83
|
-
|
|
84
|
-
### 前端 Dto 类型使用错误
|
|
85
|
-
**问题描述**:代码中使用的 Dto 类型属性与实际定义不一致,导致 TypeScript 类型检查报错
|
|
86
|
-
|
|
87
|
-
**核心原则**:遇到类型错误,先查 `@client/src/api/gen/types.gen.ts` 确认定义,再修改代码
|
|
88
|
-
|
|
89
|
-
**错误示例**:
|
|
90
|
-
- 示例1: 赋值时缺失必需属性
|
|
91
|
-
```
|
|
92
|
-
Property 'dueDate' is missing in type {...} but required in type 'CreateBorrowRecordDto'
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
- 示例2:访问不存在的属性
|
|
96
|
-
```
|
|
97
|
-
Property 'avatar' does not exist on type 'LotteryParticipantResponseDto'
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
- 示例3:导入不存在的类型
|
|
101
|
-
```
|
|
102
|
-
Module '"@client/src/api/gen"' has no exported member named 'DiscrepancyResponseDto'
|
|
103
|
-
```
|
|
104
|
-
**解决步骤**:
|
|
105
|
-
1. 在 `@client/src/api/gen/types.gen.ts` 中搜索目标Dto实际定义
|
|
106
|
-
2. 确认类型名称是否正确,明确完整定义
|
|
107
|
-
|
|
108
|
-
**检查清单**:
|
|
109
|
-
- [ ] 已查看 `@client/src/api/gen/types.gen.ts` 中的类型定义
|
|
110
|
-
- [ ] 已对比代码使用与实际定义的差异
|
|
111
|
-
- [ ] 已根据实际定义修正代码
|
|
112
|
-
- [ ] 已验证修改后类型使用正确
|
|
113
|
-
|
|
114
|
-
## JSX 和语法错误
|
|
115
|
-
|
|
116
|
-
### 特殊字符转义处理
|
|
117
|
-
|
|
118
|
-
**问题描述**:JSX 中未转义的特殊字符导致渲染错误
|
|
119
|
-
|
|
120
|
-
**常见字符**:`<`, `>`, `{`, `}`, `&`, `"`, `` ` ``
|
|
121
|
-
|
|
122
|
-
**解决方案**:
|
|
123
|
-
|
|
124
|
-
| 字符 | HTML 实体 | 使用场景 |
|
|
125
|
-
|------|-----------|----------|
|
|
126
|
-
| `<` | `<` | 显示小于号 |
|
|
127
|
-
| `>` | `>` | 显示大于号 |
|
|
128
|
-
| `&` | `&` | 显示与符号 |
|
|
129
|
-
| `"` | `"` | 显示双引号 |
|
|
130
|
-
| `'` | `'` | 显示单引号 |
|
|
131
|
-
|
|
132
|
-
**代码示例**:
|
|
133
|
-
|
|
134
|
-
```jsx
|
|
135
|
-
// ✅ 正确的特殊字符处理
|
|
136
|
-
<div>
|
|
137
|
-
<p>价格: < 100元</p>
|
|
138
|
-
<p>公司: A & B 科技</p>
|
|
139
|
-
<p>标题: "Hello World"</p>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
// ❌ 错误的写法
|
|
143
|
-
<div>
|
|
144
|
-
<p>价格: < 100元</p> {/* 会被解析为标签 */}
|
|
145
|
-
<p>公司: A & B 科技</p> {/* 可能导致解析错误 */}
|
|
146
|
-
</div>
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
## API 生成错误
|
|
150
|
-
|
|
151
|
-
### 调用 API 客户端时参数与预期不符
|
|
152
|
-
|
|
153
|
-
**问题描述**:调用 API 客户端时发现传参与后端定义不相同
|
|
154
|
-
|
|
155
|
-
**解决方案**:检查后端 Swagger 注解中是否对 DTO 对象正确做了注解
|
|
156
|
-
|
|
157
|
-
## 调用生成的 API 异常
|
|
158
|
-
|
|
159
|
-
### 需要查询线上日志时使用 miaoda-cli
|
|
160
|
-
|
|
161
|
-
**适用场景**:仅当用户提供 traceid、logid、线上报错、发布错误日志、线上运行日志、线上链路追踪,或排查必须依赖线上前端/后端日志。
|
|
162
|
-
|
|
163
|
-
**处理要求**:引导并使用 `miaoda-cli` skill 查询线上日志。优先通过 `miaoda observability log/trace` 查询线上运行日志与链路追踪;排查发布错误时使用 `miaoda deploy error-log`。本地开发日志、构建日志、测试日志、浏览器控制台日志不在此范围内,按当前技能或对应工具排查。拿到线上日志后再回到本技能继续定位与修复代码问题。
|
|
164
|
-
|
|
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. 根据服务端日志中对应的日志内容修复对应代码逻辑
|
|
175
|
-
|
|
176
|
-
### 调用 API 客户端时后端返回异常
|
|
177
|
-
|
|
178
|
-
**解决方案**:检查并修复后端的实现,请勿修改 API 客户端相关代码
|
|
179
|
-
|
|
180
|
-
**排查示例**:
|
|
181
|
-
|
|
182
|
-
异常报错如下
|
|
183
|
-
|
|
184
|
-
```json
|
|
185
|
-
[ERROR]{"type":"HTTP Response","url":"/spark/p/app_4hnezxn4uy49c/api/hello/config","method":"GET","status":500,"statusText":"Internal Server Error","message":"Request failed with status code 500","responseData":{"code":"INTERNAL_ERROR","message":"服务器内部错误","success":false,"data":null,"timestamp":1761048452289,"httpStatus":500,"error":{"code":"INTERNAL_ERROR","message":"服务器内部错误","stack":"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)"}},"responseTime":283}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
排查步骤:
|
|
189
|
-
|
|
190
|
-
1. 分析错误信息
|
|
191
|
-
|
|
192
|
-
根据 ERROR 可以得知如下关键信息:
|
|
193
|
-
请求的 URL:/spark/p/app_4hnezxn4uy49c/api/hello/config
|
|
194
|
-
请求 METHOD:GET
|
|
195
|
-
错误信息:服务内部错误
|
|
196
|
-
错误堆栈(可选):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
|
-
|
|
198
|
-
2. 如果错误堆栈存在,优先按照错误堆栈中的相关文件与行列号找到对应文件,读取内容并启发式分析
|
|
199
|
-
3. 如果错误堆栈不存在,按照请求的 URL 找到对应的 controller,检查其中的逻辑,启发式的分析依赖。如发现问题可以直接处理。若仍未发现问题,可以在 controller 抛出的错误对象上增加 `message` 属性,改变 message 内容方便 debug。你可以在增加完之后让用户重新请求触发问题。
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
try {
|
|
203
|
-
// some logic
|
|
204
|
-
} catch (err) {
|
|
205
|
-
// 后端异常必须在后端打印日志
|
|
206
|
-
this.logger.error('...')
|
|
207
|
-
// 后端异常同时需要抛出到前端,方便修复
|
|
208
|
-
// 构造为 http-errors compatible 的对象
|
|
209
|
-
err.statusCode = 500;
|
|
210
|
-
err.message = err.stack; // 抛出 stack 信息,保障有足够的错误内容透出
|
|
211
|
-
throw err;
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
注意:优先让错误信息中包含堆栈信息,以更精确的定位错误发生位置。
|
|
216
|
-
|
|
217
|
-
## 路由和导航问题
|
|
218
|
-
|
|
219
|
-
### 路由 404 错误诊断
|
|
220
|
-
|
|
221
|
-
**问题描述**:新页面无法访问或出现 404 错误
|
|
222
|
-
|
|
223
|
-
**诊断步骤**:
|
|
224
|
-
|
|
225
|
-
1. 确认页面组件已在 `client/src/app.tsx` 中注册路由
|
|
226
|
-
2. 验证路由路径拼写正确(注意大小写敏感)
|
|
227
|
-
3. 检查导航链接路径与路由定义是否完全匹配
|
|
228
|
-
4. 确认动态路由参数正确传递
|
|
229
|
-
|
|
230
|
-
> 路由配置规范和导航开发详见 `coding-guide` 的"页面与路由开发规范"部分。
|
|
231
|
-
|
|
232
|
-
## 性能优化
|
|
233
|
-
|
|
234
|
-
### React Hook 相关问题
|
|
235
|
-
|
|
236
|
-
useEffect 无限循环、依赖数组管理、useMemo/useCallback 记忆化等问题,请参考 `react-hook-best-practices` 技能,该技能涵盖 React 19 新特性、派生状态、事件 vs Effect 等完整内容。
|
|
237
|
-
|
|
238
|
-
## 错误处理快速参考
|
|
239
|
-
|
|
240
|
-
| 场景 | 做法 | 注意事项 |
|
|
241
|
-
|------|------|----------|
|
|
242
|
-
| 用户反馈 | 使用 `toast` (sonner) 显示友好消息 | 消息简洁、可操作,避免暴露技术细节 |
|
|
243
|
-
| 前端日志 | 使用 `logger` (`@lark-apaas/client-toolkit/logger`) | 禁止 console;参数为 string,对象需 `JSON.stringify` |
|
|
244
|
-
| 后端日志 | 使用 `@nestjs/common` 的 Logger | 禁止 console;参数为 string,对象需 `JSON.stringify` |
|
|
245
|
-
| 业务错误 | 区分预期错误与意外错误 | 预期错误用 `logger.warn`,意外错误用 `logger.error` |
|
|
246
|
-
| 异常处理 | 禁止静默处理异常 | 必须显示明确的错误信息,参考 `coding-guide` 相关规范 |
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: feishu
|
|
3
|
-
description: Use when integrating with Feishu (飞书) or Lark Open Platform using @larksuiteoapi/node-sdk in NestJS/TypeScript, including converting miaoda userId ↔ Feishu open_id / union_id / employee_id via spark id_convert. 触发词:飞书, Feishu, Lark, 飞书开放平台, 飞书SDK, 飞书机器人, 飞书消息, 飞书日历, 飞书审批, 多维表格, 飞书通讯录, 飞书考勤, 飞书文档, 云文档, 飞书云空间, 飞书知识库, open_id, union_id, employee_id, 飞书open_id, 飞书union_id, 飞书employee_id, open_id转换, union_id转换, 妙搭飞书ID互转, 妙搭userId转open_id, open_id反查妙搭, spark id_convert, id_convert
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Feishu Node SDK — NestJS/TypeScript 集成指南
|
|
7
|
-
|
|
8
|
-
使用 `@larksuiteoapi/node-sdk` 在 NestJS/TypeScript 项目中集成飞书开放平台。
|
|
9
|
-
|
|
10
|
-
## 集成工作流
|
|
11
|
-
|
|
12
|
-
**在写任何飞书代码之前,必须按顺序走完以下步骤:**
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
Step 1: 检查项目现状
|
|
16
|
-
├─ 检查 package.json 是否有 @larksuiteoapi/node-sdk
|
|
17
|
-
├─ 检查是否有 FeishuService / FeishuModule
|
|
18
|
-
└─ 检查 FeishuService 是否已有凭证(appId 非占位符)
|
|
19
|
-
|
|
20
|
-
↓ 已有凭证(FeishuService 已配置)→ 跳到 Step 4
|
|
21
|
-
↓ 没有 → Step 2
|
|
22
|
-
|
|
23
|
-
Step 2: 引导用户创建飞书自建应用
|
|
24
|
-
- 向用户发送以下完整操作清单(假设用户对飞书开放平台不熟悉):
|
|
25
|
-
|
|
26
|
-
1. 打开 https://open.feishu.cn/app → 点击「创建企业自建应用」
|
|
27
|
-
2. 进入应用 →「凭证与基础信息」页面 → 复制 App ID 和 App Secret
|
|
28
|
-
3. 「添加应用能力」→ 开启「机器人」(发消息必须)
|
|
29
|
-
4. 「权限管理」→ 申请以下权限:[根据用户需求列出具体权限,见"常用场景所需权限"表]
|
|
30
|
-
5. 「版本管理与发布」→ 创建版本 → 提交审核(企业管理员审核通过后应用生效)
|
|
31
|
-
|
|
32
|
-
- 清单发送完毕后,补充说明:
|
|
33
|
-
「完成以上步骤后,请将 App ID 和 App Secret 发给我,我来帮你写代码。」
|
|
34
|
-
|
|
35
|
-
- 等待用户提供真实凭证,**不要使用占位符生成代码**
|
|
36
|
-
|
|
37
|
-
Step 3: 确认权限 + 保存凭证
|
|
38
|
-
- 根据用户需求告知需申请的权限(见"常用场景所需权限"表)
|
|
39
|
-
- 将凭证写入 FeishuService 源码常量(见"NestJS 模块设置")
|
|
40
|
-
- 将 App ID 和用途写入 agent.md(见"飞书应用信息持久化"模板)
|
|
41
|
-
|
|
42
|
-
Step 4: 安装依赖
|
|
43
|
-
- npm install @larksuiteoapi/node-sdk
|
|
44
|
-
|
|
45
|
-
Step 5: 编写代码
|
|
46
|
-
- 创建 FeishuService(将凭证写入模块顶部常量)
|
|
47
|
-
- 创建 FeishuModule
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## 代码生成约束
|
|
51
|
-
|
|
52
|
-
- 所有飞书 API 调用必须放在 NestJS `@Injectable()` Service 的方法中
|
|
53
|
-
- `client` 始终通过 `this.client`(FeishuService 内部)或 `this.feishuService.getClient()`(其他 Service)获取
|
|
54
|
-
- 数据库操作使用 Drizzle ORM 注入实例(`@Inject(DRIZZLE_DATABASE)`),不使用裸 SQL 或自建连接
|
|
55
|
-
- 禁止生成独立运行的 `.ts` 脚本文件
|
|
56
|
-
|
|
57
|
-
> reference 文件中的代码示例省略了 Service 类包裹,实际使用时 `client` 应替换为 `this.client` 或从注入的 FeishuService 获取。
|
|
58
|
-
|
|
59
|
-
## 插件优先原则
|
|
60
|
-
|
|
61
|
-
以下飞书能力存在**平台一方插件**,优先使用插件,不要直接用 node-sdk:
|
|
62
|
-
|
|
63
|
-
| 需求 | 应使用 | 说明 |
|
|
64
|
-
|------|--------|------|
|
|
65
|
-
| 发送飞书文本/富文本消息 | **插件**(参考 `plugin_guide`) | 插件在 Client 侧调用,无需后端 Service |
|
|
66
|
-
| **发送飞书消息卡片** | **node-sdk**(本文档) | 插件不支持卡片消息,必须用 node-sdk |
|
|
67
|
-
| 飞书多维表格增删改查 | **插件**(参考 `plugin_guide`) | 同上 |
|
|
68
|
-
| 创建飞书群组 | **插件**(参考 `plugin_guide`) | 同上 |
|
|
69
|
-
|
|
70
|
-
> **操作步骤(插件)**:先调用 `plugin_guide` 查询候选插件实例 → 调用 `get_plugin_ai_json` 获取 schema → 按文档生成 Client 侧调用代码。
|
|
71
|
-
|
|
72
|
-
以下能力**无对应插件**,使用本文档的 node-sdk 方案:
|
|
73
|
-
消息卡片、日历、审批、云文档、云空间、知识库、权限管理、通讯录、考勤、事件订阅、OAuth 授权。
|
|
74
|
-
|
|
75
|
-
## Quick Reference
|
|
76
|
-
|
|
77
|
-
| 模块 | 参考文件 | 简介 |
|
|
78
|
-
|------|----------|------|
|
|
79
|
-
| 消息 | `references/messaging.md` | 发送文本/富文本消息优先用插件;**卡片消息**用 node-sdk |
|
|
80
|
-
| 日历 | `references/calendar.md` | 日程创建/查询/会议室 |
|
|
81
|
-
| 审批 | `references/approval.md` | 审批流创建/查询/同意/拒绝 |
|
|
82
|
-
| 多维表格 | `references/bitable.md` | 表格/记录/字段 CRUD ⚠️ 优先用插件 |
|
|
83
|
-
| 云文档 | `references/doc.md` | 文档创建/Block 读写 |
|
|
84
|
-
| 云空间 | `references/drive.md` | 文件夹/文件上传下载 |
|
|
85
|
-
| 知识库 | `references/wiki.md` | 知识空间/节点管理 |
|
|
86
|
-
| 权限管理 | `references/perm.md` | 文档协作者权限设置 |
|
|
87
|
-
| 通讯录 | `references/contacts.md` | 用户/部门信息查询 |
|
|
88
|
-
| 飞书妙搭 | `references/id-convert.md` | 妙搭与飞书开放平台用户 ID 互转 |
|
|
89
|
-
| 考勤 | `references/attendance.md` | 打卡记录/考勤规则查询 |
|
|
90
|
-
| 事件订阅 | `references/events.md` | WebSocket 长连接接收事件 |
|
|
91
|
-
| OAuth 授权 | `references/oauth.md` | user_access_token 授权 |
|
|
92
|
-
|
|
93
|
-
> 按需读取 `references/` 下的模块文档获取详细 API 用法和代码示例。
|
|
94
|
-
|
|
95
|
-
## 飞书应用创建指南
|
|
96
|
-
|
|
97
|
-
1. 打开 [飞书开发者后台](https://open.feishu.cn/app) → 创建企业自建应用
|
|
98
|
-
2. 进入「凭证与基础信息」页面,复制 **App ID** 和 **App Secret** 发给开发者
|
|
99
|
-
3. 添加应用能力 → 开启**机器人**(发消息必需)
|
|
100
|
-
4. 权限管理 → 申请对应模块的 API 权限(见下方权限映射表)
|
|
101
|
-
5. 安全设置 → 配置**通讯录权限范围**(使用通讯录/考勤 API 必需,设为「全部成员」或指定部门)
|
|
102
|
-
6. 可用范围 → 添加需要使用该应用的人员或部门(默认仅创建者可用)
|
|
103
|
-
7. 版本管理与发布 → 创建版本 → 提交管理员审核
|
|
104
|
-
8. 对于已有文档/多维表格/知识库:需要将应用机器人添加为文档协作者(否则 API 无权访问)
|
|
105
|
-
|
|
106
|
-
### 常用场景所需权限
|
|
107
|
-
|
|
108
|
-
| 用户需求 | 需要申请的权限 | 额外要求 |
|
|
109
|
-
|----------|---------------|----------|
|
|
110
|
-
| 发送消息 | `im:message:send_as_bot` | 开启机器人能力 |
|
|
111
|
-
| 读写文档 | `docx:document` | 机器人需为文档协作者 |
|
|
112
|
-
| Markdown 转文档 | `docx:document` + `docx:document.block:convert` | — |
|
|
113
|
-
| 读写多维表格 | `bitable:app` | 机器人需为表格协作者 |
|
|
114
|
-
| 管理知识库 | `wiki:wiki` | 机器人需为知识空间成员 |
|
|
115
|
-
| 文件上传下载 | `drive:drive` | — |
|
|
116
|
-
| 文档权限管理 | `drive:permission` | — |
|
|
117
|
-
| 日历日程 | `calendar:calendar` | — |
|
|
118
|
-
| 审批流操作 | `approval:approval` + `approval:task` | — |
|
|
119
|
-
| 查询通讯录 | `contact:contact.base:readonly` | 配置通讯录权限范围 |
|
|
120
|
-
| 妙搭↔飞书用户 ID 转换 | `获取 ID 转换信息`(spark 权限) | — |
|
|
121
|
-
| 查询考勤 | `attendance:task:readonly` | 配置通讯录权限范围 |
|
|
122
|
-
| 事件订阅 | 对应事件的订阅权限 | 需在开发者后台配置「使用长连接接收事件/回调」 |
|
|
123
|
-
| 卡片交互回调 | `card:action.trigger` | 开启长连接订阅方式,订阅 card.action.trigger 回调 |
|
|
124
|
-
|
|
125
|
-
### 飞书应用信息持久化
|
|
126
|
-
|
|
127
|
-
引导用户完成应用配置后,将 App ID 和项目的飞书用途写入 `agent.md`,供后续会话判断是否需要重新引导:
|
|
128
|
-
|
|
129
|
-
```markdown
|
|
130
|
-
## 飞书集成
|
|
131
|
-
|
|
132
|
-
- **App ID**: cli_xxxxxxxxxxxxxxxx
|
|
133
|
-
- **用途**: 通过飞书 API 实现消息通知、文档自动生成(描述项目实际使用飞书做了什么)
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## NestJS 模块设置
|
|
137
|
-
|
|
138
|
-
### 安装 SDK
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
npm install @larksuiteoapi/node-sdk
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### FeishuService 单例
|
|
145
|
-
|
|
146
|
-
> ⚠️ **凭证写入源码**:全栈框架不支持自定义环境变量,因此 `FEISHU_APP_ID` 和 `FEISHU_APP_SECRET` 必须直接写在源码常量中,**不要改为 `process.env`**。
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
import { Injectable } from '@nestjs/common';
|
|
150
|
-
import * as lark from '@larksuiteoapi/node-sdk';
|
|
151
|
-
|
|
152
|
-
const FEISHU_APP_ID = 'cli_xxxx'; // ← 用户提供的真实值
|
|
153
|
-
const FEISHU_APP_SECRET = 'xxxx'; // ← 用户提供的真实值
|
|
154
|
-
|
|
155
|
-
@Injectable()
|
|
156
|
-
export class FeishuService {
|
|
157
|
-
private readonly client: lark.Client;
|
|
158
|
-
|
|
159
|
-
constructor() {
|
|
160
|
-
this.client = new lark.Client({
|
|
161
|
-
appId: FEISHU_APP_ID,
|
|
162
|
-
appSecret: FEISHU_APP_SECRET,
|
|
163
|
-
appType: lark.AppType.SelfBuild,
|
|
164
|
-
domain: lark.Domain.Feishu,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
getClient(): lark.Client {
|
|
169
|
-
return this.client;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
在 Module 中注册为全局 provider:
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
@Module({
|
|
178
|
-
providers: [FeishuService],
|
|
179
|
-
exports: [FeishuService],
|
|
180
|
-
})
|
|
181
|
-
export class FeishuModule {}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## 核心 API 调用模式
|
|
185
|
-
|
|
186
|
-
### 语义化调用
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
// 调用模式: client.<domain>.<resource>.<method>({ params, data, path })
|
|
190
|
-
const res = await client.im.message.create({
|
|
191
|
-
params: { receive_id_type: 'chat_id' },
|
|
192
|
-
data: {
|
|
193
|
-
receive_id: 'oc_xxx',
|
|
194
|
-
content: JSON.stringify({ text: 'hello' }),
|
|
195
|
-
msg_type: 'text',
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// 检查返回值
|
|
200
|
-
if (res.code !== 0) {
|
|
201
|
-
throw new Error(`Feishu API error [${res.code}]: ${res.msg}`);
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### 分页迭代器
|
|
206
|
-
|
|
207
|
-
接口名后缀加 `WithIterator` 自动处理 page_token:
|
|
208
|
-
|
|
209
|
-
```typescript
|
|
210
|
-
for await (const items of await client.contact.user.listWithIterator({
|
|
211
|
-
params: { department_id: '0', page_size: 50 },
|
|
212
|
-
})) {
|
|
213
|
-
console.log(items);
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## 错误处理与权限诊断
|
|
218
|
-
|
|
219
|
-
> **排查优先级**:遇到 API 调用失败或配置问题时,先查阅对应 `references/` 文件顶部的开放平台文档链接,获取最新的参数说明、权限要求和错误码解释,再对照下方速查表定位问题。
|
|
220
|
-
|
|
221
|
-
### 常见错误码
|
|
222
|
-
|
|
223
|
-
| 错误码 | 含义 | 修复方法 |
|
|
224
|
-
|--------|------|----------|
|
|
225
|
-
| 99991672 | 权限不足 | 开发者后台 → 权限管理 → 申请对应权限 |
|
|
226
|
-
| 99991671 | access_token 无效/过期 | 检查 app_id/app_secret 是否正确 |
|
|
227
|
-
| 99991663 | tenant_access_token 过期 | SDK 自动刷新,检查网络连接 |
|
|
228
|
-
| 230001 | 应用不可见/未安装 | 管理员审核发布应用 |
|
|
229
|
-
| 99991400 | 参数错误 | 检查必填字段和参数类型 |
|
|
230
|
-
| 99991668 | 用户不在通讯录权限范围 | 安全设置 → 通讯录权限范围设为「全部成员」 |
|
|
231
|
-
|
|
232
|
-
### 权限速查
|
|
233
|
-
|
|
234
|
-
遇到 `99991672 Permission denied` 时,根据 API 域查找所需权限:
|
|
235
|
-
|
|
236
|
-
| API 域 | 权限标识 | 说明 |
|
|
237
|
-
|--------|----------|------|
|
|
238
|
-
| `im.message.*` | `im:message:send_as_bot` | 发送消息 |
|
|
239
|
-
| `calendar.calendarEvent.*` | `calendar:calendar` | 日历日程读写 |
|
|
240
|
-
| `vc.room.*` | `vc:room:readonly` | 会议室查询 |
|
|
241
|
-
| `approval.approval.*` | `approval:approval` | 审批信息读写 |
|
|
242
|
-
| `approval.instance.query` | `approval:approval.list:readonly` | 审批实例列表 |
|
|
243
|
-
| `approval.task.*` | `approval:task` | 审批操作(同意/拒绝/转交) |
|
|
244
|
-
| `bitable.app*` | `bitable:app` | 多维表格读写 |
|
|
245
|
-
| `docx.document.*` | `docx:document` | 云文档读写 |
|
|
246
|
-
| `docx.document.convert()` | `docx:document.block:convert` | Markdown 转 Block |
|
|
247
|
-
| `drive.*` | `drive:drive` | 云空间文件/文件夹 |
|
|
248
|
-
| `wiki.space.*` / `wiki.spaceNode.*` | `wiki:wiki` | 知识库读写 |
|
|
249
|
-
| `drive.permissionMember.*` | `drive:permission` | 文档权限管理 |
|
|
250
|
-
| `contact.user.*` | `contact:contact.base:readonly` | 通讯录用户信息 |
|
|
251
|
-
| `contact.department.*` | `contact:department.base:readonly` | 部门信息 |
|
|
252
|
-
| `contact.user.*.employee_id` | `contact:user.employee_id:readonly` | 用户 employee_id |
|
|
253
|
-
| `attendance.userTask.*` | `attendance:task:readonly` | 打卡数据 |
|
|
254
|
-
| `attendance.group.*` | `attendance:rule:readonly` | 考勤规则 |
|
|
255
|
-
| `spark.directory.user.id_convert` | `获取 ID 转换信息` | 妙搭 userId ↔ 飞书 open_id/union_id;nestjs-react-fullstack 项目内优先用 `AuthNPaasService`(见 user-identity skill) |
|
|
256
|
-
|
|
257
|
-
## Common Mistakes
|
|
258
|
-
|
|
259
|
-
| 错误 | 正确做法 |
|
|
260
|
-
|------|----------|
|
|
261
|
-
| 将凭证改为 process.env 读取 | 全栈框架不支持自定义环境变量,凭证必须写入源码常量 |
|
|
262
|
-
| `content` 传对象而非 JSON 字符串 | `content: JSON.stringify({ text: 'hello' })` |
|
|
263
|
-
| 每次请求都新建 `lark.Client` | NestJS `@Injectable()` 单例模式复用 |
|
|
264
|
-
| `receive_id_type` 与 ID 前缀不匹配 | `oc_` → `chat_id`, `ou_` → `open_id`, `on_` → `union_id` |
|
|
265
|
-
| 未配置通讯录权限范围 | 安全设置 → 通讯录权限范围 → 全部成员 |
|
|
266
|
-
| 应用未发布直接调 API | 创建版本 → 管理员审核 → 发布后才能使用 |
|
|
267
|
-
| 忘记开启机器人能力 | 应用能力 → 添加机器人(发消息必须) |
|
|
268
|
-
| 日期格式搞混 | 日历用 RFC3339 (`2026-01-01T09:00:00+08:00`),考勤用整数 (`20260101`) |
|
|
269
|
-
| 生成独立可运行的脚本文件 | 所有飞书调用放在 NestJS Service 方法中,由 Controller 调用 |
|
|
270
|
-
|