@lark-apaas/coding-steering 0.1.6-alpha.1 → 0.1.6-alpha.11

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 (39) hide show
  1. package/README.md +11 -2
  2. package/package.json +1 -1
  3. package/steering/design-stack/skills/.gitkeep +0 -0
  4. package/steering/nestjs-react-fullstack/skills/authn-guide/SKILL.md +122 -0
  5. package/steering/nestjs-react-fullstack/skills/authz-guide/SKILL.md +174 -0
  6. package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md +621 -0
  7. package/steering/nestjs-react-fullstack/skills/authz-guide/references/management-page-spec.md +505 -0
  8. package/steering/nestjs-react-fullstack/skills/authz-guide/references/runtime-role-controller-spec.md +203 -0
  9. package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-examples.md +90 -0
  10. package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-types.md +216 -0
  11. package/steering/nestjs-react-fullstack/skills/client-add-aily-web-chat/SKILL.md +139 -0
  12. package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +405 -0
  13. package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +628 -0
  14. package/steering/nestjs-react-fullstack/skills/devops-guide/SKILL.md +119 -0
  15. package/steering/nestjs-react-fullstack/skills/feishu/SKILL.md +270 -0
  16. package/steering/nestjs-react-fullstack/skills/feishu/references/approval.md +214 -0
  17. package/steering/nestjs-react-fullstack/skills/feishu/references/attendance.md +163 -0
  18. package/steering/nestjs-react-fullstack/skills/feishu/references/bitable.md +309 -0
  19. package/steering/nestjs-react-fullstack/skills/feishu/references/calendar.md +190 -0
  20. package/steering/nestjs-react-fullstack/skills/feishu/references/contacts.md +160 -0
  21. package/steering/nestjs-react-fullstack/skills/feishu/references/doc.md +256 -0
  22. package/steering/nestjs-react-fullstack/skills/feishu/references/drive.md +103 -0
  23. package/steering/nestjs-react-fullstack/skills/feishu/references/events.md +198 -0
  24. package/steering/nestjs-react-fullstack/skills/feishu/references/id-convert.md +128 -0
  25. package/steering/nestjs-react-fullstack/skills/feishu/references/messaging.md +207 -0
  26. package/steering/nestjs-react-fullstack/skills/feishu/references/oauth.md +164 -0
  27. package/steering/nestjs-react-fullstack/skills/feishu/references/perm.md +90 -0
  28. package/steering/nestjs-react-fullstack/skills/feishu/references/wiki.md +164 -0
  29. package/steering/nestjs-react-fullstack/skills/openapi-guide/SKILL.md +267 -0
  30. package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +582 -0
  31. package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +357 -0
  32. package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +513 -0
  33. package/steering/nestjs-react-fullstack/skills/react-hook-best-practices/SKILL.md +118 -0
  34. package/steering/nestjs-react-fullstack/skills/server-builtins-file-storage-service/SKILL.md +177 -0
  35. package/steering/nestjs-react-fullstack/skills/trigger-guide/SKILL.md +452 -0
  36. package/steering/nestjs-react-fullstack/skills/user-identity/SKILL.md +300 -0
  37. package/steering/nestjs-react-fullstack/skills/user-management-best-practices/SKILL.md +142 -0
  38. package/steering/nestjs-react-fullstack/skills_local/code-fix/SKILL.md +253 -0
  39. package/steering/nestjs-react-fullstack/skills_local/coding-guide/SKILL.md +585 -0
