@lark-apaas/coding-steering 0.1.17-alpha.0 → 0.1.17

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.
@@ -0,0 +1,240 @@
1
+ ---
2
+ name: client-builtins-user-service
3
+ description: 前端用户鉴权服务与组件指南,提供 authClient(来自 @lark-apaas/client-toolkit/auth)用户认证 API 和 React 用户组件。Use when 需要:(1) 实现登录/登出功能,(2) 获取当前用户信息或展示用户头像姓名,(3) 使用用户/部门选择器组件的 props 与挂载(选完拿哪个 ID/传飞书/字段获取见 contacts-service),(4) 处理 401 未授权错误
4
+ steering: true
5
+ steering-topic: client_builtins_user_service
6
+ match-template-name: nestjs-react-fullstack
7
+ ---
8
+
9
+ # 账户/会话 SDK (authClient)
10
+
11
+ ## 概述
12
+
13
+ 项目通过 `authClient`(来自 `@lark-apaas/client-toolkit/auth`)提供用户信息与鉴权服务,用于用户登录、登出、获取用户信息等身份认证相关功能。
14
+
15
+ > **边界说明**:`authClient.session` 仅用于用户登录/登出/获取用户信息等鉴权操作。**插件调用(capability)不属于账户 SDK**,须使用独立的 `capabilityClient`(参见 plugin-guide)。
16
+ >
17
+ > **运行时边界**:本 skill 所有能力(`authClient`、`useCurrentUserProfile`、UserSelect/UserDisplay 等)仅限前端代码使用,**严禁在 `server/**` 中 import**。服务端获取用户身份用 `req.userContext` / `AuthNPaasService`(见 `user-identity` skill),完整边界规则见 coding-guide。
18
+
19
+ ## 怎么选(决策指引)
20
+
21
+ - **React 组件内展示当前用户**(名称/头像/邮箱/飞书 ID)→ 用 `useCurrentUserProfile()`(见下文),不要手动调 `getUserInfo`
22
+ - **登录/登出/跳转用户详情页,或非 React 上下文取用户信息** → `authClient.session.*`(本节)
23
+ - **选人/选部门/展示任意用户** → UserSelect / DepartmentSelect / UserDisplay 组件(见下文)
24
+
25
+ ## 统一响应结构
26
+
27
+ 所有 `authClient.session.*` 接口返回统一的 `DataloomServiceResponse<T>` 结构。在非浏览器环境调用会返回失败结构(`status: 400`,`error.message: 'Incompatible runtime environment'`)。
28
+
29
+ ```typescript
30
+ interface DataloomServiceBase {
31
+ status: number;
32
+ statusText: string;
33
+ }
34
+
35
+ interface DataloomServiceSuccess<T> extends DataloomServiceBase {
36
+ error: null;
37
+ data: T;
38
+ }
39
+
40
+ interface DataloomServiceFailure extends DataloomServiceBase {
41
+ error: {
42
+ code: number;
43
+ details: string;
44
+ hint: string | null;
45
+ message: string;
46
+ };
47
+ data: null;
48
+ }
49
+
50
+ type DataloomServiceResponse<T> = DataloomServiceSuccess<T> | DataloomServiceFailure;
51
+ ```
52
+
53
+ ## authClient 引入方式
54
+
55
+ ```typescript
56
+ import { authClient } from "@lark-apaas/client-toolkit/auth";
57
+ // authClient 是 SDK 内置的 singleton,零参可用,不需要异步初始化
58
+ ```
59
+
60
+ ## 接口速查表
61
+
62
+ | 方法 | 入参 | 成功时 data 类型 | 说明 |
63
+ | ----------------------------------------- | ------------------------------ | -------------------------- | ---------------------------------------------- |
64
+ | `session.redirectToLogin(options?)` | `SignInRedirectionOptions` | `'success'` | 跳转至 Dataloom 登录页(身份认证/单点登录) |
65
+ | `session.signOut()` | 无 | `null`(异步) | 退出登录,删除 cookie 中的登录态 |
66
+ | `session.navigateToUserProfile(options?)` | `NavigateToUserProfileOptions` | `'success'` | 跳转至当前登录用户详情页(头像/姓名点击进入) |
67
+ | `session.getUserInfo()` | 无 | `UserInfoResponse`(异步) | 获取当前登录用户信息,未登录返回 `status: 401` |
68
+
69
+ ### 入参类型定义
70
+
71
+ ```typescript
72
+ interface SignInRedirectionOptions {
73
+ /** 选填,登录成功后跳转回的页面。省略默认用当前页面 URL */
74
+ returnUrl?: string;
75
+ /** 选填,是否在新浏览器 tab 打开登录页。默认 false */
76
+ newTab?: boolean;
77
+ }
78
+
79
+ interface NavigateToUserProfileOptions {
80
+ /** 选填,是否在新浏览器 tab 打开详情页。默认 false */
81
+ newTab?: boolean;
82
+ }
83
+ ```
84
+
85
+ ### getUserInfo 返回类型定义
86
+
87
+ ```typescript
88
+ interface I18n {
89
+ language_code: number;
90
+ text: string;
91
+ }
92
+
93
+ type I18ns = I18n[];
94
+
95
+ interface Avatar {
96
+ source?: string;
97
+ image?: {
98
+ large?: string; // 图片url, 可能不返回
99
+ };
100
+ color?: string; // 颜色,可能不返回。
101
+ }
102
+
103
+ interface UserBaseInfo {
104
+ user_id?: number;
105
+ name?: I18ns;
106
+ avatar?: Avatar;
107
+ email?: string;
108
+ phone_number?: string;
109
+ tenant_name?: string;
110
+ }
111
+
112
+ interface UserInfoResponse {
113
+ user_info?: UserBaseInfo;
114
+ }
115
+ ```
116
+
117
+ ### 综合示例
118
+
119
+ ```typescript
120
+ import { authClient } from "@lark-apaas/client-toolkit/auth";
121
+ import { logger } from "@lark-apaas/client-toolkit/logger";
122
+
123
+ // 获取用户信息(React 组件内展示当前用户请优先用 useCurrentUserProfile,见下文)
124
+ const result = await authClient.session.getUserInfo();
125
+ if (result.error) {
126
+ logger.error("获取用户信息失败:", result.error.message);
127
+ if (result.status === 401) {
128
+ // 未登录:跳转登录页(可传 returnUrl / newTab,默认回到当前页)
129
+ authClient.session.redirectToLogin();
130
+ }
131
+ } else if (result.data?.user_info) {
132
+ const info = result.data.user_info;
133
+ const userName = info.name?.[0]?.text || "未知用户"; // 名称是多语言数组
134
+ const avatarUrl = info.avatar?.image?.large; // 头像 URL 可能不返回
135
+ // React 中通过 state 渲染上述字段,禁止 document.getElementById 等直接 DOM 操作
136
+ }
137
+
138
+ // 退出登录后回到登录页
139
+ const signOutResult = await authClient.session.signOut();
140
+ if (!signOutResult.error) {
141
+ authClient.session.redirectToLogin();
142
+ }
143
+
144
+ // 跳转当前用户详情页(新标签页打开)
145
+ authClient.session.navigateToUserProfile({ newTab: true });
146
+ ```
147
+
148
+ ## 错误处理
149
+
150
+ | 错误码 | 说明 | 处理建议 |
151
+ | ------ | -------------- | ---------------------- |
152
+ | `400` | 请求参数错误 | 检查传入参数是否正确 |
153
+ | `401` | 未授权访问 | 需要重新登录 |
154
+ | `403` | 权限不足 | 联系管理员分配权限 |
155
+ | `404` | 资源不存在 | 检查请求的资源是否存在 |
156
+ | `500` | 服务器内部错误 | 稍后重试或联系技术支持 |
157
+
158
+ 统一处理建议:`401` 调用 `authClient.session.redirectToLogin()`(见综合示例);`403`/`500` 用 toast(如 `sonner`)提示用户;其余情况展示 `error.message`。
159
+
160
+ ## 注意事项
161
+
162
+ 1. **环境限制**:本 skill 涉及的 `@lark-apaas/client-toolkit/**` 能力只能在前端/浏览器环境中使用,服务端调用会返回环境不兼容错误;更重要的是,服务端代码中禁止 import 这些前端 SDK
163
+ 2. **跨域配置**:确保应用域名已在 Dataloom 后台配置白名单
164
+ 3. **安全性**:不要在客户端代码中暴露敏感的配置信息
165
+
166
+ # 用户系统前端相关规范
167
+
168
+ ## 概述
169
+
170
+ 内置用户前端组件:UserSelect(单选/多选用户选择器,onChange 返回用户对象)、DepartmentSelect(部门选择组件,交互与受控规范与 UserSelect 一致)、UserDisplay(用户信息展示组件)。均基于统一的 userid 数据,用于用户相关的表单输入和数据展示场景。
171
+
172
+ ## 类型定义
173
+
174
+ ### 用户数据类型
175
+
176
+ ```typescript
177
+ import type { User } from "@/types/common";
178
+ ```
179
+
180
+ `User` 接口包含用户的基本信息,如用户 ID、姓名、头像字段。
181
+
182
+ ```typescript
183
+ export type User = {
184
+ /** 妙搭用户 ID:入库、传飞书内置插件、跨边界透传一律用它 */
185
+ user_id: string;
186
+ /** 飞书企业内 user_id(仅内部飞书用户),调飞书开放平台 API 用它 */
187
+ employee_id?: string;
188
+ name: string;
189
+ avatar: string;
190
+ };
191
+ ```
192
+
193
+ > **用户/部门 ID 怎么选、字段获取与权限引导 → 见 [`contacts-service`](../contacts-service/SKILL.md) skill;本 skill 只讲组件 API 用法。**
194
+
195
+ ## 当前用户信息的获取方案
196
+
197
+ ### Hooks 方法: `useCurrentUserProfile` - 在 React 中获取当前用户信息
198
+
199
+ - **文件路径**:`@lark-apaas/client-toolkit/hooks/useCurrentUserProfile`
200
+ - **功能**:获取当前登录用户的个人信息(含飞书 user_id)
201
+ - **返回值**:`Partial<IUserProfile>`(初始为空对象 `{}`,异步获取后填充完整字段)
202
+
203
+ **IUserProfile 字段**:
204
+
205
+ | 字段 | 类型 | 说明 |
206
+ | -------------- | -------- | -------------------------------------------------------- |
207
+ | `user_id` | `string` | 妙搭用户 ID |
208
+ | `email` | `string` | 用户邮箱 |
209
+ | `name` | `string` | 用户名称 |
210
+ | `avatar` | `string` | 用户头像 URL |
211
+ | `lark_user_id` | `string` | 飞书 user_id,通过额外异步请求获取,可能晚于其他字段就绪 |
212
+
213
+ > ⚠️ **空值处理(CRITICAL)**:Hook 初始返回空对象 `{}`(truthy),**MUST** 用 `if (!userInfo?.user_id)` 判加载态(不是 `!userInfo`);`lark_user_id` 可能为 `undefined`,使用前条件渲染。完整禁止行为清单与飞书 ID 转换指南(后端 `AuthNPaasService` 等)参见 `user-identity` skill。
214
+
215
+ ```typescript
216
+ import { useCurrentUserProfile } from "@lark-apaas/client-toolkit/hooks/useCurrentUserProfile";
217
+
218
+ const MyComponent = () => {
219
+ const userInfo = useCurrentUserProfile();
220
+ if (!userInfo?.user_id) return <div>加载中...</div>;
221
+ return <p>{userInfo.name}</p>;
222
+ };
223
+ ```
224
+
225
+ ## 用户展示与选择方案
226
+
227
+ 这些组件在使用之前必须读取 client/src/components/business-ui/README.md文件来理解用法
228
+
229
+ 目前可用的组件有:
230
+
231
+ - UserSelect 用户选择组件(@/components/business-ui/user-select)
232
+ - DepartmentSelect 部门选择组件(@/components/business-ui/department-select)
233
+ - UserDisplay - 用户展示组件(@/components/business-ui/user-display)
234
+
235
+ ## 使用注意事项
236
+
237
+ ### 最佳实践
238
+
239
+ - 根据展示场景选择合适的组件尺寸(`small`、`medium`、`large`)
240
+ - 对于用户信息 userId,禁止直接展示文本,总是使用 UserDisplay 组件展示