apaas-oapi-client 0.1.30 → 0.1.32

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/dist/index.js CHANGED
@@ -43,6 +43,64 @@ async function functionLimiter(fn, options = {}) {
43
43
  return wrapped();
44
44
  }
45
45
 
46
+ /**
47
+ * 判断错误是否可重试
48
+ */
49
+ function isRetryableError(error) {
50
+ // 网络错误
51
+ if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
52
+ return true;
53
+ }
54
+ // Axios 错误
55
+ if (axios.isAxiosError(error)) {
56
+ const axiosError = error;
57
+ // 没有响应(网络错误)
58
+ if (!axiosError.response) {
59
+ return true;
60
+ }
61
+ // 5xx 服务器错误
62
+ if (axiosError.response.status >= 500) {
63
+ return true;
64
+ }
65
+ // 429 限流
66
+ if (axiosError.response.status === 429) {
67
+ return true;
68
+ }
69
+ }
70
+ return false;
71
+ }
72
+ /**
73
+ * 带重试的函数执行
74
+ */
75
+ async function executeWithRetry(fn, options = {}, logContext = '', logger) {
76
+ const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000, backoffMultiplier = 2 } = options;
77
+ let lastError;
78
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
79
+ try {
80
+ return await fn();
81
+ }
82
+ catch (error) {
83
+ lastError = error;
84
+ // 最后一次尝试,直接抛出
85
+ if (attempt === maxRetries) {
86
+ break;
87
+ }
88
+ // 判断是否可重试
89
+ if (!isRetryableError(error)) {
90
+ throw error;
91
+ }
92
+ // 计算延迟时间(指数退避)
93
+ const delay = Math.min(initialDelay * Math.pow(backoffMultiplier, attempt), maxDelay);
94
+ if (logger) {
95
+ logger(LoggerLevel.warn, `${logContext} Attempt ${attempt + 1}/${maxRetries + 1} failed, retrying in ${delay}ms...`, error instanceof Error ? error.message : String(error));
96
+ }
97
+ // 等待后重试
98
+ await new Promise(resolve => setTimeout(resolve, delay));
99
+ }
100
+ }
101
+ // 所有重试都失败
102
+ throw lastError;
103
+ }
46
104
  /**
47
105
  * aPaaS OpenAPI 客户端
48
106
  */
