@lark-apaas/coding-steering 0.1.6-alpha.9 → 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.
- package/package.json +1 -1
- package/steering/nestjs-react-fullstack/skills/authz-guide/SKILL.md +1 -1
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md +6 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/management-page-spec.md +4 -1
- package/steering/nestjs-react-fullstack/skills/client-add-aily-web-chat/SKILL.md +1 -1
- package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +30 -4
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +39 -127
- package/steering/nestjs-react-fullstack/skills/feishu/SKILL.md +0 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/approval.md +1 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/attendance.md +1 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/bitable.md +3 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/calendar.md +1 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/contacts.md +1 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/doc.md +2 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/drive.md +2 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/events.md +2 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/id-convert.md +2 -2
- package/steering/nestjs-react-fullstack/skills/feishu/references/messaging.md +1 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/oauth.md +2 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/perm.md +2 -1
- package/steering/nestjs-react-fullstack/skills/feishu/references/wiki.md +3 -2
- package/steering/nestjs-react-fullstack/skills/openapi-guide/SKILL.md +1 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +36 -17
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +4 -1
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +4 -2
- package/steering/nestjs-react-fullstack/skills/react-hook-best-practices/SKILL.md +4 -4
- package/steering/nestjs-react-fullstack/skills/trigger-guide/SKILL.md +1 -0
- package/steering/nestjs-react-fullstack/skills/user-identity/SKILL.md +1 -0
- package/steering/nestjs-react-fullstack/skills_local/code-fix/SKILL.md +42 -28
- package/steering/nestjs-react-fullstack/skills_local/coding-guide/SKILL.md +37 -149
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: coding-guide
|
|
3
|
-
description: "项目全局编码规范,必须在任何代码编写、阅读、修改、排查前加载。覆盖:项目结构、目录组织、文件命名、NestJS 后端(MVCS/Drizzle ORM/API 规范/异常处理/用户上下文)、React 19 前端(shadcn/ui/Tailwind/路由/样式/组件规范)、前后端联调(shared 类型/axiosForBackend
|
|
4
|
-
steering: true
|
|
5
|
-
steering-inclusion: always
|
|
6
|
-
steering-topic: coding_guide
|
|
7
|
-
match-template-name: nestjs-react-fullstack
|
|
8
|
-
control-by-feature-ab: true
|
|
3
|
+
description: "项目全局编码规范,必须在任何代码编写、阅读、修改、排查前加载。覆盖:项目结构、目录组织、文件命名、NestJS 后端(MVCS/Drizzle ORM/API 规范/异常处理/用户上下文)、React 19 前端(shadcn/ui/Tailwind/路由/样式/组件规范)、前后端联调(shared 类型/axiosForBackend)、数据库操作、质量保障流程、日志规范。Use when: 编写任意前端或后端代码、新建页面或模块、修改接口或数据库操作、排查编译错误或运行时问题、代码审查、理解项目架构。"
|
|
9
4
|
---
|
|
10
5
|
# 项目结构
|
|
11
6
|
|
|
@@ -22,7 +17,7 @@ control-by-feature-ab: true
|
|
|
22
17
|
|
|
23
18
|
## 后端结构 (`server/`)
|
|
24
19
|
|
|
25
|
-
基于 MVCS 架构,**禁止新增一级目录**。
|
|
20
|
+
基于 MVCS 架构,**禁止新增一级目录**。
|
|
26
21
|
|
|
27
22
|
```
|
|
28
23
|
server/ # 符合 NestJS 项目基本规范
|
|
@@ -36,8 +31,8 @@ server/ # 符合 NestJS 项目基本规范
|
|
|
36
31
|
│ ├── hello.controller.ts # controller 示例
|
|
37
32
|
│ ├── hello.module.ts # module 示例
|
|
38
33
|
│ └── hello.service.ts # service 示例(可选)
|
|
39
|
-
├── database/ # Drizzle ORM
|
|
40
|
-
│ ├── schema.ts # Drizzle ORM 数据库 Schema
|
|
34
|
+
├── database/ # Drizzle ORM 数据库相关。表结构变更需走 npm run gen:db-schema 流程,不要手写 SQL DDL。
|
|
35
|
+
│ ├── schema.ts # Drizzle ORM 数据库 Schema 定义。必须从该文件导入数据库类型,禁止自行编写或修改 schema 文件 — 应通过 drizzle migration 生成。
|
|
41
36
|
└── common/ # 共享工具和接口
|
|
42
37
|
│ ├── filters/ # 通用错误处理。
|
|
43
38
|
│ ├── constants/ # 通用常量。
|
|
@@ -68,10 +63,8 @@ client/
|
|
|
68
63
|
│ ├── components/ # 可复用的 UI 组件
|
|
69
64
|
│ │ ├── ui/ # shadcn/ui 组件[Use the components for functionality, but heavily style them.]
|
|
70
65
|
│ │ │ ├── README.md # shadcn/ui 组件库使用说明 [请务必参考该文档进行组件使用]
|
|
71
|
-
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
72
66
|
│ │ ├── business-ui/ # 封装好的用户,部门选择组件以及展示组件
|
|
73
67
|
│ │ │ ├── README.md # 用户展示,用户头像展示,部门选择展示逻辑必须使用 [请务必参考该文档进行组件使用],禁止直接使用avatar组件等方式自行实现
|
|
74
|
-
{% endif %}
|
|
75
68
|
│ │ ├── Layout.tsx # 布局包装器
|
|
76
69
|
│ ├── hooks/ # 自定义 React hooks
|
|
77
70
|
│ └── utils/ # 工具函数
|
|
@@ -128,7 +121,7 @@ shared/ # 前后端共享的目录
|
|
|
128
121
|
- **将风格问题视为编码错误**:项目使用 `@eslint/js` + `typescript-eslint` 推荐配置
|
|
129
122
|
- **ESLint 规范**(`@eslint/js` + `typescript-eslint`):
|
|
130
123
|
- 正则控制字符用 unicode 标志:`/\x1b\[/u`
|
|
131
|
-
- 避免无意义转义:字符串 `"'"` 非 `"\'"`, 正则 `[.]` 非 `[\.]
|
|
124
|
+
- 避免无意义转义:字符串 `"'"` 非 `"\'"`, 正则 `[.]` 非 `[\.]`
|
|
132
125
|
- switch-case 中声明变量用 `{}` 包裹作用域
|
|
133
126
|
|
|
134
127
|
## 依赖使用规范
|
|
@@ -156,14 +149,16 @@ shared/ # 前后端共享的目录
|
|
|
156
149
|
## 质量保障流程
|
|
157
150
|
|
|
158
151
|
修改代码后:
|
|
152
|
+
|
|
159
153
|
1. 运行代码检查工具检查语法错误
|
|
160
154
|
2. 改动服务端代码 → 进行接口测试,确保新增接口测试通过
|
|
161
155
|
3. 提交前 **必须** 读取相关日志确认无错误(服务端: server/server-devserver 日志;客户端: client-devserver 日志)
|
|
162
156
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
157
|
+
如果用户反馈编译失败、服务无法启动:
|
|
158
|
+
|
|
159
|
+
1. 跑 `tsc --noEmit` / `eslint` 等代码检查
|
|
160
|
+
2. 查看 `logs/server.log` / `logs/server.std.log` / `logs/dev.log` 找具体错误
|
|
161
|
+
3. dev 服务无响应:在跑 `npm run dev` 的终端 Ctrl+C 后重新启动即可
|
|
167
162
|
|
|
168
163
|
---
|
|
169
164
|
|
|
@@ -209,9 +204,8 @@ shared/ # 前后端共享的目录
|
|
|
209
204
|
|
|
210
205
|
- **@Injectable() Service 注册**:创建 `@Injectable()` Service → 在对应 Module 的 `providers` 中注册(这条在模块目录内部完成,与聚合文件无关)
|
|
211
206
|
- **三方集成**:调用第三方 API 需在后端实现,使用 @nestjs/axios
|
|
212
|
-
-
|
|
207
|
+
- **文件上传**:前端用 dataloom SDK 上传,服务端仅保存元信息(不要在服务端处理 multipart/form-data)
|
|
213
208
|
- **环境判断**:`process.env.NODE_ENV === "production"` 表示生产环境
|
|
214
|
-
- **文件系统**:**严禁使用 `fs` 写入非 `/tmp` 路径**(无状态 FaaS 容器)
|
|
215
209
|
|
|
216
210
|
## 日志约定
|
|
217
211
|
|
|
@@ -246,6 +240,7 @@ const query = conditions.length > 0
|
|
|
246
240
|
```
|
|
247
241
|
|
|
248
242
|
- Count 查询:
|
|
243
|
+
|
|
249
244
|
```typescript
|
|
250
245
|
// 简单 count
|
|
251
246
|
const result = await this.db.select({ count: count() }).from(users).where(eq(users.status, "active"));
|
|
@@ -286,8 +281,8 @@ await db.select().from(users).where(eq(users.adminUser, userId));
|
|
|
286
281
|
### 数据库使用强约束
|
|
287
282
|
|
|
288
283
|
- 仅通过 schema.ts 暴露的客户端和类型读写,禁止手写 SQL/临时类型
|
|
289
|
-
-
|
|
290
|
-
- 连接失败/SSL
|
|
284
|
+
- **schema变更流程(CRITICAL)**:变更数据表结构后,运行 npm run gen:db-schema 重新生成 schema.ts → **立即重新读取 schema.ts** → 再编写业务代码。跳过重新读取直接编码是 TS2339 批量错误的主要根因
|
|
285
|
+
- 连接失败/SSL 错误:先检查 `.env.local` 里 `SUDA_DATABASE_URL` 是否正确(走过 `lark-cli apps +env-pull`),再排查网络/代理
|
|
291
286
|
|
|
292
287
|
## API 规范
|
|
293
288
|
|
|
@@ -305,12 +300,14 @@ await db.select().from(users).where(eq(users.adminUser, userId));
|
|
|
305
300
|
5. **路由注册验证**:创建新 Controller 后必须用接口测试工具验证路由是否生效。**遇到 404 排查路径**:① 检查 `@Controller(...)` 是否以 `api/` 或 `openapi/` 开头 ② 检查 Module 是否在 `app.module.ts` 注册 ③ 检查静态路由是否在动态路由 `/:id` 之前
|
|
306
301
|
6. **@Query/@Param 类型转换**:默认 string,必须在 controller 层手动转换(如 `parseInt(limit, 10)`)
|
|
307
302
|
7. **写操作加 @NeedLogin**:POST/PUT/PATCH/DELETE 接口显式加 `@NeedLogin()` 装饰器:
|
|
303
|
+
|
|
308
304
|
```typescript
|
|
309
305
|
import { NeedLogin } from "@lark-apaas/fullstack-nestjs-core";
|
|
310
306
|
@NeedLogin()
|
|
311
307
|
@Post()
|
|
312
308
|
async createItem(@Req() req, @Body() dto) { ... }
|
|
313
309
|
```
|
|
310
|
+
|
|
314
311
|
8. **OpenAPI 文档同步**:改动 `*.openapi.controller.ts` 或其引用的 interface / service 返回值 / schema 字段时,加载 `openapi-guide` skill,同步更新 `docs/openapi.json`
|
|
315
312
|
|
|
316
313
|
## 异常处理
|
|
@@ -341,115 +338,12 @@ async createArticle(@Req() req: Request, @Body() dto: CreateArticleDto) {
|
|
|
341
338
|
|
|
342
339
|
## 插件能力(Plugin)
|
|
343
340
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
当用户需求涉及以下关键词时,**必须查询相应 Skill 获取开发指南**:
|
|
347
|
-
|
|
348
|
-
| 关键词 | 对应能力 |
|
|
349
|
-
|-------|---------|
|
|
350
|
-
| 多维表格、飞书表格、Base、bitable | 飞书多维表格增删改查 |
|
|
351
|
-
| 飞书消息、发送通知、消息推送 | 发送飞书消息 |
|
|
352
|
-
| 飞书群、创建群组、群聊 | 创建飞书群组 |
|
|
353
|
-
| AI生文、AI写作、文本生成 | AI智能生文 |
|
|
354
|
-
| AI总结、文本摘要、内容提炼 | AI文本总结 |
|
|
355
|
-
| AI翻译、多语言翻译、语言互译 | AI智能翻译 |
|
|
356
|
-
| AI分类、文本分类、自动归类 | AI智能分类 |
|
|
357
|
-
| 文本转JSON、结构化提取、文本解析 | AI文本转JSON |
|
|
358
|
-
| AI生图、图片生成、智能配图 | AI智能生图 |
|
|
359
|
-
| 图生图、图片编辑、图片修改 | AI图片编辑 |
|
|
360
|
-
| 图片理解、图片识别、图片分析 | AI图片理解 |
|
|
361
|
-
| 图片转JSON、图片提取、图片结构化 | AI图片转JSON |
|
|
362
|
-
| 图片对比、图片比较、图片差异 | AI图片对比 |
|
|
363
|
-
| 抠图、去背景、去水印、去文字 | AI智能抠图 |
|
|
364
|
-
| 背景替换、换背景、背景合成 | AI背景替换 |
|
|
365
|
-
| 文档解析、PDF解析、文件提取 | AI文档解析 |
|
|
366
|
-
| 搜索总结、网页搜索、搜索摘要 | AI搜索总结 |
|
|
367
|
-
| 语音合成、文本转语音、TTS | AI语音合成 |
|
|
368
|
-
| 语音识别、音频转文字、STT | AI语音转文字 |
|
|
369
|
-
| 插件实例、PluginInstance、Capability | 通用插件调用 |
|
|
370
|
-
|
|
371
|
-
### 插件配置完整性(CRITICAL)
|
|
372
|
-
|
|
373
|
-
调用 `capabilityClient.load(pluginInstanceId)` 前**必须确认**:
|
|
374
|
-
1. `server/capabilities/` 下存在对应 `.json` 配置文件
|
|
375
|
-
2. 配置 `id` 与代码中 `pluginInstanceId` 完全匹配
|
|
376
|
-
3. 不存在则**必须先用 `plugin_instance` 工具创建配置**
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
// ❌ 调用不存在的插件(CapabilityNotFoundError — 开发态 Top 错误)
|
|
380
|
-
capabilityClient.load('feishu_monthly_demand'); // server/capabilities/ 下无此配置
|
|
381
|
-
|
|
382
|
-
// ✅ load 失败时停止后续请求
|
|
383
|
-
try {
|
|
384
|
-
const result = await plugin.call('read_records', input);
|
|
385
|
-
} catch (err) {
|
|
386
|
-
if (err.name === 'CapabilityNotFoundError') { setConfigError(true); return; }
|
|
387
|
-
throw err;
|
|
388
|
-
}
|
|
389
|
-
```
|
|
390
|
-
**缓存 load 结果**,避免重复 load 放大错误请求(单应用可达每页 4-6 次失败)。
|
|
391
|
-
|
|
392
|
-
### 关键区分:多维表格 vs 数据库表
|
|
393
|
-
|
|
394
|
-
| 场景 | 判断 | 操作 |
|
|
395
|
-
|-----|------|-----|
|
|
396
|
-
| 明确提到"多维表格/飞书/Base/bitable" | 飞书平台外部服务 | 召回 `plugin_guide` |
|
|
397
|
-
| "建表/DDL/schema变更" | 应用内数据库结构 | 使用 `ddl_sql` 工具 |
|
|
398
|
-
| "往XX表插入数据"(无明确来源) | 检查 `schema.ts` | 有定义→Drizzle ORM;无定义→询问用户确认 |
|
|
399
|
-
|
|
400
|
-
### 多维表格应用的数据架构选择
|
|
401
|
-
|
|
402
|
-
当应用需要读取飞书多维表格数据时,根据**查询模式**选择架构:
|
|
403
|
-
|
|
404
|
-
| 查询模式 | 架构选择 | 实现方式 |
|
|
405
|
-
|---------|---------|---------|
|
|
406
|
-
| 展示/编辑单条记录 | 纯插件 | `getRecord` / `batchUpdateRecords` |
|
|
407
|
-
| 列表分页浏览 | 纯插件 | `searchRecords` + `pageToken` 游标分页 |
|
|
408
|
-
| 统计聚合(计数/求和/平均) | **纯插件 + aggregateQuery** | 禁止 searchRecords 全量拉取后内存计算 |
|
|
409
|
-
| 统计 + 明细下钻 | 纯插件 | 聚合用 `aggregateQuery`,下钻用 `searchRecords` + filter |
|
|
410
|
-
| 排行榜 / TOP N | 纯插件 | `searchRecords` + sort + pageSize=N |
|
|
411
|
-
| 复杂排序/多表关联/全文搜索 | 插件同步 + 本地数据库 | 定时/Webhook 同步到 postgres,复杂查询走数据库 |
|
|
412
|
-
| 高频写入 + 读取 | 本地数据库为主 | 多维表格仅作展示/备份 |
|
|
413
|
-
|
|
414
|
-
**快捷判断:**
|
|
415
|
-
- 用户说"仪表盘/dashboard/驾驶舱/看板/统计" → **必须用 `aggregateQuery`**
|
|
416
|
-
- 用户说"列表/明细/详情" → `searchRecords` 分页
|
|
417
|
-
- 用户说"排行榜/TOP N" → `searchRecords` + sort + pageSize=N
|
|
418
|
-
- 需要跨表关联或复杂计算 → 建本地数据库表 + 同步
|
|
341
|
+
本地开发态**暂不支持** PluginInstance / capability 类插件能力的创建与调用 — 这套链路依赖云端 agent 工具(`plugin_instance` / `get_plugin_ai_json`)与平台下发的 `server/capabilities/` 配置,本地没有等价物。
|
|
419
342
|
|
|
420
|
-
|
|
421
|
-
1. **复用优先**:先查询插件的 Skills,查看已有的候选插件实例 `available_plugin_instances`。
|
|
422
|
-
2. **查 Runtime Spec**:对候选插件实例调用 `get_plugin_ai_json(pluginInstanceId)`,以返回的 `actions[].key / inputSchema / outputSchema / outputMode` 作为**唯一权威**。
|
|
423
|
-
3. **不能复用才创建/更新**:通过 `plugin_instance` 工具 CREATE/UPDATE 生成或调整单文件 PluginInstance 配置。**绝对禁止**使用 `multi_edit` 工具和 `write` 工具来直接修改 `server/capabilities/` 目录下的内容。
|
|
424
|
-
4. **生成调用代码**:必须先调用 `get_plugin_ai_json(pluginInstanceId)` 获取输入输出要求,严格按 schema 与 outputMode 生成调用逻辑。
|
|
425
|
-
> 详细用法、配置格式、代码示例请参阅 `plugin_guide` 文档。
|
|
343
|
+
如果业务用到飞书多维表格、AI 生文/翻译/分类、消息推送等"插件能力"覆盖的场景:
|
|
426
344
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
// Client 侧
|
|
431
|
-
capabilityClient.load(pluginInstanceId).call(actionKey, input) // 非流式
|
|
432
|
-
capabilityClient.load(pluginInstanceId).callStream(actionKey, input) // 流式
|
|
433
|
-
// Server 侧
|
|
434
|
-
capabilityService.load(pluginInstanceId).call(actionKey, input)
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
| 参数 | 说明 |
|
|
438
|
-
|------|------|
|
|
439
|
-
| `pluginInstanceId` | 插件实例 ID,如 `'booking_notification_feishu'` |
|
|
440
|
-
| `actionKey` | 来自 `get_plugin_ai_json` 返回的 `actions[].key` |
|
|
441
|
-
| `input` | 结构必须符合 `actions[].inputSchema` |
|
|
442
|
-
|
|
443
|
-
```typescript
|
|
444
|
-
// ❌ 错误:把参数对象作为 actionKey 传入
|
|
445
|
-
plugin.call(JSON.stringify({ meeting_title: '...' }));
|
|
446
|
-
// ❌ 错误:漏掉 actionKey,直接传参数对象
|
|
447
|
-
plugin.call({ meeting_title: '...' });
|
|
448
|
-
// ✅ 正确:actionKey + input 分开传
|
|
449
|
-
plugin.call('send_feishu_message', { meeting_title: '...' });
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
使用方式:复用优先 → 查 Runtime Spec(`get_plugin_ai_json`)→ 不能复用才创建 → 严格按 schema 生成调用代码。**绝对禁止**用 `multi_edit`/`write` 工具直接修改 `server/capabilities/` 目录下的内容。
|
|
345
|
+
- **数据存储/查询类**(多维表格 CRUD、列表分页、聚合统计):优先用本应用自带的 Postgres + Drizzle,数据可在云端运行时再同步飞书 Base
|
|
346
|
+
- **AI / 飞书消息类**:在云端发布后由平台插件链路承接;本地开发时可先用接口 mock 桩占位
|
|
453
347
|
|
|
454
348
|
## 自动化任务
|
|
455
349
|
|
|
@@ -458,11 +352,13 @@ plugin.call('send_feishu_message', { meeting_title: '...' });
|
|
|
458
352
|
## 分页最佳实践
|
|
459
353
|
|
|
460
354
|
### 传统分页(后台管理表格、跳页场景)
|
|
355
|
+
|
|
461
356
|
- 1-indexed page,`@Max()` 限制 pageSize
|
|
462
357
|
- 响应含 items/total/page/pageSize,page 超出返回空数组
|
|
463
358
|
- 深分页性能差,大数据集考虑游标分页
|
|
464
359
|
|
|
465
360
|
### 游标分页(移动端/无限滚动,优先推荐)
|
|
361
|
+
|
|
466
362
|
- 使用「唯一 + 可排序」字段作为游标排序依据,默认 创建时间+id 降序
|
|
467
363
|
- cursor(首次空) + limit(默认12, @Max(50))
|
|
468
364
|
- 响应仅 items/nextCursor/hasMore,不返回 total
|
|
@@ -471,10 +367,10 @@ plugin.call('send_feishu_message', { meeting_title: '...' });
|
|
|
471
367
|
|
|
472
368
|
## 问题排查指引
|
|
473
369
|
|
|
474
|
-
1.
|
|
475
|
-
2.
|
|
476
|
-
3.
|
|
477
|
-
4. API 测试返回 HTML
|
|
370
|
+
1. 用 `curl` / 浏览器 DevTools / 第三方 HTTP 客户端测试不依赖用户信息的接口
|
|
371
|
+
2. 查看 `logs/server.log` / `logs/server.std.log` 找后端报错;无有效日志时主动补 logger 打印
|
|
372
|
+
3. dev 服务无响应:在跑 `npm run dev` 的终端 Ctrl+C 后重新启动
|
|
373
|
+
4. API 测试返回 HTML 内容时:检查模块注册顺序、路由顺序、请求路径。禁止修改内置 ViewController
|
|
478
374
|
|
|
479
375
|
---
|
|
480
376
|
|
|
@@ -489,13 +385,9 @@ plugin.call('send_feishu_message', { meeting_title: '...' });
|
|
|
489
385
|
- **图表**: ReactECharts,**开发前必须调用 `/charts-skill`**
|
|
490
386
|
- **图标**: Lucide React(唯一图标库,禁止 Emoji 和其他图标库)
|
|
491
387
|
- **表格/表单/图表**: 见下方"组件 Skill 召回规则",开发前必须先调用对应 Skill
|
|
492
|
-
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
493
388
|
- **用户**: 用户信息展示/选择必须用 `business-ui` 组件(阅读 README.md),禁止直接展示 userId
|
|
494
|
-
{% endif %}
|
|
495
|
-
{% if projectMeta['flags']['supportTiptapAndStreamdown'] %}
|
|
496
389
|
- **富文本**: `business-ui/tiptap-editor`(阅读 README.md)
|
|
497
390
|
- **Markdown 渲染**: `components/ui/streamdown`(内置 prose 排版)
|
|
498
|
-
{% endif %}
|
|
499
391
|
|
|
500
392
|
## API 请求
|
|
501
393
|
|
|
@@ -527,13 +419,9 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
527
419
|
| 组件 | 来源 | 用途 |
|
|
528
420
|
|------|------|------|
|
|
529
421
|
| Table | `@lark-apaas/client-toolkit/antd-table` | 数据表格,**先调 `/table-skill`** |
|
|
530
|
-
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
531
422
|
| UserSelect/UserDisplay/UserProfile/DepartmentSelect | `business-ui/*` | 用户/部门选择展示 |
|
|
532
|
-
{% endif %}
|
|
533
|
-
{% if projectMeta['flags']['supportTiptapAndStreamdown'] %}
|
|
534
423
|
| TiptapEditorComplete | `business-ui/tiptap-editor` | 富文本编辑器 |
|
|
535
424
|
| Streamdown | `components/ui/streamdown` | Markdown/流式渲染 |
|
|
536
|
-
{% endif %}
|
|
537
425
|
|
|
538
426
|
### 组件 Skill 召回规则(强制执行)
|
|
539
427
|
|
|
@@ -557,7 +445,7 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
557
445
|
|
|
558
446
|
## @lark-apaas/client-toolkit
|
|
559
447
|
|
|
560
|
-
**零假设原则**:绝不基于假设使用任何子模块。使用任何 `@lark-apaas/client-toolkit` 子模块前**必须**:①
|
|
448
|
+
**零假设原则**:绝不基于假设使用任何子模块。使用任何 `@lark-apaas/client-toolkit` 子模块前**必须**:① 先查询本地已加载的 Skills 或对应包文档 → ② 严格按文档编码。**禁止直接调用 `@lark-apaas/client-toolkit` 任何函数/方法**(不基于查询到的文档)。该库**仅可在前端代码中使用,禁止在后端代码中引用**。
|
|
561
449
|
|
|
562
450
|
| 功能 | 导入路径 |
|
|
563
451
|
|------|----------|
|
|
@@ -571,7 +459,7 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
571
459
|
|
|
572
460
|
**useCurrentUserProfile** 返回 `Partial<IUserProfile>`,初始为空对象:访问字段用 `?.`,判断加载完成用 `if (!userInfo?.user_id)`。
|
|
573
461
|
|
|
574
|
-
**UserService.listUsersByIds**: 根据用户 ID 获取用户详细信息(姓名/头像/邮箱/部门)时**必须使用**,**禁止自行实现用户信息查询逻辑**。非组件上下文需要批量获取用户信息时使用。返回 `result.data.userInfoMap: Record<string, UserInfo>`,UserInfo 含 `userID: string`, `name: I18nText { zh_cn, en_us?, ja_jp? }`, `avatar: string`, `email?: string`, `userType: "_employee"|"_externalUser"|"_anonymousUser"`, `department: { departmentID, name: I18nText }
|
|
462
|
+
**UserService.listUsersByIds**: 根据用户 ID 获取用户详细信息(姓名/头像/邮箱/部门)时**必须使用**,**禁止自行实现用户信息查询逻辑**。非组件上下文需要批量获取用户信息时使用。返回 `result.data.userInfoMap: Record<string, UserInfo>`,UserInfo 含 `userID: string`, `name: I18nText { zh_cn, en_us?, ja_jp? }`, `avatar: string`, `email?: string`, `userType: "_employee"|"_externalUser"|"_anonymousUser"`, `department: { departmentID, name: I18nText }`。展示场景仍优先用 UserDisplay/UserProfile 组件。
|
|
575
463
|
|
|
576
464
|
> ⚠️ `UserInfo` 不含任何飞书 ID 字段(`lark_user_id` / `open_id` / `union_id` 都没有)。需要飞书 ID 一律见 `user-identity` skill(当前用户、任意用户 user_id、open_id、union_id 各种场景都在那里)。
|
|
577
465
|
|
|
@@ -614,6 +502,7 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
614
502
|
- **禁止 React.lazy**:严禁异步加载路由组件
|
|
615
503
|
- **页面跳转**:必须用 `NavLink`/`Link`/`useNavigate`,**严禁 `window.location.href`**
|
|
616
504
|
- **分享链接/二维码**(CRITICAL):应用部署后 URL 与本地路由不同,**必须**用 `resolveAppUrl` 转换:
|
|
505
|
+
|
|
617
506
|
```typescript
|
|
618
507
|
import { resolveAppUrl } from '@lark-apaas/client-toolkit/utils/resolveAppUrl';
|
|
619
508
|
const shareUrl = resolveAppUrl(`/detail/${id}`); // ✅ 路由路径→完整 URL
|
|
@@ -621,6 +510,7 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
621
510
|
resolveAppUrl('https://other-site.com/page'); // ✅ 外部链接原样返回
|
|
622
511
|
// ❌ 严禁自行拼接:`${window.location.origin}/detail/${id}`
|
|
623
512
|
```
|
|
513
|
+
|
|
624
514
|
- **首页路由**:必须含指向 `/` 的 index 路由
|
|
625
515
|
- **路由唯一性**:每页一个唯一路由
|
|
626
516
|
- **导航**:NavLink + active 高亮,路径必须与 `app.tsx` 路由精确对应。全局 Layout 中使用 `useAppInfo`/`useCurrentUserProfile` 获取站点和用户信息
|
|
@@ -661,18 +551,16 @@ return <h1>{data?.title || '未知标题'}</h1>;
|
|
|
661
551
|
## 图片规范
|
|
662
552
|
|
|
663
553
|
- **展示**:必须用 `@client/src/components/ui/image` 组件(非原生 `<img>`),响应式设 `sizes`,固定宽设 `width`
|
|
664
|
-
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
665
554
|
- 用户头像用 business-ui 组件
|
|
666
|
-
{% endif %}
|
|
667
555
|
|
|
668
556
|
### 图片资源优先级
|
|
669
557
|
|
|
670
|
-
1.
|
|
671
|
-
2.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
558
|
+
1. 用户上传图片(最高)
|
|
559
|
+
2. 用户自带的素材库 / 设计资源 — 放到 `client/public/` 或 CDN
|
|
560
|
+
3. 内置 Avatar(仅头像)— `@client/src/utils/img-resources/avatar-placeholders.ts`
|
|
561
|
+
4. Picsum(临时)— `https://picsum.photos/seed/${seed}/${w}/${h}`(必须带 seed)
|
|
562
|
+
|
|
563
|
+
> 本地开发态不接 AI 图片生成,需要 AI 生图请走云端发布后由平台插件链路承接
|
|
676
564
|
|
|
677
565
|
## 核心功能依赖
|
|
678
566
|
|