@@ -0,0 +1,203 @@
1
+ # 运行态角色管理 Controller 实施规格
2
+
3
+ > **前置条件**:应用必须已开启角色服务。
4
+
5
+ ---
6
+
7
+ ## 1. 注入
8
+
9
+ ```typescript
10
+ import { AuthorizationSDK } from '@lark-apaas/fullstack-nestjs-core';
11
+
12
+ // AuthorizationSDK 由 AuthZPaasModule 全局注册,直接注入即可
13
+ // SDK 自动从请求上下文获取 appId 和 userId
14
+ constructor(private readonly authzSDK: AuthorizationSDK) {}
15
+ ```
16
+
17
+ ## 2. DTO 定义
18
+
19
+ ```typescript
20
+ import type { MemberMutationData, FilterParams, MemberType } from '@lark-apaas/fullstack-nestjs-core';
21
+
22
+ export class CreateRoleDto {
23
+ role: { name: string; description?: string; bizID: string };
24
+ userID?: string; // 可不传,默认为当前登录用户
25
+ }
26
+
27
+ export class UpdateRoleDto {
28
+ role: { name?: string; description?: string };
29
+ userID?: string;
30
+ }
31
+
32
+ export class AddMembersDto {
33
+ members: MemberMutationData;
34
+ userID?: string;
35
+ }
36
+
37
+ export class RemoveMembersDto {
38
+ members: MemberMutationData;
39
+ userID?: string;
40
+ }
41
+
42
+ export class SearchDto {
43
+ query: string;
44
+ filters?: FilterParams;
45
+ includeExternalUser?: boolean;
46
+ includeExternalGroup?: boolean;
47
+ pageSize?: number;
48
+ page?: number;
49
+ userID?: string;
50
+ }
51
+
52
+ export class ListMembersQueryDto {
53
+ type?: MemberType;
54
+ page?: number;
55
+ pageSize?: number;
56
+ }
57
+ ```
58
+
59
+ ## 3. Controller
60
+
61
+ 管理接口可按需改为 `@CanRole(['admin'])`:
62
+
63
+ ```typescript
64
+ import { Controller, Get, Post, Put, Delete, Body, Param, Query } from '@nestjs/common';
65
+ import { AuthorizationSDK } from '@lark-apaas/fullstack-nestjs-core';
66
+
67
+ @Controller('api/role_manager')
68
+ export class AuthorizationController {
69
+ constructor(private readonly authzSDK: AuthorizationSDK) {}
70
+
71
+ @Get('roles')
72
+ listRoles() { return this.authzSDK.roles.list(); }
73
+
74
+ @Get('roles/:bizID')
75
+ getRole(@Param('bizID') bizID: string) { return this.authzSDK.roles.get(bizID); }
76
+
77
+ @Post('roles')
78
+ createRole(@Body() dto: CreateRoleDto) { return this.authzSDK.roles.create(dto); }
79
+
80
+ @Put('roles/:bizID')
81
+ updateRole(@Param('bizID') bizID: string, @Body() dto: UpdateRoleDto) {
82
+ return this.authzSDK.roles.update(bizID, dto);
83
+ }
84
+
85
+ @Delete('roles/:bizID')
86
+ deleteRole(@Param('bizID') bizID: string) { return this.authzSDK.roles.delete(bizID); }
87
+
88
+ @Get('roles/:bizID/members')
89
+ listMembers(@Param('bizID') bizID: string, @Query() query: ListMembersQueryDto) {
90
+ return this.authzSDK.members.list(bizID, query);
91
+ }
92
+
93
+ @Post('roles/:bizID/members')
94
+ addMembers(@Param('bizID') bizID: string, @Body() dto: AddMembersDto) {
95
+ return this.authzSDK.members.add(bizID, dto);
96
+ }
97
+
98
+ @Post('roles/:bizID/members/batch_remove')
99
+ removeMembers(@Param('bizID') bizID: string, @Body() dto: RemoveMembersDto) {
100
+ return this.authzSDK.members.remove(bizID, dto);
101
+ }
102
+
103
+ @Delete('roles/:bizID/members')
104
+ clearMembers(@Param('bizID') bizID: string) { return this.authzSDK.members.clear(bizID); }
105
+
106
+ @Post('search')
107
+ search(@Body() dto: SearchDto) { return this.authzSDK.search.search(dto); }
108
+ }
109
+ ```
110
+
111
+ ## 4. Shared 类型定义
112
+
113
+ 在 `shared/api.interface.ts` 中定义前后端共享的接口类型。SDK 已导出的类型直接 re-export,请求/响应类型与 Controller DTO 对齐。
114
+
115
+ ```typescript
116
+ // shared/api.interface.ts — 角色管理相关类型
117
+
118
+ // ---- re-export SDK 类型,前后端统一引用 ----
119
+ export type {
120
+ ForceRoleDTO,
121
+ RoleMemberDTO,
122
+ MemberMutationData,
123
+ MemberType,
124
+ UserSimpleDTO,
125
+ DepartmentDTO,
126
+ DepartmentMutationDTO,
127
+ ChatSimpleDTO,
128
+ PresetGroupDTO,
129
+ SearchResponse,
130
+ SearchResult,
131
+ FilterParams,
132
+ I18nText,
133
+ } from '@lark-apaas/fullstack-nestjs-core';
134
+
135
+ // ---- 请求类型(与 Controller DTO 一致) ----
136
+
137
+ /** POST /api/role_manager/roles */
138
+ export interface CreateRoleRequest {
139
+ role: { name: string; description?: string; bizID: string };
140
+ }
141
+
142
+ /** PUT /api/role_manager/roles/:bizID */
143
+ export interface UpdateRoleRequest {
144
+ role: { name?: string; description?: string };
145
+ }
146
+
147
+ /** POST /api/role_manager/roles/:bizID/members */
148
+ export interface AddMembersRequest {
149
+ members: MemberMutationData;
150
+ }
151
+
152
+ /** POST /api/role_manager/roles/:bizID/members/batch_remove */
153
+ export interface RemoveMembersRequest {
154
+ members: MemberMutationData;
155
+ }
156
+
157
+ /** POST /api/role_manager/search */
158
+ export interface SearchMembersRequest {
159
+ query: string;
160
+ filters?: FilterParams;
161
+ pageSize?: number;
162
+ page?: number;
163
+ }
164
+ ```
165
+
166
+ > **注意**:请求类型省略了 `userID` 字段——`userID` 由后端从请求上下文自动注入,前端无需传递。
167
+
168
+ ### 前端 API 层引用示例
169
+
170
+ ```typescript
171
+ // client/src/api/index.ts
172
+ import type {
173
+ ForceRoleDTO,
174
+ CreateRoleRequest,
175
+ UpdateRoleRequest,
176
+ AddMembersRequest,
177
+ RemoveMembersRequest,
178
+ SearchMembersRequest,
179
+ SearchResponse,
180
+ } from '@shared/api.interface';
181
+
182
+ export async function getRoles(): Promise<ForceRoleDTO[]> { ... }
183
+ export async function createRole(data: CreateRoleRequest): Promise<void> { ... }
184
+ export async function updateRole(bizID: string, data: UpdateRoleRequest): Promise<void> { ... }
185
+ export async function deleteRole(bizID: string): Promise<void> { ... }
186
+ export async function addRoleMembers(bizID: string, data: AddMembersRequest): Promise<void> { ... }
187
+ export async function removeRoleMembers(bizID: string, data: RemoveMembersRequest): Promise<void> { ... }
188
+ export async function searchMembers(data: SearchMembersRequest): Promise<SearchResponse> { ... }
189
+ ```
190
+
191
+ ## 5. 模块注册
192
+
193
+ 创建 `server/modules/role-manager/role-manager.module.ts`,并在 `app.module.ts` 中注册(ViewModule 之前)。
194
+
195
+ ## 错误处理
196
+
197
+ SDK 内部统一将平台错误转为 `HttpException`,Controller 无需 try-catch:
198
+
199
+ | 场景 | HTTP 状态码 | 说明 |
200
+ |------|-----------|------|
201
+ | 平台 4xx | 透传 | 参数错误、权限不足、角色不存在等 |
202
+ | 平台 5xx | 502 | 平台内部错误 |
203
+ | HTTP 200 + `status_code !== '0'` | 502 | 平台业务错误(如远程服务不可用) |
@@ -0,0 +1,90 @@
1
+ # AuthorizationSDK 调用示例
2
+
3
+ > 完整类型定义见 [sdk-types.md](./sdk-types.md)
4
+
5
+ ```typescript
6
+ // ===================== 角色管理 =====================
7
+
8
+ // 获取所有角色列表,needMember=true 会在每个角色中附带成员数据
9
+ const roles: ForceRoleDTO[] = await sdk.roles.list({ needMember: true });
10
+
11
+ // 获取单个角色详情
12
+ const role: ForceRoleDTO = await sdk.roles.get('editor');
13
+
14
+ // 创建角色,bizID 为角色唯一 key,应用内不可重复
15
+ const created: CreateRoleResponse = await sdk.roles.create({
16
+ role: { bizID: 'editor', name: '编辑者', description: '可编辑内容' },
17
+ });
18
+ // created.bizID → 'editor', created.apiID → 平台分配的 API 标识
19
+
20
+ // 更新角色名称或描述
21
+ await sdk.roles.update('editor', { role: { name: '高级编辑者' } });
22
+
23
+ // 删除角色(⚠️ 包含「企业全员」或「互联网公开」成员的角色不支持删除)
24
+ await sdk.roles.delete('editor');
25
+
26
+ // ===================== 成员管理 =====================
27
+ // 成员按类型分组传递(MemberMutationData),不是扁平数组
28
+
29
+ // 添加用户到角色
30
+ await sdk.members.add('admin', {
31
+ members: { userList: [{ userID: '1826968659245100' }] },
32
+ });
33
+
34
+ // 添加部门到角色(ID 保持字符串)
35
+ await sdk.members.add('admin', {
36
+ members: { departmentList: [{ id: '7579138586559286811' }] },
37
+ });
38
+
39
+ // 添加群组到角色
40
+ await sdk.members.add('admin', {
41
+ members: { groupChatList: [{ chatID: '123456' }] },
42
+ });
43
+
44
+ // 设置包含应用开发者(唯一可通过 SDK 修改的特殊范围)
45
+ await sdk.members.add('admin', { members: { isContainsAdmin: true } });
46
+ // 取消包含应用开发者
47
+ await sdk.members.remove('admin', { members: { isContainsAdmin: true } });
48
+
49
+ // 查询成员列表,不传 type 返回所有类型(用户+部门+群组)
50
+ const membersRes: ListMembersResponse = await sdk.members.list('admin');
51
+ // 响应按类型分组:
52
+ // membersRes.members.userList → 用户列表
53
+ // membersRes.members.departmentList → 部门列表
54
+ // membersRes.members.groupChatList → 群组列表
55
+ // membersRes.members.allEmployees → 是否包含企业全员(只读)
56
+ // membersRes.members.public → 是否互联网公开(只读)
57
+ // membersRes.members.presetGroup?.isContainsAdmin → 是否包含应用开发者
58
+ // membersRes.total / membersRes.hasMore → 分页信息
59
+
60
+ // 按类型过滤查询,只获取用户成员
61
+ const userMembers = await sdk.members.list('admin', { type: 'User', pageSize: 20 });
62
+
63
+ // 从角色移除指定用户
64
+ await sdk.members.remove('admin', {
65
+ members: { userList: [{ userID: '1826968659245100' }] },
66
+ });
67
+
68
+ // 清空角色下所有成员
69
+ await sdk.members.clear('admin');
70
+
71
+ // ===================== 混合搜索 =====================
72
+ // 同时搜索用户、部门、群组,不传 filters 时默认三种类型各 pageSize=20
73
+
74
+ const searchRes: SearchResponse = await sdk.search.search({
75
+ query: '张三',
76
+ pageSize: 20,
77
+ page: 1,
78
+ });
79
+
80
+ // 响应按类型分组,分别遍历
81
+ searchRes.result.userResult?.items?.forEach(u =>
82
+ console.log('用户:', u.name, u.userID, u.avatar),
83
+ );
84
+ searchRes.result.departmentResult?.items?.forEach(d =>
85
+ console.log('部门:', d.name, d.departmentID),
86
+ );
87
+ searchRes.result.chatResult?.items?.forEach(c =>
88
+ console.log('群组:', c.name, c.chatID, '成员数:', c.userCount),
89
+ );
90
+ ```
@@ -0,0 +1,216 @@
1
+ # AuthorizationSDK 类型定义
2
+
3
+ > 所有类型均从 `@lark-apaas/fullstack-nestjs-core` 导入。
4
+
5
+ ```typescript
6
+ // ===================== 通用 =====================
7
+
8
+ interface I18nText {
9
+ [locale: string]: string;
10
+ }
11
+
12
+ /** 部门名称的国际化格式(language_code: 2052=zh_cn, 1033=en_us) */
13
+ interface I18nLangItem {
14
+ language_code: number;
15
+ text: string;
16
+ }
17
+
18
+ // ===================== 角色 =====================
19
+
20
+ interface ForceRoleDTO {
21
+ id?: number; // 数据库自增 ID
22
+ apiID?: string; // 平台 API 标识
23
+ bizID?: string; // 角色唯一 key
24
+ tenantID?: number; // 租户 ID
25
+ name?: string; // 角色名称
26
+ description?: string; // 角色描述
27
+ type?: number; // 0-普通角色, 1-预置角色
28
+ source?: string; // 来源: 'aPaaS' | 'MiaoDa'
29
+ status?: number;
30
+ memberScope?: string; // 'all' | 'custom'
31
+ envScope?: string; // 'all' | 'custom'
32
+ feature?: string;
33
+ deletedAt?: number;
34
+ version?: number;
35
+ createdBy?: number;
36
+ createdAt?: number;
37
+ updatedBy?: number;
38
+ updatedAt?: number;
39
+ roleMembers?: RoleMemberDTO; // 仅 needMember=true 时返回
40
+ }
41
+
42
+ interface ListRolesParams {
43
+ needMember?: boolean; // 是否返回成员信息,默认 true
44
+ userID?: string; // 可不传,默认为当前登录用户
45
+ }
46
+
47
+ interface CreateRoleParams {
48
+ role: {
49
+ name: string; // 角色名称(必填)
50
+ description?: string;
51
+ bizID: string; // 角色唯一 key(必填)
52
+ };
53
+ userID?: string; // 可不传,默认为当前登录用户
54
+ }
55
+
56
+ interface CreateRoleResponse {
57
+ bizID: string; // 角色唯一 key
58
+ apiID: string; // 平台 API 标识
59
+ }
60
+
61
+ interface UpdateRoleParams {
62
+ role: { name?: string; description?: string };
63
+ userID?: string; // 可不传,默认为当前登录用户
64
+ }
65
+
66
+ // ===================== 成员 =====================
67
+
68
+ type MemberType =
69
+ | 'User'
70
+ | 'Department'
71
+ | 'GroupChat'
72
+ | 'Tenant'
73
+ | 'PresetGroup'
74
+ | 'Public'
75
+ | 'AllEmployee';
76
+
77
+ interface UserSimpleDTO {
78
+ userID?: string; // 用户 ID
79
+ name?: I18nText;
80
+ avatar?: string;
81
+ email?: string;
82
+ userType?: string;
83
+ larkUserID?: number;
84
+ department?: DepartmentSimpleDTO;
85
+ }
86
+
87
+ interface DepartmentSimpleDTO {
88
+ departmentID?: number;
89
+ larkDepartmentID?: number;
90
+ name?: I18nText; // SDK 归一化后统一为 { zh_cn, en_us } 格式
91
+ }
92
+
93
+ /** 查询响应中的部门(SDK 已将平台的数组格式归一化为 I18nText) */
94
+ interface DepartmentDTO {
95
+ id?: string; // 部门 ID
96
+ name?: I18nText; // 部门名称,SDK 归一化后统一为 { zh_cn, en_us } 格式
97
+ }
98
+
99
+ /** 变更入参中的部门(提交接口只需 id) */
100
+ interface DepartmentMutationDTO {
101
+ id?: string; // 部门 ID
102
+ }
103
+
104
+ interface ChatSimpleDTO {
105
+ chatID?: string; // 群组 ID
106
+ name?: I18nText;
107
+ avatar?: string;
108
+ isExternal?: boolean;
109
+ }
110
+
111
+ interface PresetGroupDTO {
112
+ isContainsAdmin?: boolean;
113
+ }
114
+
115
+ /** 平台返回的成员数据(list 响应中使用) */
116
+ interface RoleMemberDTO {
117
+ userList?: UserSimpleDTO[];
118
+ departmentList?: DepartmentDTO[];
119
+ groupChatList?: ChatSimpleDTO[];
120
+ allEmployees?: boolean; // 是否包含企业全员(只读)
121
+ public?: boolean; // 是否互联网公开(只读)
122
+ presetGroup?: PresetGroupDTO; // 是否包含应用开发者
123
+ }
124
+
125
+ /** 用户接口的成员变更数据(add/remove 参数中使用),SDK 内部自动映射 isContainsAdmin 为 presetGroup */
126
+ interface MemberMutationData {
127
+ userList?: UserSimpleDTO[];
128
+ departmentList?: DepartmentMutationDTO[];
129
+ groupChatList?: ChatSimpleDTO[];
130
+ isContainsAdmin?: boolean; // 是否包含应用开发者,SDK 内部映射为 presetGroup.isContainsAdmin
131
+ }
132
+
133
+ interface ListMembersParams {
134
+ type?: MemberType; // 不传返回所有类型
135
+ page?: number;
136
+ pageSize?: number;
137
+ userID?: string; // 可不传,默认为当前登录用户
138
+ }
139
+
140
+ interface ListMembersResponse {
141
+ members: RoleMemberDTO; // 按类型分组
142
+ total: number;
143
+ hasMore: boolean;
144
+ }
145
+
146
+ interface AddMembersParams {
147
+ members: MemberMutationData;
148
+ userID?: string; // 可不传,默认为当前登录用户
149
+ }
150
+
151
+ interface RemoveMembersParams {
152
+ members: MemberMutationData;
153
+ userID?: string; // 可不传,默认为当前登录用户
154
+ }
155
+
156
+ // ===================== 搜索 =====================
157
+
158
+ interface CommonParam {
159
+ searchable?: boolean;
160
+ pageSize?: number;
161
+ offset?: number;
162
+ }
163
+
164
+ interface FilterParams {
165
+ userParam?: { commonParam?: CommonParam };
166
+ departmentParam?: { commonParam?: CommonParam; searchType?: string };
167
+ chatParam?: { commonParam?: CommonParam };
168
+ }
169
+
170
+ interface SearchParams {
171
+ query: string; // 关键词(必填)
172
+ filters?: FilterParams; // 不传时默认三种类型各 pageSize=20
173
+ includeExternalUser?: boolean; // 默认 true
174
+ includeExternalGroup?: boolean; // 默认 true
175
+ pageSize?: number; // 默认 20
176
+ page?: number; // 默认 1
177
+ userID?: string; // 可不传,默认为当前登录用户
178
+ }
179
+
180
+ interface SearchUserEntity {
181
+ userID?: string;
182
+ larkUserID?: number;
183
+ name?: I18nText;
184
+ avatar?: string;
185
+ department?: DepartmentSimpleDTO; // 用户所属部门(name 已归一化)
186
+ userType?: string;
187
+ email?: string;
188
+ userStatus?: number;
189
+ }
190
+
191
+ interface DepartmentEntity {
192
+ departmentID?: number;
193
+ larkDepartmentID?: number;
194
+ name?: I18nText; // SDK 归一化后统一为 { zh_cn, en_us } 格式
195
+ }
196
+
197
+ interface SearchChatEntity {
198
+ chatID?: number;
199
+ name?: I18nText;
200
+ avatar?: string;
201
+ isExternal?: boolean;
202
+ userCount?: number;
203
+ }
204
+
205
+ interface SearchResult {
206
+ userResult?: { total?: number; items?: SearchUserEntity[] };
207
+ departmentResult?: { total?: number; items?: DepartmentEntity[] };
208
+ chatResult?: { total?: number; items?: SearchChatEntity[] };
209
+ }
210
+
211
+ interface SearchResponse {
212
+ result: SearchResult;
213
+ }
214
+ ```
215
+
216
+ > **部门名称归一化**:平台返回的部门 `name` 有两种格式(`I18nText` 对象或 `[{ language_code, text }]` 数组),SDK 在所有查询接口(`roles.list`、`roles.get`、`members.list`、`search.search`)中已统一归一化为 `I18nText`(`{ zh_cn: "...", en_us: "..." }`),下游直接用 `name?.zh_cn` 取值即可。
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: client-add-aily-web-chat
3
+ description: Use when integrating Aily AI chat panel into a web app using @lark-apaas/client-toolkit/aily-chat. 触发词:Aily 聊天, Aily 对话面板, Aily chat, aily-chat SDK, AI 对话组件集成
4
+ ---
5
+
6
+ # 添加 Aily Web 对话面板
7
+
8
+ 使用 `@lark-apaas/client-toolkit/aily-chat` 将 Aily 对话面板集成到 Web 应用。SDK 与框架无关(纯 DOM 操作)。
9
+
10
+ ## Quick Reference
11
+
12
+ | 操作 | 方法 |
13
+ |------|------|
14
+ | 初始化 | `await initAilyChat(container, config)` |
15
+ | 发送消息 | `chatPanel.sendMessage({ content, skillID? })` |
16
+ | 清空记录 | `chatPanel.clear()` |
17
+ | 清空并停止 | `chatPanel.clearAndStop()` |
18
+ | 更新配置 | `chatPanel.updateConfig(partialConfig)` |
19
+ | 销毁 | `chatPanel.destroy()` |
20
+
21
+ ## 核心流程
22
+
23
+ ### 1. 向用户获取 appKey
24
+
25
+ 实现前必须先询问用户:
26
+
27
+ > "请提供你的 Aily `appKey` 以配置对话组件。你可以在 [Aily 平台](https://apaas.feishu.cn/ai/projects) 中找到对应应用的 appKey。"
28
+
29
+ ### 2. 集成代码
30
+
31
+ React 组件示例(vanilla JS 同理,对 DOM 元素调用 `initAilyChat` 即可):
32
+
33
+ ```tsx
34
+ import { useEffect, useRef } from "react";
35
+ import { initAilyChat, type ChatPanel } from "@lark-apaas/client-toolkit/aily-chat";
36
+
37
+ interface AilyChatProps {
38
+ appKey: string;
39
+ anonymous?: boolean;
40
+ }
41
+
42
+ const containerStyle = { width: "100%", height: "500px" };
43
+
44
+ const AilyChat = ({ appKey, anonymous }: AilyChatProps) => {
45
+ const containerRef = useRef<HTMLDivElement>(null);
46
+ const chatPanelRef = useRef<ChatPanel | null>(null);
47
+
48
+ useEffect(() => {
49
+ if (!containerRef.current) return;
50
+ let destroyed = false;
51
+
52
+ initAilyChat(containerRef.current, {
53
+ appKey,
54
+ anonymous,
55
+ conversion: { needAvatar: true },
56
+ events: {
57
+ onReady: () => console.log("对话就绪"),
58
+ onError: (err) => console.error("对话错误", err),
59
+ },
60
+ }).then((panel) => {
61
+ if (destroyed) panel.destroy();
62
+ else chatPanelRef.current = panel;
63
+ });
64
+
65
+ return () => {
66
+ destroyed = true;
67
+ chatPanelRef.current?.destroy();
68
+ chatPanelRef.current = null;
69
+ };
70
+ }, [appKey, anonymous]);
71
+
72
+ return <div ref={containerRef} style={containerStyle} />;
73
+ };
74
+
75
+ export default AilyChat;
76
+ ```
77
+
78
+ ### 3. 告知用户 UI 定制选项
79
+
80
+ 代码集成完成后,**必须**向用户展示以下可定制选项:
81
+
82
+ - 修改组件标题/顶部标题
83
+ - 关闭/开启组件标题栏
84
+ - 显示/隐藏关闭按钮
85
+ - 隐藏/展示用户头像、隐藏/展示系统头像
86
+ - 修改用户头像或系统头像,并发送新头像图片
87
+ - 隐藏/展示组件欢迎语
88
+
89
+ ## 配置参考
90
+
91
+ ### WebSDKConfig(顶层)
92
+
93
+ | 字段 | 类型 | 必填 | 说明 |
94
+ |------|------|------|------|
95
+ | `appKey` | string | 是 | 应用标识 |
96
+ | `uuid` | string | 否 | 用户标识,启用 `needCache` 时必填 |
97
+ | `anonymous` | boolean | 否 | `true` 开启匿名模式,跳过登录。**须在 Aily 应用「渠道管理」中将 SDK 模式开启匿名访问,否则会报错「匿名SDK没有开启支持使用应用身份调用API和SDK」** |
98
+ | `conversion` | object | 否 | 对话配置(头像、缓存、欢迎语) |
99
+ | `editor` | object | 否 | 顶部栏配置(标题、关闭按钮) |
100
+ | `events` | object | 否 | 事件回调 |
101
+
102
+ ### conversion
103
+
104
+ | 字段 | 类型 | 默认值 | 说明 |
105
+ |------|------|--------|------|
106
+ | `needCache` | boolean | `false` | 启用消息缓存(需同时提供 `uuid`) |
107
+ | `storageType` | string | `"memory"` | `memory` / `sessionStorage` / `localStorage` / `indexDB` / `server` |
108
+
109
+ > 头像和欢迎语相关字段见上方「UI 定制选项」表。
110
+
111
+ ### editor
112
+
113
+ | 字段 | 类型 | 默认值 | 说明 |
114
+ |------|------|--------|------|
115
+ | `display` | boolean | `true` | 是否显示编辑器 |
116
+ | `needHeader` | boolean | `true` | 是否显示顶部 header(含标题和新建对话按钮),仅 V2 生效 |
117
+ | `headerTitle` | string | 应用名称 | 对话标题,显示在 header 中 |
118
+ | `needCloseButton` | boolean | `false` | 是否显示关闭按钮。配合 `events.onClose` 监听点击 |
119
+
120
+ ### events
121
+
122
+ | 事件 | 签名 | 说明 |
123
+ |------|------|------|
124
+ | `onReady` | `() => void` | 对话就绪 |
125
+ | `onError` | `(error: Error) => void` | 发生错误 |
126
+ | `onMessage` | `(message: unknown) => void` | 收到消息 |
127
+ | `onInited` | `(data: { sessionId, conversationId, handler }) => void` | 会话已创建 |
128
+ | `onInitedWithWelcome` | `() => void` | 欢迎语已创建 |
129
+ | `onClose` | `() => void` | 用户点击关闭按钮时触发(需配合 `editor.needCloseButton: true`) |
130
+
131
+ ## Common Mistakes
132
+
133
+ | 错误 | 正确做法 |
134
+ |------|----------|
135
+ | 忘记在 useEffect 清理时调用 `destroy()` | 必须在 cleanup 中销毁 ChatPanel,否则内存泄漏 |
136
+ | 启用 `needCache` 但未传 `uuid` | 缓存依赖 `uuid` 识别用户,缺失则无法恢复历史消息 |
137
+ | 容器元素无固定宽高 | 容器必须有明确尺寸,否则面板不可见 |
138
+ | 在 React 严格模式下未处理重复初始化 | 用 `destroyed` 标志防止 StrictMode 双重 mount 导致重复面板 |
139
+ | 设置 `anonymous: true` 但未在 Aily 平台开启匿名访问 | 须前往 Aily 应用「渠道管理」将 SDK 模式开启匿名访问,否则报错「匿名SDK没有开启支持使用应用身份调用API和SDK」 |