@@ -61,11 +119,14 @@ class Client {
61
119
  this.object = {
62
120
  /**
63
121
  * 列出所有对象(数据表)
64
- * @param params 请求参数 { offset, filter?, limit }
65
- * @returns 接口返回结果
122
+ * @param params 请求参数 { offset?, filter?, limit? }
123
+ * @returns 接口返回结果,包含 has_more 字段表示是否还有更多数据
66
124
  */
67
125
  list: async (params) => {
68
- const { offset, filter, limit } = params;
126
+ var _a, _b;
127
+ const offset = (_a = params === null || params === void 0 ? void 0 : params.offset) !== null && _a !== void 0 ? _a : 0;
128
+ const limit = (_b = params === null || params === void 0 ? void 0 : params.limit) !== null && _b !== void 0 ? _b : 50;
129
+ const filter = params === null || params === void 0 ? void 0 : params.filter;
69
130
  await this.ensureTokenValid();
70
131
  const url = `/api/data/v1/namespaces/${this.namespace}/meta/objects/list`;
71
132
  this.log(LoggerLevel.debug, `[object.list] Fetching objects list: offset=${offset}, limit=${limit}`);
@@ -78,8 +139,66 @@ class Client {
78
139
  });
79
140
  this.log(LoggerLevel.debug, `[object.list] Objects list fetched successfully: code=${res.data.code}`);
80
141
  this.log(LoggerLevel.trace, `[object.list] Response: ${JSON.stringify(res.data)}`);
142
+ // 添加 has_more 字段判断是否还有更多数据
143
+ if (res.data && res.data.data) {
144
+ const total = res.data.data.total || 0;
145
+ const currentEnd = offset + limit;
146
+ res.data.has_more = currentEnd < total;
147
+ this.log(LoggerLevel.debug, `[object.list] has_more=${res.data.has_more}, total=${total}, currentEnd=${currentEnd}`);
148
+ }
81
149
  return res.data;
82
150
  },
151
+ /**
152
+ * 列出所有对象(数据表)- 支持自动分页查询
153
+ * @description 该方法会自动处理分页,直到没有更多数据为止
154
+ * @param params 请求参数 { filter?, limit? }
155
+ * @returns { total, items }
156
+ */
157
+ listWithIterator: async (params) => {
158
+ var _a, _b, _c, _d;
159
+ const filter = params === null || params === void 0 ? void 0 : params.filter;
160
+ const limit = (_a = params === null || params === void 0 ? void 0 : params.limit) !== null && _a !== void 0 ? _a : 50;
161
+ let results = [];
162
+ let offset = 0;
163
+ let total = 0;
164
+ let hasMore = true;
165
+ let page = 0;
166
+ let totalPages = 0;
167
+ this.log(LoggerLevel.info, `[object.listWithIterator] Starting paginated query with limit=${limit}`);
168
+ while (hasMore) {
169
+ const res = await this.object.list({
170
+ offset,
171
+ limit,
172
+ filter
173
+ });
174
+ if (res.code !== '0') {
175
+ this.log(LoggerLevel.error, `[object.listWithIterator] Error querying objects: code=${res.code}, msg=${res.msg}`);
176
+ throw new Error(res.msg || `Query failed with code ${res.code}`);
177
+ }
178
+ page += 1;
179
+ if (res.data && Array.isArray(res.data.items)) {
180
+ results = results.concat(res.data.items);
181
+ }
182
+ if (res.data && (res.data.total !== undefined && res.data.total !== null)) {
183
+ total = res.data.total;
184
+ }
185
+ if (page === 1) {
186
+ totalPages = Math.ceil(total / limit);
187
+ this.log(LoggerLevel.info, `[object.listWithIterator] Total objects: ${total}, pages: ${totalPages}`);
188
+ }
189
+ // 判断是否还有更多数据
190
+ hasMore = res.has_more === true;
191
+ offset += limit;
192
+ const padLength = totalPages.toString().length;
193
+ const pageStr = page.toString().padStart(padLength, '0');
194
+ const totalPagesStr = totalPages.toString().padStart(padLength, '0');
195
+ this.log(LoggerLevel.info, `[object.listWithIterator] Page completed: [${pageStr}/${totalPagesStr}]`);
196
+ this.log(LoggerLevel.debug, `[object.listWithIterator] Page ${page} details: items=${(_c = (_b = res.data) === null || _b === void 0 ? void 0 : _b.items) === null || _c === void 0 ? void 0 : _c.length}, hasMore=${hasMore}`);
197
+ this.log(LoggerLevel.trace, `[object.listWithIterator] Page ${page} data: ${JSON.stringify((_d = res.data) === null || _d === void 0 ? void 0 : _d.items)}`);
198
+ }
199
+ this.log(LoggerLevel.info, `[object.listWithIterator] Completed: total=${total}, fetched=${results.length}`);
200
+ return { total, items: results };
201
+ },
83
202
  metadata: {
84
203
  /**
85
204
  * 获取指定对象下指定字段的元数据
@@ -214,6 +333,45 @@ class Client {
214
333
  });
215
334
  }
216
335
  return { total, items: results };
336
+ },
337
+ /**
338
+ * 统计记录数量
339
+ * @description 统计指定对象中的记录总数,支持按条件统计
340
+ * @param params 请求参数 { object_name, data? }
341
+ * @returns 接口返回结果 { code, total, msg }
342
+ */
343
+ count: async (params) => {
344
+ var _a;
345
+ const { object_name, data } = params;
346
+ // 默认查询参数:最小化数据传输,只获取总数
347
+ const defaultData = {
348
+ offset: 0,
349
+ page_size: 1,
350
+ need_total_count: true,
351
+ use_page_token: true,
352
+ select: ['_id'],
353
+ query_deleted_record: false
354
+ };
355
+ // 合并用户传入的参数(用户参数优先)
356
+ const queryData = data ? { ...defaultData, ...data } : defaultData;
357
+ this.log(LoggerLevel.info, `[object.search.count] Counting records in: ${object_name}`);
358
+ this.log(LoggerLevel.debug, `[object.search.count] Query data: ${JSON.stringify(queryData)}`);
359
+ const res = await this.object.search.records({
360
+ object_name,
361
+ data: queryData
362
+ });
363
+ if (res.code !== '0') {
364
+ this.log(LoggerLevel.error, `[object.search.count] Error counting records: code=${res.code}, msg=${res.msg}`);
365
+ throw new Error(res.msg || `Count failed with code ${res.code}`);
366
+ }
367
+ const total = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.total) || 0;
368
+ this.log(LoggerLevel.info, `[object.search.count] Total records in ${object_name}: ${total}`);
369
+ // 返回格式:{ code, total, msg }
370
+ return {
371
+ code: res.code,
372
+ total: total,
373
+ msg: res.msg
374
+ };
217
375
  }
218
376
  },
