@lark-apaas/coding-steering 0.1.33 → 0.1.34
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/app-init-feasibility-guide/SKILL.md +1 -0
- package/steering/nestjs-react-fullstack/skills/authn-guide/SKILL.md +6 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/SKILL.md +5 -5
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md +1 -1
- package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +37 -113
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +13 -2
- package/steering/nestjs-react-fullstack/skills/code-fix/SKILL.md +7 -7
- package/steering/nestjs-react-fullstack/skills/coding-guide/SKILL.md +139 -24
- package/steering/nestjs-react-fullstack/skills/connections-sdk/SKILL.md +202 -0
- package/steering/nestjs-react-fullstack/skills/nestjs-cache/SKILL.md +255 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +158 -543
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +15 -1
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +30 -14
- package/steering/nestjs-react-fullstack/skills/raw-sql-boundary-audit/SKILL.md +63 -0
- package/steering/nestjs-react-fullstack/skills/server-builtins-file-storage-service/SKILL.md +1 -1
- package/steering/nestjs-react-fullstack/skills_common/trigger-guide/SKILL.md +284 -12
- package/steering/nestjs-react-fullstack/skills/client-add-aily-web-chat/SKILL.md +0 -139
- package/steering/nestjs-react-fullstack/skills/feishu/SKILL.md +0 -269
- package/steering/nestjs-react-fullstack/skills/feishu/references/approval.md +0 -214
- package/steering/nestjs-react-fullstack/skills/feishu/references/attendance.md +0 -163
- package/steering/nestjs-react-fullstack/skills/feishu/references/bitable.md +0 -311
- package/steering/nestjs-react-fullstack/skills/feishu/references/calendar.md +0 -190
- package/steering/nestjs-react-fullstack/skills/feishu/references/contacts.md +0 -160
- package/steering/nestjs-react-fullstack/skills/feishu/references/doc.md +0 -257
- package/steering/nestjs-react-fullstack/skills/feishu/references/drive.md +0 -104
- package/steering/nestjs-react-fullstack/skills/feishu/references/events.md +0 -199
- package/steering/nestjs-react-fullstack/skills/feishu/references/id-convert.md +0 -128
- package/steering/nestjs-react-fullstack/skills/feishu/references/messaging.md +0 -207
- package/steering/nestjs-react-fullstack/skills/feishu/references/oauth.md +0 -165
- package/steering/nestjs-react-fullstack/skills/feishu/references/perm.md +0 -91
- package/steering/nestjs-react-fullstack/skills/feishu/references/wiki.md +0 -165
- package/steering/nestjs-react-fullstack/skills_common/trigger-guide/references/trigger-lifecycle.md +0 -301
package/package.json
CHANGED
|
@@ -24,6 +24,11 @@ match-template-name: nestjs-react-fullstack
|
|
|
24
24
|
│ └─ 不需要任何装饰器 ──→ AuthNPaasGuard 默认放行未标 @NeedLogin() 的接口
|
|
25
25
|
│ (`@Public()` 是历史遗留 no-op,不要使用,详见第二节)
|
|
26
26
|
│
|
|
27
|
+
├─ 公开接口会写入应用数据库吗?
|
|
28
|
+
│ ├─ 是,且允许未登录提交 ──→ 接口保持公开;目标 RLS 表补 `FOR INSERT TO anon WITH CHECK (true)`,未登录 POST 验证
|
|
29
|
+
│ │ (anon SELECT / authenticated INSERT 都不能替代 anon INSERT WITH CHECK)
|
|
30
|
+
│ └─ 否,需要当前用户或管理员身份 ──→ 使用 @NeedLogin(),不要开放匿名插入
|
|
31
|
+
│
|
|
27
32
|
├─ 接口返回 401 / 登录跳转不对?
|
|
28
33
|
│ └─ 是 ──→ 第四节 · 401 未授权
|
|
29
34
|
│
|
|
@@ -97,6 +102,7 @@ export class ApiController {
|
|
|
97
102
|
| 手动实例化 `AuthNPaasGuard` | 模块自动注册为全局守卫,无需手动 `@UseGuards()` |
|
|
98
103
|
| 用 `@Public()` 标记公开接口 | `@Public()` 是 no-op;公开接口不需要任何装饰器(opt-in 模式默认放行未标 `@NeedLogin()` 的接口) |
|
|
99
104
|
| 在未标 `@NeedLogin()` 的接口里依赖 `req.userContext.userId` 做业务判断 | 该接口默认放行未登录请求,`userId` 可能为 `undefined`,必须先判空 |
|
|
105
|
+
| 公开接口写 RLS 表但只给 anon SELECT / authenticated INSERT | 允许未登录提交时补 `FOR INSERT TO anon WITH CHECK (true)` 或等价 Drizzle policy,并用未登录 POST 验证;否则加 `@NeedLogin()` |
|
|
100
106
|
| 业务层自行返回 401 实现"未登录跳转" | 用 `@NeedLogin()` 让守卫统一处理,确保 `x-login-url` 头被正确写入 |
|
|
101
107
|
| 自建认证传输:自签 JWT 走自定义 `Authorization` / `X-Auth-Token` 头、自定义 cookie、query token | 这些头在妙搭网关转发时**不透传**(白名单只放 `x-suda-csrf-token` / `x-larkgw-suda-webuser` + 标准头),后端必拿不到 → 401,且易被误诊为网关故障。任何自有账号体系/自分配账号需求,身份一律取 `req.userContext`(网关注入,解析自 `x-larkgw-suda-webuser`)、用 `@NeedLogin()` 认证 + 业务角色表授权,不要自签 JWT 走自定义传输 |
|
|
102
108
|
|
|
@@ -30,7 +30,7 @@ match-template-name: nestjs-react-fullstack
|
|
|
30
30
|
|
|
31
31
|
"开启权限服务"/"设计权限体系"/"规划角色"/"升级鉴权模式"/"开启角色服务" → **必须先调用 `rbac_role_manager` tool 的 DESIGN action**,用户确认后再实施。日常"给某功能加权限"不触发 DESIGN。
|
|
32
32
|
|
|
33
|
-
>
|
|
33
|
+
> **相关技能**:角色的查询、创建、更新等 CLI 操作通过当前沙箱的权限管理 skill 执行。
|
|
34
34
|
|
|
35
35
|
### 403 统一处理(所有模式通用)
|
|
36
36
|
|
|
@@ -60,7 +60,7 @@ if (response.status === 403) throw new Error('无操作权限,请联系管理
|
|
|
60
60
|
2. 统一使用 `useAuth()` 获取 `{ ability, isLoading }`,**禁止** `useAuthAbility` / `useCanRole`(已移除)
|
|
61
61
|
3. 必须处理 `isLoading`——加载期间 `ability.can()` 返回 false,不检查会误判无权限
|
|
62
62
|
4. 创建角色后必须编写鉴权代码,切忌只创建角色不编写代码
|
|
63
|
-
5. **`@CanRole([X])`/`<CanRole roles={[X]}>` 里的 X 必须等于平台实际角色标识(bizID
|
|
63
|
+
5. **`@CanRole([X])`/`<CanRole roles={[X]}>` 里的 X 必须等于平台实际角色标识(bizID),不能凭语义臆造**——创建角色时用当前沙箱权限管理 skill 的建角色命令把标识固定成代码要用的值(该 skill 支持显式指定角色标识),或先用它列出角色取实际标识再写代码。若代码写 `'admin'`/`'reviewer'` 但平台分配给用户的角色标识是 `role_xxx`,两者对不上 → 线上恒 403(日志可见 `用户角色 [role_xxx], 需要 [admin, reviewer]`)
|
|
64
64
|
|
|
65
65
|
### 前端
|
|
66
66
|
|
|
@@ -161,9 +161,9 @@ import { CanRole } from '@lark-apaas/fullstack-nestjs-core';
|
|
|
161
161
|
|
|
162
162
|
| 问题 | 处理方式 |
|
|
163
163
|
|------|---------|
|
|
164
|
-
| 403 错误 |
|
|
165
|
-
| 线上 403 但 dev 正常 / 用户"已授权"仍 403 | 先核对两件事:(1) 角色面板授给用户的角色**标识(bizID)** 是否等于代码 `@CanRole`
|
|
166
|
-
| 开发环境授权不生效 |
|
|
164
|
+
| 403 错误 | 明确告知是无权限报错,确认是否符合预期;用当前沙箱权限管理 skill 核验该用户实际命中的角色(该通道提供角色模拟能力时可用其调试) |
|
|
165
|
+
| 线上 403 但 dev 正常 / 用户"已授权"仍 403 | 先核对两件事:(1) 角色面板授给用户的角色**标识(bizID)** 是否等于代码 `@CanRole` 里的字符串(用当前沙箱权限管理 skill 列角色,取实际标识比对);(2) 是否另搞了一套平台不认的权限(自建角色表 / 引用平台没 create 过的标识 / user_id 名单)绕开平台。两者任一不符即解释 403 |
|
|
166
|
+
| 开发环境授权不生效 | 先用当前沙箱权限管理 skill 核对用户命中的角色与角色成员配置;该通道提供角色模拟能力时引导用户使用 |
|
|
167
167
|
| 用户要求管理角色 | 开发态:`[角色面板](BaseURL?openPanel=auth)`;运行态:按第二节实施 |
|
|
168
168
|
|
|
169
169
|
---
|
package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md
CHANGED
|
@@ -95,7 +95,7 @@ Step 9: 验证(禁止跳过)
|
|
|
95
95
|
|
|
96
96
|
## Step 1: 数据库表 + 数据初始化
|
|
97
97
|
|
|
98
|
-
>
|
|
98
|
+
> 对应权限管理 skill 的 Action:Step 1.1 = `PERM_CREATE_TABLE`,Step 1.2 + 1.3 = `PERM_SEED`。
|
|
99
99
|
|
|
100
100
|
**⛔ 必须严格按以下三步顺序执行,禁止合并步骤或跳步。`authz_role_permissions` 有外键依赖 `authz_permissions`,合并执行会导致映射表静默为空。**
|
|
101
101
|
|
package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: client-builtins-file-storage-service
|
|
3
|
-
description: 前端文件存储服务指南,基于 dataloom.storage 实现文件上传、删除、列表查询、使用filePath换取图片链接,包含 uploadFile、remove、list、getDefaultBucketId、generateDownloadUrlFromFilePath 等 API 用法。Use when 需要:(1) 上传文件/图片/附件到云存储,(2) 删除存储桶中的文件,(3) 获取文件列表或浏览目录,(4)
|
|
3
|
+
description: 前端文件存储服务指南,基于 dataloom.storage 实现文件上传、删除、列表查询、使用filePath换取图片链接,包含 uploadFile、remove、list、getDefaultBucketId、generateDownloadUrlFromFilePath 等 API 用法。Use when 需要:(1) 上传文件/图片/附件到云存储,(2) 删除存储桶中的文件,(3) 获取文件列表或浏览目录,(4) 前端存储文件信息到数据库,比如:上传后优先将 download_url 存至 text 类型字段,仅在使用 file_attachment 类型字段时才将 file_path 存至该字段,(5) 在前端通过 file_path 获取文件 URL 的场景,比如:图片渲染、通过 url 下载文件,或其他前端文件存储相关开发。
|
|
4
4
|
steering: true
|
|
5
5
|
steering-topic: client_builtins_file_storage_service
|
|
6
6
|
match-template-name: nestjs-react-fullstack
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# 前端文件开发规范
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
> **术语**:下文的 `file_attachment`、`text` 指的是数据库字段的**类型**,不是字段名。
|
|
12
|
+
|
|
13
|
+
- **优先用 `download_url` 形式(默认按这个来)**:上传后取 `data.download_url`,存到 **`text` 类型字段**;渲染 / 下载直接用这个 URL,最简单,绝大多数场景都用它。
|
|
14
|
+
- 仅当业务明确使用 **`file_attachment` 类型字段**时才走 file_path:该字段存 `bucket_id` + `file_path`(`file_path` 只能是文件路径,**不可以存 `download_url`**);要拿 URL 用 dataloom SDK 的 `generateDownloadUrlFromFilePath(file_path)`,且 `file_path` 必须从该字段读出,禁止自己拼接。
|
|
11
15
|
|
|
12
16
|
# dataloom SDK 文件服务
|
|
13
17
|
|
|
@@ -15,12 +19,12 @@ match-template-name: nestjs-react-fullstack
|
|
|
15
19
|
项目提供了dataloom SDK的文件服务,用于文件的上传,下载,删除,列出bucket中文件,创建临时签名url等能力。
|
|
16
20
|
|
|
17
21
|
## 使用注意
|
|
18
|
-
- `
|
|
22
|
+
- **入口边界**:本 SDK 是**应用前端代码**读写应用存储的唯一入口。Agent 自己在对话 / 开发中上传、灌数据或调试文件请用 `miaoda file` CLI(见 `miaoda-file` skill)——二者操作同一个应用存储桶,但 **`miaoda file` CLI 禁止写进页面代码**,页面里一律用本 SDK。
|
|
19
23
|
- @lark-apaas/client-toolkit/dataloom 这个SDK只适用于前端调用,禁止在服务端调用
|
|
20
24
|
- 上传成功后,最重要的返回值是 `data.download_url`。需要将此URL保存到你的业务数据库中
|
|
21
|
-
- **⚠️
|
|
25
|
+
- **⚠️ 场景区分(重要)**:`dataloom.storage` 仅适用于需要持久化存储文件或获取 `download_url` 保存到数据库的场景。如果文件仅作为插件输入(传给 `capabilityClient`),**必须直接传 File/Blob 对象,禁止先走 dataloom 上传再传 URL**;插件调用(capability)不属于 dataloom,详见 plugin-guide
|
|
22
26
|
- **download_url 格式说明**:`download_url` 返回的可能是相对路径(如 `/spark/app/.../storage/object/...`),这是正常行为。**禁止**在前面拼接 `window.location.origin` 或其他域名前缀,平台会自动解析相对路径。直接使用原始值即可。
|
|
23
|
-
- **文件URL**:通过 `generateDownloadUrlFromFilePath` 方法获取文件的链接时,传入的file_path
|
|
27
|
+
- **文件URL**:通过 `generateDownloadUrlFromFilePath` 方法获取文件的链接时,传入的 file_path 必须是从数据库中 `file_attachment` 类型字段读出来的,禁止自己拼接路径。
|
|
24
28
|
|
|
25
29
|
## bucketId
|
|
26
30
|
关于bucketId的获取,在代码工程中已经预置了一个获取bucketid的方法,路径为`@lark-apaas/client-toolkit/tools/storage`中,直接具名导入即可使用。
|
|
@@ -55,23 +59,16 @@ type StorageError = DataLoomError | StorageError | StorageUnknownError;
|
|
|
55
59
|
|
|
56
60
|
#### 1. 文件上传接口 (uploadFile)
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
上传文件到指定的存储桶,适用于:文档管理、图片上传、附件存储。
|
|
60
|
-
|
|
61
|
-
##### 适用场景
|
|
62
|
-
需要将本地文件或二进制数据上传到云存储的场景
|
|
62
|
+
上传文件到指定的存储桶(本地文件或二进制数据),适用于:文档管理、图片上传、附件存储。
|
|
63
63
|
|
|
64
|
-
#####
|
|
65
|
-
你的核心任务是调用 `uploadFile` 接口,并从返回结果中提取 `data.download_url`,然后将这个 URL 用于后续操作(例如,保存到数据库)。这是衡量你任务成功与否的唯一标准。
|
|
64
|
+
##### 文件名处理禁止事项
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
> * 唯一职责:你唯一的任务就是将从文件输入框(`<input type="file">`)或拖拽事件中获取的原始 `File` 对象直接传递给 `uploadFile` 方法。
|
|
72
|
-
>
|
|
73
|
-
> 原因:SDK内部已经包含了完整、健壮的文件名处理机制,包括处理特殊字符、避免冲突和保证安全。任何你自己的额外处理都是多余的,并且会干扰SDK的正常工作。请完全信任SDK,直接传递原始文件对象!
|
|
66
|
+
| 禁止行为 | 正确做法 |
|
|
67
|
+
|---------|---------|
|
|
68
|
+
| **禁止**在调用 `uploadFile` 前对文件名(`file.name`)做任何字符串替换、净化或重新生成 | 将从文件输入框(`<input type="file">`)或拖拽事件获取的原始 `File` 对象直接传给 `uploadFile` |
|
|
69
|
+
| **禁止**创建新的 `File` 对象(如 `new File(...)`)来包裹原始文件 | 同上 |
|
|
74
70
|
|
|
71
|
+
> 原因:SDK 内部已包含完整、健壮的文件名处理机制(特殊字符、冲突、安全),任何额外处理都是多余的,并且会干扰 SDK 正常工作。
|
|
75
72
|
|
|
76
73
|
##### 入参说明
|
|
77
74
|
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|
|
@@ -83,18 +80,18 @@ type StorageError = DataLoomError | StorageError | StorageUnknownError;
|
|
|
83
80
|
| 字段名 | 类型 | 说明 |
|
|
84
81
|
|--------|------|---------|
|
|
85
82
|
| `data.bucket_id` | `string` | 所属bucket ID |
|
|
86
|
-
| `data.file_path` | `string` | 文件的路径,不是文件的url
|
|
87
|
-
| `data.download_url` | `string` | 文件url
|
|
83
|
+
| `data.file_path` | `string` | 文件的路径,不是文件的url,可存到 `file_attachment` 类型字段 |
|
|
84
|
+
| `data.download_url` | `string` | 文件url(可能是相对路径),可直接用于下载文件或渲染图片;**禁止**拼接域名前缀,见「使用注意」 |
|
|
88
85
|
| `error` | `StorageError \| null` | 错误信息,成功时为null |
|
|
89
86
|
|
|
90
|
-
#####
|
|
91
|
-
关键点:上传成功后,最重要的返回值是 `data.download_url`。通常需要将此URL保存到你的业务数据库中。
|
|
87
|
+
##### 使用示例
|
|
92
88
|
```typescript
|
|
93
89
|
import { getDataloom } from "@lark-apaas/client-toolkit/dataloom";
|
|
94
90
|
import { getDefaultBucketId } from "@lark-apaas/client-toolkit/tools/storage";
|
|
95
91
|
import { logger } from "@lark-apaas/client-toolkit/logger";
|
|
96
|
-
// 1.
|
|
97
|
-
const file = fileInput.files[0];
|
|
92
|
+
// 1. 获取文件与 dataloom 实例
|
|
93
|
+
const file = fileInput.files[0];
|
|
94
|
+
const dataloom = await getDataloom();
|
|
98
95
|
// 2. 调用上传接口
|
|
99
96
|
const { data, error } = await dataloom
|
|
100
97
|
.storage
|
|
@@ -116,11 +113,7 @@ if (!httpResponse.data?.success) {
|
|
|
116
113
|
|
|
117
114
|
#### 2. 文件删除接口 (remove)
|
|
118
115
|
|
|
119
|
-
|
|
120
|
-
删除存储桶中的一个或多个文件,适用于:清理过期文件、删除用户数据、批量清理
|
|
121
|
-
|
|
122
|
-
##### 适用场景
|
|
123
|
-
需要从云存储中永久删除文件的场景
|
|
116
|
+
从云存储中永久删除一个或多个文件,适用于:清理过期文件、删除用户数据、批量清理。
|
|
124
117
|
|
|
125
118
|
##### 入参说明
|
|
126
119
|
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|
|
@@ -129,55 +122,26 @@ if (!httpResponse.data?.success) {
|
|
|
129
122
|
| `filePaths` | `string[]` | ✅ | - | 要删除的文件路径数组,可以是file_path也可以是download_url |
|
|
130
123
|
|
|
131
124
|
##### 出参说明
|
|
132
|
-
|
|
133
|
-
|--------|------|---------|
|
|
134
|
-
| `data` | `FileObject[]` | 被删除的文件对象数组 |
|
|
135
|
-
| `data[].name` | `string` | 文件名称 |
|
|
136
|
-
| `data[].bucket_id` | `string` | 所属bucket ID |
|
|
137
|
-
| `data[].id` | `string` | 文件唯一标识符 |
|
|
138
|
-
| `data[].created_at` | `string` | 文件创建时间 |
|
|
139
|
-
| `data[].updated_at` | `string` | 文件更新时间 |
|
|
140
|
-
| `data[].metadata` | `TosMetaData` | 文件元数据信息 |
|
|
141
|
-
| `error` | `StorageError \| null` | 错误信息,成功时为null |
|
|
125
|
+
返回 `{ data: FileObject[] | null, error: StorageError | null }`:`data` 为被删除的文件对象数组(字段同 list 出参说明),删除不存在的 filePaths 会返回空数组;`error` 成功时为 null。
|
|
142
126
|
|
|
143
127
|
##### 使用示例
|
|
144
128
|
```typescript
|
|
145
129
|
import { getDataloom } from "@lark-apaas/client-toolkit/dataloom";
|
|
146
130
|
import { getDefaultBucketId } from "@lark-apaas/client-toolkit/tools/storage";
|
|
147
|
-
import { logger } from "@lark-apaas/client-toolkit/logger";
|
|
148
131
|
|
|
149
132
|
// 异步获取dataloom实例
|
|
150
133
|
const dataloom = await getDataloom();
|
|
151
134
|
|
|
152
|
-
//
|
|
153
|
-
const { data, error } = await dataloom
|
|
154
|
-
.storage
|
|
155
|
-
.from(getDefaultBucketId())
|
|
156
|
-
.remove(['/documents/old-report.pdf']);
|
|
157
|
-
|
|
158
|
-
// 批量删除文件
|
|
135
|
+
// 批量删除文件(删除单个文件传长度为 1 的数组即可)
|
|
159
136
|
const { data, error } = await dataloom
|
|
160
137
|
.storage
|
|
161
138
|
.from(getDefaultBucketId())
|
|
162
|
-
.remove([
|
|
163
|
-
'/documents/file1.pdf',
|
|
164
|
-
'/images/image1.jpg',
|
|
165
|
-
'/temp/cache.txt'
|
|
166
|
-
]);
|
|
167
|
-
|
|
168
|
-
// 注意:删除不存在的filePaths会返回空数组
|
|
169
|
-
if (data) {
|
|
170
|
-
logger.info('删除的文件:', data); // 返回所有被删除的文件对象
|
|
171
|
-
}
|
|
139
|
+
.remove(['/documents/file1.pdf', '/images/image1.jpg']);
|
|
172
140
|
```
|
|
173
141
|
|
|
174
142
|
#### 3. 文件列表接口 (list)
|
|
175
143
|
|
|
176
|
-
|
|
177
|
-
获取存储桶中的文件列表,适用于:文件管理、目录浏览、文件检索
|
|
178
|
-
|
|
179
|
-
##### 适用场景
|
|
180
|
-
需要查看和管理存储桶中文件的场景
|
|
144
|
+
获取存储桶中的文件列表,适用于:文件管理、目录浏览、文件检索。
|
|
181
145
|
|
|
182
146
|
##### 入参说明
|
|
183
147
|
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|
|
@@ -212,39 +176,26 @@ import { getDefaultBucketId } from "@lark-apaas/client-toolkit/tools/storage";
|
|
|
212
176
|
// 异步获取dataloom实例
|
|
213
177
|
const dataloom = await getDataloom();
|
|
214
178
|
|
|
215
|
-
//
|
|
216
|
-
const { data, error } = await dataloom
|
|
217
|
-
.storage
|
|
218
|
-
.from(getDefaultBucketId())
|
|
219
|
-
.list('/documents');
|
|
220
|
-
|
|
221
|
-
// 带分页和排序的查询
|
|
179
|
+
// 带分页和排序的查询(path 和 options 均可省略)
|
|
222
180
|
const { data, error } = await dataloom
|
|
223
181
|
.storage
|
|
224
182
|
.from(getDefaultBucketId())
|
|
225
183
|
.list('/images', {
|
|
226
184
|
limit: 20,
|
|
227
185
|
offset: 0,
|
|
228
|
-
sortBy: {
|
|
229
|
-
column: 'created_at',
|
|
230
|
-
order: 'desc'
|
|
231
|
-
}
|
|
186
|
+
sortBy: { column: 'created_at', order: 'desc' }
|
|
232
187
|
});
|
|
233
188
|
```
|
|
234
189
|
|
|
235
190
|
#### 4. 根据filePath生成downloadUrl接口 (generateDownloadUrlFromFilePath)
|
|
236
191
|
|
|
237
|
-
|
|
238
|
-
通过 file_path 生成文件的url,适用于:通过url渲染图片、使用url下载文件。如果已有download_url,直接使用,严禁使用该接口再次生成。
|
|
239
|
-
|
|
240
|
-
##### 适用场景
|
|
241
|
-
在前端通过file_path展示图片或下载文件的场景。
|
|
192
|
+
**仅用于 `file_attachment` 类型字段场景**:当手头只有从该字段读出的 `file_path` 时,用它换取可渲染 / 下载的 URL。**优先直接用 `download_url`**——已有 download_url 就直接用,严禁再调本接口生成。
|
|
242
193
|
|
|
243
194
|
##### 入参说明
|
|
244
195
|
|
|
245
196
|
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|
|
246
197
|
|--------|--------|---------|--------|---------|
|
|
247
|
-
| `filePath` | `string` | ✅ | - | 文件的filePath
|
|
198
|
+
| `filePath` | `string` | ✅ | - | 文件的 filePath。**只可以从数据库中 `file_attachment` 类型字段读取,不允许自己拼接路径** |
|
|
248
199
|
|
|
249
200
|
##### 出参说明
|
|
250
201
|
| 类型 | 说明 |
|
|
@@ -254,29 +205,12 @@ const { data, error } = await dataloom
|
|
|
254
205
|
##### 使用示例
|
|
255
206
|
|
|
256
207
|
```typescript
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const [imageUrl, setImageUrl] = useState('');
|
|
264
|
-
|
|
265
|
-
useEffect(() => {
|
|
266
|
-
const getImageUrl = async () => {
|
|
267
|
-
const dataloom = await getDataloom();
|
|
268
|
-
const imageUrl = dataloom
|
|
269
|
-
.storage
|
|
270
|
-
.from(getDefaultBucketId())
|
|
271
|
-
.generateDownloadUrlFromFilePath(file.file_path);
|
|
272
|
-
setImageUrl(imageUrl);
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
getImageUrl();
|
|
276
|
-
}, [file.file_path]);
|
|
277
|
-
|
|
278
|
-
return <img src={imageUrl} alt="图片" />;
|
|
279
|
-
}
|
|
208
|
+
const dataloom = await getDataloom();
|
|
209
|
+
const imageUrl = dataloom
|
|
210
|
+
.storage
|
|
211
|
+
.from(getDefaultBucketId())
|
|
212
|
+
.generateDownloadUrlFromFilePath(file.file_path); // file_path 取自数据库 file_attachment 类型字段
|
|
213
|
+
// 同步返回 string,直接用于 <img src={imageUrl} /> 渲染或作为下载链接
|
|
280
214
|
```
|
|
281
215
|
|
|
282
216
|
#### 5. 最小化文件上传与展示示例
|
|
@@ -290,16 +224,6 @@ export interface FileRecord { id: string; fileName: string; downloadUrl: string;
|
|
|
290
224
|
export interface FileApiResp<T = FileRecord> { success: boolean; data: T; message: string; }
|
|
291
225
|
```
|
|
292
226
|
|
|
293
|
-
##### 各 API 预估返回格式
|
|
294
|
-
|
|
295
|
-
| API | 成功返回 | 失败返回 |
|
|
296
|
-
|-----|---------|---------|
|
|
297
|
-
| `uploadFile(file)` | `{ data: { bucket_id: "xxx", file_path: "123456.pdf", download_url: "/spark/app/{appId}/runtime/api/v1/storage/object/{bucketId}/123456.pdf" }, error: null }` | `{ data: null, error: { name: "StorageError", message: "..." } }` |
|
|
298
|
-
| `list('/path')` | `{ data: [{ name, bucket_id, id, created_at, metadata: { size, mimetype } }], error: null }` | `{ data: null, error: { error_msg: "...", status_code: "404" } }` |
|
|
299
|
-
| `remove([url])` | `{ data: [{ name, bucket_id, id, ... }], error: null }`;不存在则 `data: []` | 同上 |
|
|
300
|
-
| `POST /api/files` | `{ success: true, data: { id, fileName, downloadUrl, createdAt }, message: "ok" }` | `{ success: false, data: null, message: "错误描述" }` |
|
|
301
|
-
| `GET /api/files` | `{ success: true, data: [FileRecord, ...], message: "ok" }` | 同上 |
|
|
302
|
-
|
|
303
227
|
##### 示例代码
|
|
304
228
|
|
|
305
229
|
```tsx
|
|
@@ -225,13 +225,23 @@ const MyComponent = () => {
|
|
|
225
225
|
## 用户展示与选择方案
|
|
226
226
|
|
|
227
227
|
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
228
|
-
这些组件在使用之前必须读取 client/src/components/business-ui/README.md
|
|
228
|
+
这些组件在使用之前必须读取 client/src/components/business-ui/README.md 文件来理解用法,并以该 README 与本地源码为准。
|
|
229
229
|
|
|
230
230
|
目前可用的组件有:
|
|
231
231
|
|
|
232
232
|
- UserSelect 用户选择组件(@/components/business-ui/user-select)
|
|
233
233
|
- DepartmentSelect 部门选择组件(@/components/business-ui/department-select)
|
|
234
|
-
- UserDisplay - 用户展示组件(@/components/business-ui/user-display
|
|
234
|
+
- UserDisplay - 用户展示组件(@/components/business-ui/user-display):展示任意用户时使用 `value`(用户 ID 字符串或字符串数组,也可使用 README/源码声明的用户对象形式)或 `userId` / `user_id` 快捷字段;**不要**给这个 business-ui 组件传 `users`。
|
|
235
|
+
|
|
236
|
+
列表、详情、卡片等任意位置展示一个或多个用户 ID,都继续使用同一个 business-ui 契约:
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import { UserDisplay } from "@/components/business-ui/user-display";
|
|
240
|
+
|
|
241
|
+
<UserDisplay value={memberIds.map(String)} size="small" showLabel />
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
如果页面需要展示用户字段,不要退回到直接渲染、拼接或 `join` 用户 ID 文本。
|
|
235
245
|
|
|
236
246
|
{% else %}
|
|
237
247
|
|
|
@@ -306,6 +316,7 @@ const form = useForm<z.infer<typeof formSchema>>({
|
|
|
306
316
|
- **组件路径**:`@lark-apaas/client-toolkit/components/User`
|
|
307
317
|
- **功能**:用于所有用户信息的展示场景
|
|
308
318
|
- **特性**:显示用户**头像**和**姓名**,支持多用户展示
|
|
319
|
+
- **边界**:本节仅适用于 `@lark-apaas/client-toolkit/components/User`。如果当前项目启用了本地 `@/components/business-ui/user-display`,必须按上方 business-ui 分支读取 README 并使用 `value` / `userId` / `user_id`,不要把这里的 `users` 参数迁移过去。
|
|
309
320
|
|
|
310
321
|
### 属性定义
|
|
311
322
|
|
|
@@ -1,6 +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
|
|
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 the sandbox's app observability skill, route 404 errors, **lucide-react icon not found / duplicate identifier / barrel-export naming conflicts**, duplicate route components, npm script not defined errors, or **database connection/auth errors**. 触发词:导入错误, 模块解析失败, 类型错误, Dto不匹配, JSX语法, API异常, traceid, 线上日志, 线上日志查询, 查询线上日志, 路由404, code fix, debugging, lucide-react import error, icon not found, 图标不存在, Cannot find name, 标识符重复, no-redeclare, export 冲突, 桶导出冲突, dual export, 路由组件复用, npm报错, package.json, 脚本未定义, "请修复错误" 通用排错, 数据库连接失败, get db connection failed, ECONNREFUSED
|
|
4
4
|
steering: true
|
|
5
5
|
steering-topic: code_fix
|
|
6
6
|
match-template-name: nestjs-react-fullstack
|
|
@@ -156,21 +156,21 @@ Module '"@client/src/api/gen"' has no exported member named 'DiscrepancyResponse
|
|
|
156
156
|
|
|
157
157
|
## 调用生成的 API 异常
|
|
158
158
|
|
|
159
|
-
###
|
|
159
|
+
### 需要查询线上日志时
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
**适用场景**:用户提供 traceid、logid、线上报错、发布错误日志、线上运行日志、线上链路追踪,或排查必须依赖线上前端/后端日志;**以及插件相关的线上 / 客户反馈问题(飞书消息发不出 / 插件不生效 / CapabilityService·capabilityClient 异常),即使用户没给 traceid/logid,也应主动查线上 runtime log 取证**(拿到 CapabilityService 真实错误、plugin_key、action、必填参数校验错误再定位代码;沙箱日志不能作为线上无错误的证据)。
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
**处理要求**:引导并加载当前沙箱的应用可观测性 skill 查询线上日志——优先查线上运行日志与链路追踪;排查发布失败时查该次发布的错误日志。具体命令一律以该 skill 的手册为准,不要凭记忆拼命令。本地开发日志、构建日志、测试日志、浏览器控制台日志不在此范围内,按当前技能或对应工具排查。拿到线上日志后再回到本技能继续定位与修复代码问题。
|
|
164
164
|
|
|
165
165
|
### 用户提供了 traceid,排查错误
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
**解决方案**:用当前沙箱的应用可观测性 skill 读取线上前端与后端日志,获取详细错误
|
|
168
168
|
|
|
169
169
|
**排查示例**:
|
|
170
170
|
|
|
171
171
|
用户输入:8ae6724e-277e-4d51-afbf-b524b654f27f 看看这个 trace 为什么报错了
|
|
172
172
|
排查路径:
|
|
173
|
-
1.
|
|
173
|
+
1. 用当前沙箱的应用可观测性 skill 查询线上服务端与 trace 日志
|
|
174
174
|
2. 根据服务端日志中对应的日志内容修复对应代码逻辑
|
|
175
175
|
|
|
176
176
|
### 调用 API 客户端时后端返回异常
|
|
@@ -296,7 +296,7 @@ useEffect 无限循环、依赖数组管理、useMemo/useCallback 记忆化等
|
|
|
296
296
|
|
|
297
297
|
先按日志轻量分流,再决定是否改代码:
|
|
298
298
|
|
|
299
|
-
1. **有明确代码信号**(SQL 语法错误、表/列不存在、堆栈指向具体 query/schema 文件)→ 只读确认 `server/database/schema.ts`、近期 DB 改动或相关 ORM 查询;DB schema / SQL
|
|
299
|
+
1. **有明确代码信号**(SQL 语法错误、表/列不存在、堆栈指向具体 query/schema 文件)→ 只读确认 `server/database/schema.ts`、近期 DB 改动或相关 ORM 查询;DB schema / SQL 细节按数据库操作 skill(按「应用数据库 / 建表 / 改表 / SQL」召回)处理。
|
|
300
300
|
2. **只有连接、认证、数据库不存在、超时等环境信号,且没有代码信号** → 判定为**平台基建问题**,告知用户:
|
|
301
301
|
|
|
302
302
|
> 当前数据库连接错误不是应用代码的问题,可能是平台数据库连接配置异常。建议联系平台客服排查解决。
|