219
377
  create: {
@@ -631,10 +789,10 @@ class Client {
631
789
  /**
632
790
  * 批量部门 ID 交换
633
791
  * @param params 请求参数
634
- * @returns 所有子请求的返回结果数组
792
+ * @returns 批量操作结果,包含成功和失败的详细信息
635
793
  */
636
794
  batchExchange: async (params) => {
637
- const { department_id_type, department_ids } = params;
795
+ const { department_id_type, department_ids, retryOptions } = params;
638
796
  // department_id_type 可选值:
639
797
  // - 'department_id' (如 "1758534140403815")
640
798
  // - 'external_department_id' (外部平台 department_id, 无固定格式)
@@ -646,37 +804,177 @@ class Client {
646
804
  }
647
805
  if (department_ids.length === 0) {
648
806
  this.log(LoggerLevel.warn, '[department.batchExchange] Empty department_ids array provided, returning empty result');
649
- return [];
807
+ return { success: [], failed: [], successCount: 0, failedCount: 0, total: 0 };
650
808
  }
651
809
  const url = '/api/integration/v2/feishu/getDepartments';
652
- const chunkSize = 100;
810
+ const chunkSize = 200; // 最大支持200个
653
811
  const chunks = [];
654
812
  for (let i = 0; i < department_ids.length; i += chunkSize) {
655
813
  chunks.push(department_ids.slice(i, i + chunkSize));
656
814
  }
657
815
  this.log(LoggerLevel.info, `[department.batchExchange] Chunking ${department_ids.length} department IDs into ${chunks.length} groups of ${chunkSize}`);
658
- const results = [];
816
+ const successResults = [];
817
+ const failedResults = [];
659
818
  for (const [index, chunk] of chunks.entries()) {
660
819
  this.log(LoggerLevel.info, `[department.batchExchange] Processing chunk ${index + 1}/${chunks.length}: ${chunk.length} IDs`);
661
- const res = await functionLimiter(async () => {
662
- await this.ensureTokenValid();
663
- const response = await this.axiosInstance.post(url, {
664
- department_id_type,
665
- department_ids: chunk
666
- }, {
667
- headers: { Authorization: `${this.accessToken}` }
820
+ try {
821
+ const res = await executeWithRetry(async () => {
822
+ return await functionLimiter(async () => {
823
+ await this.ensureTokenValid();
824
+ const response = await this.axiosInstance.post(url, {
825
+ department_id_type,
826
+ department_ids: chunk
827
+ }, {
828
+ headers: { Authorization: `${this.accessToken}` },
829
+ timeout: 30000 // 30秒超时
830
+ });
831
+ this.log(LoggerLevel.debug, `[department.batchExchange] Chunk ${index + 1} completed: code=${response.data.code}`);
832
+ this.log(LoggerLevel.trace, `[department.batchExchange] Chunk ${index + 1} response: ${JSON.stringify(response.data)}`);
833
+ if (response.data.code !== '0') {
834
+ this.log(LoggerLevel.error, `[department.batchExchange] Error exchanging departments: code=${response.data.code}, msg=${response.data.msg}`);
835
+ throw new Error(response.data.msg || `Exchange failed with code ${response.data.code}`);
836
+ }
837
+ return response.data.data || [];
838
+ });
839
+ }, retryOptions, `[department.batchExchange] Chunk ${index + 1}/${chunks.length}`, this.log.bind(this));
840
+ successResults.push(...res);
841
+ this.log(LoggerLevel.info, `[department.batchExchange] Chunk ${index + 1} succeeded: ${res.length} departments`);
842
+ }
843
+ catch (error) {
844
+ // 部分失败:记录失败的chunk
845
+ const errorMsg = error instanceof Error ? error.message : String(error);
846
+ this.log(LoggerLevel.error, `[department.batchExchange] Chunk ${index + 1} failed after retries: ${errorMsg}`);
847
+ chunk.forEach(id => {
848
+ failedResults.push({
849
+ id,
850
+ error: errorMsg
851
+ });
668
852
  });
669
- this.log(LoggerLevel.debug, `[department.batchExchange] Chunk ${index + 1} completed: code=${response.data.code}`);
670
- this.log(LoggerLevel.trace, `[department.batchExchange] Chunk ${index + 1} response: ${JSON.stringify(response.data)}`);
671
- if (response.data.code !== '0') {
672
- this.log(LoggerLevel.error, `[department.batchExchange] Error exchanging departments: code=${response.data.code}, msg=${response.data.msg}`);
673
- throw new Error(response.data.msg || `Exchange failed with code ${response.data.code}`);
674
- }
675
- return response.data.data || [];
853
+ }
854
+ }
855
+ const result = {
856
+ success: successResults,
857
+ failed: failedResults,
858
+ successCount: successResults.length,
859
+ failedCount: failedResults.length,
860
+ total: department_ids.length
861
+ };
862
+ this.log(LoggerLevel.info, `[department.batchExchange] Completed: total=${result.total}, success=${result.successCount}, failed=${result.failedCount}`);
863
+ return result;
864
+ }
865
+ };
866
+ /**
867
+ * 用户 ID 交换模块
868
+ */
869
+ this.user = {
870
+ /**
871
+ * 单个用户 ID 交换
872
+ * @param params 请求参数
873
+ * @returns 单个用户映射结果
874
+ */
875
+ exchange: async (params) => {
876
+ const { user_id_type, user_id, feishu_app_id } = params;
877
+ // user_id_type 可选值:
878
+ // - 'user_id' (如 "1758534140403815")
879
+ // - 'external_user_id' (外部平台 user_id, 无固定格式)
880
+ // - 'external_open_id' (以 'ou_' 开头的 open_id)
881
+ const url = '/api/integration/v2/feishu/getUsers';
882
+ this.log(LoggerLevel.info, `[user.exchange] Exchanging user ID: ${user_id}`);
883
+ const res = await functionLimiter(async () => {
884
+ await this.ensureTokenValid();
885
+ const response = await this.axiosInstance.post(url, {
886
+ user_id_type,
887
+ feishu_app_id,
888
+ user_ids: [user_id]
889
+ }, {
890
+ headers: { Authorization: `${this.accessToken}` }
676
891
  });
677
- results.push(...res);
892
+ this.log(LoggerLevel.debug, `[user.exchange] User ID exchanged: ${user_id}, code=${response.data.code}`);
893
+ this.log(LoggerLevel.trace, `[user.exchange] Response: ${JSON.stringify(response.data)}`);
894
+ if (response.data.code !== '0') {
895
+ this.log(LoggerLevel.error, `[user.exchange] Error exchanging user: code=${response.data.code}, msg=${response.data.msg}`);
896
+ throw new Error(response.data.msg || `Exchange failed with code ${response.data.code}`);
897
+ }
898
+ return response.data.data && response.data.data[0]; // 返回第一个元素
899
+ });
900
+ return res;
901
+ },
902
+ /**
903
+ * 批量用户 ID 交换
904
+ * @param params 请求参数
905
+ * @returns 批量操作结果,包含成功和失败的详细信息
906
+ */
907
+ batchExchange: async (params) => {
908
+ const { user_id_type, user_ids, feishu_app_id, retryOptions } = params;
909
+ // user_id_type 可选值:
910
+ // - 'user_id' (如 "1758534140403815")
911
+ // - 'external_user_id' (外部平台 user_id, 无固定格式)
912
+ // - 'external_open_id' (以 'ou_' 开头的 open_id)
913
+ // 参数校验
914
+ if (!user_ids || !Array.isArray(user_ids)) {
915
+ this.log(LoggerLevel.error, '[user.batchExchange] Invalid user_ids parameter: must be a non-empty array');
916
+ throw new Error('参数 user_ids 必须是一个数组');
917
+ }
918
+ if (user_ids.length === 0) {
919
+ this.log(LoggerLevel.warn, '[user.batchExchange] Empty user_ids array provided, returning empty result');
920
+ return { success: [], failed: [], successCount: 0, failedCount: 0, total: 0 };
921
+ }
922
+ const url = '/api/integration/v2/feishu/getUsers';
923
+ const chunkSize = 200; // 最大支持200个
924
+ const chunks = [];
925
+ for (let i = 0; i < user_ids.length; i += chunkSize) {
926
+ chunks.push(user_ids.slice(i, i + chunkSize));
927
+ }
928
+ this.log(LoggerLevel.info, `[user.batchExchange] Chunking ${user_ids.length} user IDs into ${chunks.length} groups of ${chunkSize}`);
929
+ const successResults = [];
930
+ const failedResults = [];
931
+ for (const [index, chunk] of chunks.entries()) {
932
+ this.log(LoggerLevel.info, `[user.batchExchange] Processing chunk ${index + 1}/${chunks.length}: ${chunk.length} IDs`);
933
+ try {
934
+ const res = await executeWithRetry(async () => {
935
+ return await functionLimiter(async () => {
936
+ await this.ensureTokenValid();
937
+ const response = await this.axiosInstance.post(url, {
938
+ user_id_type,
939
+ feishu_app_id,
940
+ user_ids: chunk
941
+ }, {
942
+ headers: { Authorization: `${this.accessToken}` },
943
+ timeout: 30000 // 30秒超时
944
+ });
945
+ this.log(LoggerLevel.debug, `[user.batchExchange] Chunk ${index + 1} completed: code=${response.data.code}`);
946
+ this.log(LoggerLevel.trace, `[user.batchExchange] Chunk ${index + 1} response: ${JSON.stringify(response.data)}`);
947
+ if (response.data.code !== '0') {
948
+ this.log(LoggerLevel.error, `[user.batchExchange] Error exchanging users: code=${response.data.code}, msg=${response.data.msg}`);
949
+ throw new Error(response.data.msg || `Exchange failed with code ${response.data.code}`);
950
+ }
951
+ return response.data.data || [];
952
+ });
953
+ }, retryOptions, `[user.batchExchange] Chunk ${index + 1}/${chunks.length}`, this.log.bind(this));
954
+ successResults.push(...res);
955
+ this.log(LoggerLevel.info, `[user.batchExchange] Chunk ${index + 1} succeeded: ${res.length} users`);
956
+ }
957
+ catch (error) {
958
+ // 部分失败:记录失败的chunk
959
+ const errorMsg = error instanceof Error ? error.message : String(error);
960
+ this.log(LoggerLevel.error, `[user.batchExchange] Chunk ${index + 1} failed after retries: ${errorMsg}`);
961
+ chunk.forEach(id => {
962
+ failedResults.push({
963
+ id,
964
+ error: errorMsg
965
+ });
966
+ });
967
+ }
678
968
  }
679
- return results;
969
+ const result = {
970
+ success: successResults,
971
+ failed: failedResults,
972
+ successCount: successResults.length,
973
+ failedCount: failedResults.length,
974
+ total: user_ids.length
975
+ };
976
+ this.log(LoggerLevel.info, `[user.batchExchange] Completed: total=${result.total}, success=${result.successCount}, failed=${result.failedCount}`);
977
+ return result;
680
978
  }
681
979
  };
682
980
  /**
@@ -0,0 +1,17 @@
1
+ import Bottleneck from 'bottleneck';
2
+ /**
3
+ * 默认 apaas 限流配置
4
+ */
5
+ export declare const apaasLimiterOptions: {
6
+ minTime: number;
7
+ reservoir: number;
8
+ reservoirRefreshAmount: number;
9
+ reservoirRefreshInterval: number;
10
+ };
11
+ /**
12
+ * 创建限流器
13
+ * @param fn 被限流函数
14
+ * @param options 自定义限流配置
15
+ * @returns 包装后的限流函数
16
+ */
17
+ export declare function functionLimiter<T>(fn: () => Promise<T>, options?: Partial<Bottleneck.ConstructorOptions>): Promise<T>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 日志等级枚举
3
+ */
4
+ export declare enum LoggerLevel {
5
+ fatal = 0,
6
+ error = 1,
7
+ warn = 2,
8
+ info = 3,
9
+ debug = 4,
10
+ trace = 5
11
+ }
@@ -1,4 +1,35 @@
1
1
  import { LoggerLevel } from './logger';
2
+ /**
3
+ * 批量操作结果
4
+ */
5
+ interface BatchResult<T> {
6
+ /** 成功的项 */
7
+ success: T[];
8
+ /** 失败的项 */
9
+ failed: Array<{
10
+ id: string;
11
+ error: string;
12
+ }>;
13
+ /** 成功数量 */
14
+ successCount: number;
15
+ /** 失败数量 */
16
+ failedCount: number;
17
+ /** 总数 */
18
+ total: number;
19
+ }
20
+ /**
21
+ * 重试配置
22
+ */
23
+ interface RetryOptions {
24
+ /** 最大重试次数 */
25
+ maxRetries?: number;
26
+ /** 初始延迟时间(ms) */
27
+ initialDelay?: number;
28
+ /** 最大延迟时间(ms) */
29
+ maxDelay?: number;
30
+ /** 延迟倍数 */
31
+ backoffMultiplier?: number;
32
+ }
2
33
  /**
3
34
  * Client 初始化配置
4
35
  */
@@ -71,17 +102,33 @@ declare class Client {
71
102
  object: {
72
103
  /**
73
104
  * 列出所有对象(数据表)
74
- * @param params 请求参数 { offset, filter?, limit }
75
- * @returns 接口返回结果
105
+ * @param params 请求参数 { offset?, filter?, limit? }
106
+ * @returns 接口返回结果,包含 has_more 字段表示是否还有更多数据
76
107
  */
77
- list: (params: {
78
- offset: number;
108
+ list: (params?: {
109
+ offset?: number;
79
110
  filter?: {
80
111
  type?: string;
81
112
  quickQuery?: string;
82
113
  };
83
- limit: number;
114
+ limit?: number;
84
115
  }) => Promise<any>;
116
+ /**
117
+ * 列出所有对象(数据表)- 支持自动分页查询
118
+ * @description 该方法会自动处理分页,直到没有更多数据为止
119
+ * @param params 请求参数 { filter?, limit? }
120
+ * @returns { total, items }
121
+ */
122
+ listWithIterator: (params?: {
123
+ filter?: {
124
+ type?: string;
125
+ quickQuery?: string;
126
+ };
127
+ limit?: number;
128
+ }) => Promise<{
129
+ total: number;
130
+ items: any[];
131
+ }>;
85
132
  metadata: {
86
133
  /**
87
134
  * 获取指定对象下指定字段的元数据
@@ -138,6 +185,16 @@ declare class Client {
138
185
  total: number;
139
186
  items: any[];
140
187
  }>;
188
+ /**
189
+ * 统计记录数量
190
+ * @description 统计指定对象中的记录总数,支持按条件统计
191
+ * @param params 请求参数 { object_name, data? }
192
+ * @returns 接口返回结果 { code, total, msg }
193
+ */
194
+ count: (params: {
195
+ object_name: string;
196
+ data?: any;
197
+ }) => Promise<any>;
141
198
  };
142
199
  create: {
143
200
  /**
@@ -295,12 +352,39 @@ declare class Client {
295
352
  /**
296
353
  * 批量部门 ID 交换
297
354
  * @param params 请求参数
298
- * @returns 所有子请求的返回结果数组
355
+ * @returns 批量操作结果,包含成功和失败的详细信息
299
356
  */
300
357
  batchExchange: (params: {
301
358
  department_id_type: "department_id" | "external_department_id" | "external_open_department_id";
302
359
  department_ids: string[];
303
- }) => Promise<any[]>;
360
+ retryOptions?: RetryOptions;
361
+ }) => Promise<BatchResult<any>>;
362
+ };
363
+ /**
364
+ * 用户 ID 交换模块
365
+ */
366
+ user: {
367
+ /**
368
+ * 单个用户 ID 交换
369
+ * @param params 请求参数
370
+ * @returns 单个用户映射结果
371
+ */
372
+ exchange: (params: {
373
+ user_id_type: "user_id" | "external_user_id" | "external_open_id";
374
+ user_id: string;
375
+ feishu_app_id: string;
376
+ }) => Promise<any>;
377
+ /**
378
+ * 批量用户 ID 交换
379
+ * @param params 请求参数
380
+ * @returns 批量操作结果,包含成功和失败的详细信息
381
+ */
382
+ batchExchange: (params: {
383
+ user_id_type: "user_id" | "external_user_id" | "external_open_id";
384
+ user_ids: string[];
385
+ feishu_app_id: string;
386
+ retryOptions?: RetryOptions;
387
+ }) => Promise<BatchResult<any>>;
304
388
  };
305
389
  /**
306
390
  * 云函数模块
@@ -549,4 +633,4 @@ declare class Client {
549
633
  export declare const apaas: {
550
634
  Client: typeof Client;
551
635
  };
552
- export {};
636
+ export type { BatchResult, RetryOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apaas-oapi-client",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "main": "dist/index.js",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",