apaas-oapi-client 0.1.33 → 0.1.35
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/README.md +1 -0
- package/UserManual.md +43 -0
- package/dist/index.js +329 -41
- package/dist/src/index.d.ts +41 -3
- package/package.json +1 -1
- package/src/index.ts +365 -42
package/README.md
CHANGED
package/UserManual.md
CHANGED
|
@@ -332,6 +332,49 @@ const res = await client.object.metadata.fields({
|
|
|
332
332
|
console.log(res);
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
+
### **导出数据对象文档为 Markdown**
|
|
336
|
+
|
|
337
|
+
将数据对象的元数据导出为详细的 Markdown 文档,包含完整的字段信息、类型、配置等。
|
|
338
|
+
|
|
339
|
+
```JavaScript
|
|
340
|
+
const fs = require('fs');
|
|
341
|
+
|
|
342
|
+
// 方式一:导出所有对象(推荐,无需参数)
|
|
343
|
+
const markdown = await client.object.metadata.export2markdown();
|
|
344
|
+
fs.writeFileSync('all_objects.md', markdown, 'utf-8');
|
|
345
|
+
|
|
346
|
+
// 方式二:只导出指定的对象
|
|
347
|
+
const markdown2 = await client.object.metadata.export2markdown({
|
|
348
|
+
object_names: ['object_store', 'object_order', '_user']
|
|
349
|
+
});
|
|
350
|
+
fs.writeFileSync('specific_objects.md', markdown2, 'utf-8');
|
|
351
|
+
|
|
352
|
+
// 方式三:结合 listWithIterator 灵活筛选
|
|
353
|
+
const allObjects = await client.object.listWithIterator();
|
|
354
|
+
const customObjects = allObjects.items
|
|
355
|
+
.filter(obj => !obj.apiName.startsWith('_')) // 只要自定义对象
|
|
356
|
+
.map(obj => obj.apiName);
|
|
357
|
+
|
|
358
|
+
const markdown3 = await client.object.metadata.export2markdown({
|
|
359
|
+
object_names: customObjects
|
|
360
|
+
});
|
|
361
|
+
fs.writeFileSync('custom_objects.md', markdown3, 'utf-8');
|
|
362
|
+
|
|
363
|
+
console.log('✅ 文档导出成功!');
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**生成的 Markdown 文档包含:**
|
|
367
|
+
- 📋 自动生成的目录(带锚点链接)
|
|
368
|
+
- 📊 每个对象的详细信息(中英文名称、创建时间、字段数量)
|
|
369
|
+
- 📝 字段列表(中文名称、API名称、类型、必填、唯一性)
|
|
370
|
+
- ⚙️ 字段配置详情:
|
|
371
|
+
- 选项字段:展示所有选项值
|
|
372
|
+
- 公式字段:显示公式表达式和返回类型
|
|
373
|
+
- 引用字段:显示引用来源和字段
|
|
374
|
+
- lookup 字段:显示关联对象
|
|
375
|
+
- 其他配置:最大长度、小数位、显示样式等
|
|
376
|
+
- 🎯 字段智能排序(系统字段、业务字段、公式字段分类展示)
|
|
377
|
+
|
|
335
378
|
***
|
|
336
379
|
|
|
337
380
|
|
package/dist/index.js
CHANGED
|
@@ -120,10 +120,10 @@ class Client {
|
|
|
120
120
|
/**
|
|
121
121
|
* 列出所有对象(数据表)
|
|
122
122
|
* @param params 请求参数 { offset?, filter?, limit? }
|
|
123
|
-
* @returns
|
|
123
|
+
* @returns 接口返回结果 { code, items, total, msg, has_more }
|
|
124
124
|
*/
|
|
125
125
|
list: async (params) => {
|
|
126
|
-
var _a, _b;
|
|
126
|
+
var _a, _b, _c, _d, _e, _f;
|
|
127
127
|
const offset = (_a = params === null || params === void 0 ? void 0 : params.offset) !== null && _a !== void 0 ? _a : 0;
|
|
128
128
|
const limit = (_b = params === null || params === void 0 ? void 0 : params.limit) !== null && _b !== void 0 ? _b : 50;
|
|
129
129
|
const filter = params === null || params === void 0 ? void 0 : params.filter;
|
|
@@ -139,23 +139,28 @@ class Client {
|
|
|
139
139
|
});
|
|
140
140
|
this.log(LoggerLevel.debug, `[object.list] Objects list fetched successfully: code=${res.data.code}`);
|
|
141
141
|
this.log(LoggerLevel.trace, `[object.list] Response: ${JSON.stringify(res.data)}`);
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
142
|
+
// 扁平化返回结构并添加 has_more 字段
|
|
143
|
+
const items = ((_d = (_c = res.data) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.items) || [];
|
|
144
|
+
const total = ((_f = (_e = res.data) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.total) || 0;
|
|
145
|
+
const currentEnd = offset + limit;
|
|
146
|
+
const has_more = currentEnd < total;
|
|
147
|
+
this.log(LoggerLevel.debug, `[object.list] has_more=${has_more}, total=${total}, currentEnd=${currentEnd}`);
|
|
148
|
+
return {
|
|
149
|
+
code: res.data.code,
|
|
150
|
+
items,
|
|
151
|
+
total,
|
|
152
|
+
msg: res.data.msg,
|
|
153
|
+
has_more
|
|
154
|
+
};
|
|
150
155
|
},
|
|
151
156
|
/**
|
|
152
157
|
* 列出所有对象(数据表)- 支持自动分页查询
|
|
153
158
|
* @description 该方法会自动处理分页,直到没有更多数据为止
|
|
154
159
|
* @param params 请求参数 { filter?, limit? }
|
|
155
|
-
* @returns {
|
|
160
|
+
* @returns { code, msg, items, total, failed? } - code 表示失败的分页数量
|
|
156
161
|
*/
|
|
157
162
|
listWithIterator: async (params) => {
|
|
158
|
-
var _a, _b
|
|
163
|
+
var _a, _b;
|
|
159
164
|
const filter = params === null || params === void 0 ? void 0 : params.filter;
|
|
160
165
|
const limit = (_a = params === null || params === void 0 ? void 0 : params.limit) !== null && _a !== void 0 ? _a : 50;
|
|
161
166
|
let results = [];
|
|
@@ -164,40 +169,87 @@ class Client {
|
|
|
164
169
|
let hasMore = true;
|
|
165
170
|
let page = 0;
|
|
166
171
|
let totalPages = 0;
|
|
172
|
+
let failed = [];
|
|
173
|
+
let allSuccess = true;
|
|
167
174
|
this.log(LoggerLevel.info, `[object.listWithIterator] Starting paginated query with limit=${limit}`);
|
|
168
175
|
while (hasMore) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
176
|
+
try {
|
|
177
|
+
const res = await this.object.list({
|
|
178
|
+
offset,
|
|
179
|
+
limit,
|
|
180
|
+
filter
|
|
181
|
+
});
|
|
182
|
+
if (res.code !== '0') {
|
|
183
|
+
this.log(LoggerLevel.error, `[object.listWithIterator] Error querying objects: code=${res.code}, msg=${res.msg}, offset=${offset}`);
|
|
184
|
+
allSuccess = false;
|
|
185
|
+
failed.push({
|
|
186
|
+
offset,
|
|
187
|
+
limit,
|
|
188
|
+
code: res.code,
|
|
189
|
+
msg: res.msg || `Query failed with code ${res.code}`
|
|
190
|
+
});
|
|
191
|
+
// 继续尝试下一页,而不是直接退出
|
|
192
|
+
offset += limit;
|
|
193
|
+
page += 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
page += 1;
|
|
197
|
+
if (Array.isArray(res.items)) {
|
|
198
|
+
results = results.concat(res.items);
|
|
199
|
+
}
|
|
200
|
+
if (res.total !== undefined && res.total !== null) {
|
|
201
|
+
total = res.total;
|
|
202
|
+
}
|
|
203
|
+
if (page === 1) {
|
|
204
|
+
totalPages = Math.ceil(total / limit);
|
|
205
|
+
this.log(LoggerLevel.info, `[object.listWithIterator] Total objects: ${total}, pages: ${totalPages}`);
|
|
206
|
+
}
|
|
207
|
+
// 判断是否还有更多数据
|
|
208
|
+
hasMore = res.has_more === true;
|
|
209
|
+
offset += limit;
|
|
210
|
+
const padLength = totalPages.toString().length;
|
|
211
|
+
const pageStr = page.toString().padStart(padLength, '0');
|
|
212
|
+
const totalPagesStr = totalPages.toString().padStart(padLength, '0');
|
|
213
|
+
this.log(LoggerLevel.info, `[object.listWithIterator] Page completed: [${pageStr}/${totalPagesStr}]`);
|
|
214
|
+
this.log(LoggerLevel.debug, `[object.listWithIterator] Page ${page} details: items=${(_b = res.items) === null || _b === void 0 ? void 0 : _b.length}, hasMore=${hasMore}`);
|
|
215
|
+
this.log(LoggerLevel.trace, `[object.listWithIterator] Page ${page} data: ${JSON.stringify(res.items)}`);
|
|
184
216
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
217
|
+
catch (error) {
|
|
218
|
+
this.log(LoggerLevel.error, `[object.listWithIterator] Exception occurred: ${error}, offset=${offset}`);
|
|
219
|
+
allSuccess = false;
|
|
220
|
+
failed.push({
|
|
221
|
+
offset,
|
|
222
|
+
limit,
|
|
223
|
+
code: '1',
|
|
224
|
+
msg: error instanceof Error ? error.message : String(error)
|
|
225
|
+
});
|
|
226
|
+
// 继续尝试下一页
|
|
227
|
+
offset += limit;
|
|
228
|
+
page += 1;
|
|
229
|
+
// 如果没有获取到 total,可能需要退出循环
|
|
230
|
+
if (total === 0) {
|
|
231
|
+
hasMore = false;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
hasMore = offset < total;
|
|
235
|
+
}
|
|
188
236
|
}
|
|
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
237
|
}
|
|
199
|
-
|
|
200
|
-
|
|
238
|
+
const resultCode = failed.length.toString();
|
|
239
|
+
const resultMsg = failed.length === 0
|
|
240
|
+
? 'Success'
|
|
241
|
+
: `Completed with ${failed.length} failed page(s)`;
|
|
242
|
+
this.log(LoggerLevel.info, `[object.listWithIterator] Completed: code=${resultCode}, total=${total}, fetched=${results.length}, failed=${failed.length}`);
|
|
243
|
+
const result = {
|
|
244
|
+
code: resultCode,
|
|
245
|
+
msg: resultMsg,
|
|
246
|
+
items: results,
|
|
247
|
+
total
|
|
248
|
+
};
|
|
249
|
+
if (failed.length > 0) {
|
|
250
|
+
result.failed = failed;
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
201
253
|
},
|
|
202
254
|
metadata: {
|
|
203
255
|
/**
|
|
@@ -235,6 +287,242 @@ class Client {
|
|
|
235
287
|
this.log(LoggerLevel.debug, `[object.metadata.fields] All fields metadata fetched: ${object_name}, code=${res.data.code}`);
|
|
236
288
|
this.log(LoggerLevel.trace, `[object.metadata.fields] Response: ${JSON.stringify(res.data)}`);
|
|
237
289
|
return res.data;
|
|
290
|
+
},
|
|
291
|
+
/**
|
|
292
|
+
* 导出数据对象元数据为 Markdown 文档
|
|
293
|
+
* @description 将数据对象的字段信息导出为详细的 Markdown 文档,包含字段类型、配置、选项等完整信息
|
|
294
|
+
* @param options 导出配置
|
|
295
|
+
* @param options.object_names 可选,要导出的对象名称数组。如果不传,则导出所有对象
|
|
296
|
+
* @returns Markdown 文档字符串
|
|
297
|
+
* @example
|
|
298
|
+
* ```typescript
|
|
299
|
+
* // 导出所有对象
|
|
300
|
+
* const markdown = await client.object.metadata.export2markdown();
|
|
301
|
+
*
|
|
302
|
+
* // 只导出指定对象
|
|
303
|
+
* const markdown = await client.object.metadata.export2markdown({
|
|
304
|
+
* object_names: ['object_store', 'object_order', '_user']
|
|
305
|
+
* });
|
|
306
|
+
*
|
|
307
|
+
* // 结合 listWithIterator 使用
|
|
308
|
+
* const allObjects = await client.object.listWithIterator();
|
|
309
|
+
* const objectNames = allObjects.items.map(obj => obj.apiName);
|
|
310
|
+
* const markdown = await client.object.metadata.export2markdown({
|
|
311
|
+
* object_names: objectNames
|
|
312
|
+
* });
|
|
313
|
+
*
|
|
314
|
+
* // 保存到文件
|
|
315
|
+
* fs.writeFileSync('objects_doc.md', markdown, 'utf-8');
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
export2markdown: async (options) => {
|
|
319
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
320
|
+
const objectNames = options === null || options === void 0 ? void 0 : options.object_names;
|
|
321
|
+
this.log(LoggerLevel.info, `[object.metadata.export2markdown] Starting markdown export${objectNames && objectNames.length > 0 ? ` for ${objectNames.length} objects` : ' for all objects'}`);
|
|
322
|
+
let items = [];
|
|
323
|
+
if (objectNames && objectNames.length > 0) {
|
|
324
|
+
// 如果指定了对象名称,只获取这些对象
|
|
325
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetching specified objects: ${objectNames.join(', ')}`);
|
|
326
|
+
// 先获取所有对象列表
|
|
327
|
+
const allObjects = await this.object.listWithIterator();
|
|
328
|
+
// 过滤出指定的对象
|
|
329
|
+
items = allObjects.items.filter((obj) => objectNames.includes(obj.apiName));
|
|
330
|
+
// 检查是否有不存在的对象
|
|
331
|
+
if (items.length < objectNames.length) {
|
|
332
|
+
const foundNames = items.map((obj) => obj.apiName);
|
|
333
|
+
const notFound = objectNames.filter(name => !foundNames.includes(name));
|
|
334
|
+
this.log(LoggerLevel.warn, `[object.metadata.export2markdown] Objects not found: ${notFound.join(', ')}`);
|
|
335
|
+
}
|
|
336
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Found ${items.length}/${objectNames.length} matching objects`);
|
|
337
|
+
if (items.length === 0) {
|
|
338
|
+
this.log(LoggerLevel.warn, `[object.metadata.export2markdown] No matching objects found`);
|
|
339
|
+
return '# 数据对象字段文档\n\n> 未找到匹配的对象\n';
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
// 获取所有对象
|
|
344
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetching all objects`);
|
|
345
|
+
const allObjects = await this.object.listWithIterator();
|
|
346
|
+
items = allObjects.items || [];
|
|
347
|
+
}
|
|
348
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetched ${items.length} objects`);
|
|
349
|
+
// 生成 Markdown 文档
|
|
350
|
+
let markdown = '# 数据对象字段文档\n\n';
|
|
351
|
+
markdown += `> 生成时间: ${dayjs().format('YYYY-MM-DD HH:mm:ss')}\n\n`;
|
|
352
|
+
markdown += `> 对象总数: ${items.length}\n\n`;
|
|
353
|
+
markdown += '---\n\n';
|
|
354
|
+
// 目录
|
|
355
|
+
markdown += '## 目录\n\n';
|
|
356
|
+
items.forEach((obj, index) => {
|
|
357
|
+
var _a, _b;
|
|
358
|
+
const chineseName = ((_a = obj.label) === null || _a === void 0 ? void 0 : _a.zh_CN) || ((_b = obj.label) === null || _b === void 0 ? void 0 : _b.en_US) || obj.apiName;
|
|
359
|
+
markdown += `${index + 1}. [${chineseName} (${obj.apiName})](#${obj.apiName.replace(/_/g, '')})\n`;
|
|
360
|
+
});
|
|
361
|
+
markdown += '\n---\n\n';
|
|
362
|
+
// 遍历每个对象
|
|
363
|
+
for (const obj of items) {
|
|
364
|
+
const chineseName = ((_a = obj.label) === null || _a === void 0 ? void 0 : _a.zh_CN) || ((_b = obj.label) === null || _b === void 0 ? void 0 : _b.en_US) || obj.apiName;
|
|
365
|
+
const englishName = ((_c = obj.label) === null || _c === void 0 ? void 0 : _c.en_US) || '';
|
|
366
|
+
markdown += `## ${chineseName} \`${obj.apiName}\`\n\n`;
|
|
367
|
+
if (englishName && englishName !== chineseName) {
|
|
368
|
+
markdown += `**英文名称:** ${englishName}\n\n`;
|
|
369
|
+
}
|
|
370
|
+
markdown += `**创建时间:** ${dayjs(obj.createdAt).format('YYYY-MM-DD HH:mm:ss')}\n\n`;
|
|
371
|
+
markdown += `**字段数量:** ${((_d = obj.fields) === null || _d === void 0 ? void 0 : _d.length) || 0}\n\n`;
|
|
372
|
+
// 字段表格
|
|
373
|
+
if (obj.fields && obj.fields.length > 0) {
|
|
374
|
+
// 对字段进行分类和排序
|
|
375
|
+
const systemFieldOrder = ['_name', '_createdBy', '_createdAt', '_updatedBy', '_updatedAt'];
|
|
376
|
+
const specialFieldTypes = ['formula', 'referenceField'];
|
|
377
|
+
let idField = null;
|
|
378
|
+
const normalFields = [];
|
|
379
|
+
const specialFields = [];
|
|
380
|
+
const systemFields = [];
|
|
381
|
+
obj.fields.forEach((field) => {
|
|
382
|
+
var _a;
|
|
383
|
+
if (field.apiName === '_id') {
|
|
384
|
+
idField = field;
|
|
385
|
+
}
|
|
386
|
+
else if (systemFieldOrder.includes(field.apiName)) {
|
|
387
|
+
systemFields.push(field);
|
|
388
|
+
}
|
|
389
|
+
else if (specialFieldTypes.includes((_a = field.type) === null || _a === void 0 ? void 0 : _a.name)) {
|
|
390
|
+
specialFields.push(field);
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
normalFields.push(field);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
// 对系统字段按指定顺序排序
|
|
397
|
+
systemFields.sort((a, b) => {
|
|
398
|
+
return systemFieldOrder.indexOf(a.apiName) - systemFieldOrder.indexOf(b.apiName);
|
|
399
|
+
});
|
|
400
|
+
// 组合所有字段:_id + 正常字段 + 特殊字段 + 系统字段
|
|
401
|
+
const sortedFields = [];
|
|
402
|
+
if (idField)
|
|
403
|
+
sortedFields.push(idField);
|
|
404
|
+
sortedFields.push(...normalFields);
|
|
405
|
+
sortedFields.push(...specialFields);
|
|
406
|
+
sortedFields.push(...systemFields);
|
|
407
|
+
markdown += '### 字段列表\n\n';
|
|
408
|
+
markdown += '| 中文名称 | API名称 | 类型 | 必填 | 唯一 | 其他设置 |\n';
|
|
409
|
+
markdown += '|---------|---------|------|------|------|----------|\n';
|
|
410
|
+
for (const field of sortedFields) {
|
|
411
|
+
// 转义 Markdown 表格中的特殊字符
|
|
412
|
+
const escapeMarkdown = (text) => {
|
|
413
|
+
return text.replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
414
|
+
};
|
|
415
|
+
const label = escapeMarkdown(((_e = field.label) === null || _e === void 0 ? void 0 : _e.zh_CN) || ((_f = field.label) === null || _f === void 0 ? void 0 : _f.en_US) || '-');
|
|
416
|
+
const apiName = field.apiName || '-';
|
|
417
|
+
const typeName = ((_g = field.type) === null || _g === void 0 ? void 0 : _g.name) || '-';
|
|
418
|
+
const required = ((_j = (_h = field.type) === null || _h === void 0 ? void 0 : _h.settings) === null || _j === void 0 ? void 0 : _j.required) ? '✓' : '';
|
|
419
|
+
const unique = ((_l = (_k = field.type) === null || _k === void 0 ? void 0 : _k.settings) === null || _l === void 0 ? void 0 : _l.unique) ? '✓' : '';
|
|
420
|
+
// 构建其他设置信息
|
|
421
|
+
const otherSettings = [];
|
|
422
|
+
const settings = ((_m = field.type) === null || _m === void 0 ? void 0 : _m.settings) || {};
|
|
423
|
+
// lookup 类型:标注关联的对象(外键)
|
|
424
|
+
if (((_o = field.type) === null || _o === void 0 ? void 0 : _o.name) === 'lookup' && settings.objectAPIName) {
|
|
425
|
+
otherSettings.push(`🔗 关联对象: \`${settings.objectAPIName}\``);
|
|
426
|
+
}
|
|
427
|
+
// referenceField 类型:引用字段
|
|
428
|
+
if (((_p = field.type) === null || _p === void 0 ? void 0 : _p.name) === 'referenceField') {
|
|
429
|
+
otherSettings.push(`⚙️ 系统自动维护,不需要写/更新`);
|
|
430
|
+
if (settings.guideFieldAPIName) {
|
|
431
|
+
otherSettings.push(`📎 引用自: \`${settings.guideFieldAPIName}\``);
|
|
432
|
+
}
|
|
433
|
+
if (settings.fieldAPIName) {
|
|
434
|
+
otherSettings.push(`📋 引用字段: \`${settings.fieldAPIName}\``);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// formula 类型:系统自动维护
|
|
438
|
+
if (((_q = field.type) === null || _q === void 0 ? void 0 : _q.name) === 'formula') {
|
|
439
|
+
otherSettings.push(`⚙️ 系统自动维护,不需要写/更新`);
|
|
440
|
+
if (settings.formula && Array.isArray(settings.formula)) {
|
|
441
|
+
// 优先显示中文公式,否则显示第一个
|
|
442
|
+
const zhFormula = settings.formula.find((f) => f.language_code === 2052);
|
|
443
|
+
const formulaText = (zhFormula === null || zhFormula === void 0 ? void 0 : zhFormula.text) || ((_r = settings.formula[0]) === null || _r === void 0 ? void 0 : _r.text);
|
|
444
|
+
if (formulaText) {
|
|
445
|
+
// 转义公式中的特殊字符
|
|
446
|
+
const escapedFormula = formulaText.replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
447
|
+
otherSettings.push(`公式: ${escapedFormula}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (settings.returnType) {
|
|
451
|
+
otherSettings.push(`返回类型: ${settings.returnType}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
// 根据不同类型展示不同的设置
|
|
455
|
+
if (settings.maxLength) {
|
|
456
|
+
otherSettings.push(`最大长度:${settings.maxLength}`);
|
|
457
|
+
}
|
|
458
|
+
if (settings.decimalPlacesNumber !== undefined) {
|
|
459
|
+
otherSettings.push(`小数位:${settings.decimalPlacesNumber}`);
|
|
460
|
+
}
|
|
461
|
+
if (settings.displayAsPercentage) {
|
|
462
|
+
otherSettings.push('百分比显示');
|
|
463
|
+
}
|
|
464
|
+
if (settings.multiline) {
|
|
465
|
+
otherSettings.push('多行');
|
|
466
|
+
}
|
|
467
|
+
if (settings.multiple) {
|
|
468
|
+
otherSettings.push('多选');
|
|
469
|
+
}
|
|
470
|
+
if (settings.hierarchy) {
|
|
471
|
+
otherSettings.push('层级');
|
|
472
|
+
}
|
|
473
|
+
if (settings.displayStyle && ((_s = field.type) === null || _s === void 0 ? void 0 : _s.name) !== 'lookup') {
|
|
474
|
+
otherSettings.push(`显示样式:${settings.displayStyle}`);
|
|
475
|
+
}
|
|
476
|
+
if (settings.referenceObjectApiName) {
|
|
477
|
+
otherSettings.push(`关联:${settings.referenceObjectApiName}`);
|
|
478
|
+
}
|
|
479
|
+
if (settings.rollUpType) {
|
|
480
|
+
otherSettings.push(`汇总:${settings.rollUpType}`);
|
|
481
|
+
}
|
|
482
|
+
// 如果是 option 类型,获取选项列表
|
|
483
|
+
if (((_t = field.type) === null || _t === void 0 ? void 0 : _t.name) === 'option') {
|
|
484
|
+
// 优先使用已有的 optionList,避免额外 API 请求
|
|
485
|
+
const options = settings.optionList;
|
|
486
|
+
if (options && options.length > 0) {
|
|
487
|
+
const optionTexts = options.map((opt) => {
|
|
488
|
+
var _a, _b;
|
|
489
|
+
const zhLabel = ((_b = (_a = opt.label) === null || _a === void 0 ? void 0 : _a.find((l) => l.language_code === 2052)) === null || _b === void 0 ? void 0 : _b.text) || '-';
|
|
490
|
+
return `${zhLabel}(\`${opt.apiName}\`)`;
|
|
491
|
+
});
|
|
492
|
+
otherSettings.push(`选项: ${optionTexts.join(', ')}`);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
// 如果没有 optionList,再尝试单独请求(但这可能很慢)
|
|
496
|
+
try {
|
|
497
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetching option details for ${obj.apiName}.${field.apiName}`);
|
|
498
|
+
const optionsResult = await this.object.metadata.field({
|
|
499
|
+
object_name: obj.apiName,
|
|
500
|
+
field_name: field.apiName
|
|
501
|
+
});
|
|
502
|
+
const fetchedOptions = (_w = (_v = (_u = optionsResult === null || optionsResult === void 0 ? void 0 : optionsResult.data) === null || _u === void 0 ? void 0 : _u.type) === null || _v === void 0 ? void 0 : _v.settings) === null || _w === void 0 ? void 0 : _w.optionList;
|
|
503
|
+
if (fetchedOptions && fetchedOptions.length > 0) {
|
|
504
|
+
const optionTexts = fetchedOptions.map((opt) => {
|
|
505
|
+
var _a, _b;
|
|
506
|
+
const zhLabel = ((_b = (_a = opt.label) === null || _a === void 0 ? void 0 : _a.find((l) => l.language_code === 2052)) === null || _b === void 0 ? void 0 : _b.text) || '-';
|
|
507
|
+
return `${zhLabel}(\`${opt.apiName}\`)`;
|
|
508
|
+
});
|
|
509
|
+
otherSettings.push(`选项: ${optionTexts.join(', ')}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
this.log(LoggerLevel.warn, `[object.metadata.export2markdown] Failed to fetch option field details for ${obj.apiName}.${field.apiName}:`, error);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const otherSettingsStr = otherSettings.length > 0 ? otherSettings.join('<br>') : '-';
|
|
518
|
+
markdown += `| ${label} | \`${apiName}\` | ${typeName} | ${required} | ${unique} | ${otherSettingsStr} |\n`;
|
|
519
|
+
}
|
|
520
|
+
markdown += '\n';
|
|
521
|
+
}
|
|
522
|
+
markdown += '---\n\n';
|
|
523
|
+
}
|
|
524
|
+
this.log(LoggerLevel.info, `[object.metadata.export2markdown] Markdown export completed`);
|
|
525
|
+
return markdown;
|
|
238
526
|
}
|
|
239
527
|
},
|
|
240
528
|
search: {
|
package/dist/src/index.d.ts
CHANGED
|
@@ -103,7 +103,7 @@ declare class Client {
|
|
|
103
103
|
/**
|
|
104
104
|
* 列出所有对象(数据表)
|
|
105
105
|
* @param params 请求参数 { offset?, filter?, limit? }
|
|
106
|
-
* @returns
|
|
106
|
+
* @returns 接口返回结果 { code, items, total, msg, has_more }
|
|
107
107
|
*/
|
|
108
108
|
list: (params?: {
|
|
109
109
|
offset?: number;
|
|
@@ -117,7 +117,7 @@ declare class Client {
|
|
|
117
117
|
* 列出所有对象(数据表)- 支持自动分页查询
|
|
118
118
|
* @description 该方法会自动处理分页,直到没有更多数据为止
|
|
119
119
|
* @param params 请求参数 { filter?, limit? }
|
|
120
|
-
* @returns {
|
|
120
|
+
* @returns { code, msg, items, total, failed? } - code 表示失败的分页数量
|
|
121
121
|
*/
|
|
122
122
|
listWithIterator: (params?: {
|
|
123
123
|
filter?: {
|
|
@@ -126,8 +126,16 @@ declare class Client {
|
|
|
126
126
|
};
|
|
127
127
|
limit?: number;
|
|
128
128
|
}) => Promise<{
|
|
129
|
-
|
|
129
|
+
code: string;
|
|
130
|
+
msg: string;
|
|
130
131
|
items: any[];
|
|
132
|
+
total: number;
|
|
133
|
+
failed?: Array<{
|
|
134
|
+
offset: number;
|
|
135
|
+
limit: number;
|
|
136
|
+
code: string;
|
|
137
|
+
msg: string;
|
|
138
|
+
}>;
|
|
131
139
|
}>;
|
|
132
140
|
metadata: {
|
|
133
141
|
/**
|
|
@@ -149,6 +157,36 @@ declare class Client {
|
|
|
149
157
|
fields: (params: {
|
|
150
158
|
object_name: string;
|
|
151
159
|
}) => Promise<any>;
|
|
160
|
+
/**
|
|
161
|
+
* 导出数据对象元数据为 Markdown 文档
|
|
162
|
+
* @description 将数据对象的字段信息导出为详细的 Markdown 文档,包含字段类型、配置、选项等完整信息
|
|
163
|
+
* @param options 导出配置
|
|
164
|
+
* @param options.object_names 可选,要导出的对象名称数组。如果不传,则导出所有对象
|
|
165
|
+
* @returns Markdown 文档字符串
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* // 导出所有对象
|
|
169
|
+
* const markdown = await client.object.metadata.export2markdown();
|
|
170
|
+
*
|
|
171
|
+
* // 只导出指定对象
|
|
172
|
+
* const markdown = await client.object.metadata.export2markdown({
|
|
173
|
+
* object_names: ['object_store', 'object_order', '_user']
|
|
174
|
+
* });
|
|
175
|
+
*
|
|
176
|
+
* // 结合 listWithIterator 使用
|
|
177
|
+
* const allObjects = await client.object.listWithIterator();
|
|
178
|
+
* const objectNames = allObjects.items.map(obj => obj.apiName);
|
|
179
|
+
* const markdown = await client.object.metadata.export2markdown({
|
|
180
|
+
* object_names: objectNames
|
|
181
|
+
* });
|
|
182
|
+
*
|
|
183
|
+
* // 保存到文件
|
|
184
|
+
* fs.writeFileSync('objects_doc.md', markdown, 'utf-8');
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export2markdown: (options?: {
|
|
188
|
+
object_names?: string[];
|
|
189
|
+
}) => Promise<string>;
|
|
152
190
|
};
|
|
153
191
|
search: {
|
|
154
192
|
/**
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -291,7 +291,7 @@ class Client {
|
|
|
291
291
|
/**
|
|
292
292
|
* 列出所有对象(数据表)
|
|
293
293
|
* @param params 请求参数 { offset?, filter?, limit? }
|
|
294
|
-
* @returns
|
|
294
|
+
* @returns 接口返回结果 { code, items, total, msg, has_more }
|
|
295
295
|
*/
|
|
296
296
|
list: async (params?: { offset?: number; filter?: { type?: string; quickQuery?: string }; limit?: number }): Promise<any> => {
|
|
297
297
|
const offset = params?.offset ?? 0;
|
|
@@ -314,24 +314,36 @@ class Client {
|
|
|
314
314
|
this.log(LoggerLevel.debug, `[object.list] Objects list fetched successfully: code=${res.data.code}`);
|
|
315
315
|
this.log(LoggerLevel.trace, `[object.list] Response: ${JSON.stringify(res.data)}`);
|
|
316
316
|
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
this.log(LoggerLevel.debug, `[object.list] has_more=${res.data.has_more}, total=${total}, currentEnd=${currentEnd}`);
|
|
323
|
-
}
|
|
317
|
+
// 扁平化返回结构并添加 has_more 字段
|
|
318
|
+
const items = res.data?.data?.items || [];
|
|
319
|
+
const total = res.data?.data?.total || 0;
|
|
320
|
+
const currentEnd = offset + limit;
|
|
321
|
+
const has_more = currentEnd < total;
|
|
324
322
|
|
|
325
|
-
|
|
323
|
+
this.log(LoggerLevel.debug, `[object.list] has_more=${has_more}, total=${total}, currentEnd=${currentEnd}`);
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
code: res.data.code,
|
|
327
|
+
items,
|
|
328
|
+
total,
|
|
329
|
+
msg: res.data.msg,
|
|
330
|
+
has_more
|
|
331
|
+
};
|
|
326
332
|
},
|
|
327
333
|
|
|
328
334
|
/**
|
|
329
335
|
* 列出所有对象(数据表)- 支持自动分页查询
|
|
330
336
|
* @description 该方法会自动处理分页,直到没有更多数据为止
|
|
331
337
|
* @param params 请求参数 { filter?, limit? }
|
|
332
|
-
* @returns {
|
|
338
|
+
* @returns { code, msg, items, total, failed? } - code 表示失败的分页数量
|
|
333
339
|
*/
|
|
334
|
-
listWithIterator: async (params?: { filter?: { type?: string; quickQuery?: string }; limit?: number }): Promise<{
|
|
340
|
+
listWithIterator: async (params?: { filter?: { type?: string; quickQuery?: string }; limit?: number }): Promise<{
|
|
341
|
+
code: string;
|
|
342
|
+
msg: string;
|
|
343
|
+
items: any[];
|
|
344
|
+
total: number;
|
|
345
|
+
failed?: Array<{ offset: number; limit: number; code: string; msg: string }>
|
|
346
|
+
}> => {
|
|
335
347
|
const filter = params?.filter;
|
|
336
348
|
const limit = params?.limit ?? 50;
|
|
337
349
|
|
|
@@ -341,51 +353,101 @@ class Client {
|
|
|
341
353
|
let hasMore = true;
|
|
342
354
|
let page = 0;
|
|
343
355
|
let totalPages = 0;
|
|
356
|
+
let failed: Array<{ offset: number; limit: number; code: string; msg: string }> = [];
|
|
357
|
+
let allSuccess = true;
|
|
344
358
|
|
|
345
359
|
this.log(LoggerLevel.info, `[object.listWithIterator] Starting paginated query with limit=${limit}`);
|
|
346
360
|
|
|
347
361
|
while (hasMore) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
362
|
+
try {
|
|
363
|
+
const res = await this.object.list({
|
|
364
|
+
offset,
|
|
365
|
+
limit,
|
|
366
|
+
filter
|
|
367
|
+
});
|
|
353
368
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
369
|
+
if (res.code !== '0') {
|
|
370
|
+
this.log(LoggerLevel.error, `[object.listWithIterator] Error querying objects: code=${res.code}, msg=${res.msg}, offset=${offset}`);
|
|
371
|
+
allSuccess = false;
|
|
372
|
+
failed.push({
|
|
373
|
+
offset,
|
|
374
|
+
limit,
|
|
375
|
+
code: res.code,
|
|
376
|
+
msg: res.msg || `Query failed with code ${res.code}`
|
|
377
|
+
});
|
|
378
|
+
// 继续尝试下一页,而不是直接退出
|
|
379
|
+
offset += limit;
|
|
380
|
+
page += 1;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
358
383
|
|
|
359
|
-
|
|
384
|
+
page += 1;
|
|
360
385
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
386
|
+
if (Array.isArray(res.items)) {
|
|
387
|
+
results = results.concat(res.items);
|
|
388
|
+
}
|
|
364
389
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
390
|
+
if (res.total !== undefined && res.total !== null) {
|
|
391
|
+
total = res.total;
|
|
392
|
+
}
|
|
368
393
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
394
|
+
if (page === 1) {
|
|
395
|
+
totalPages = Math.ceil(total / limit);
|
|
396
|
+
this.log(LoggerLevel.info, `[object.listWithIterator] Total objects: ${total}, pages: ${totalPages}`);
|
|
397
|
+
}
|
|
373
398
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
399
|
+
// 判断是否还有更多数据
|
|
400
|
+
hasMore = res.has_more === true;
|
|
401
|
+
offset += limit;
|
|
377
402
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
403
|
+
const padLength = totalPages.toString().length;
|
|
404
|
+
const pageStr = page.toString().padStart(padLength, '0');
|
|
405
|
+
const totalPagesStr = totalPages.toString().padStart(padLength, '0');
|
|
381
406
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
407
|
+
this.log(LoggerLevel.info, `[object.listWithIterator] Page completed: [${pageStr}/${totalPagesStr}]`);
|
|
408
|
+
this.log(LoggerLevel.debug, `[object.listWithIterator] Page ${page} details: items=${res.items?.length}, hasMore=${hasMore}`);
|
|
409
|
+
this.log(LoggerLevel.trace, `[object.listWithIterator] Page ${page} data: ${JSON.stringify(res.items)}`);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
this.log(LoggerLevel.error, `[object.listWithIterator] Exception occurred: ${error}, offset=${offset}`);
|
|
412
|
+
allSuccess = false;
|
|
413
|
+
failed.push({
|
|
414
|
+
offset,
|
|
415
|
+
limit,
|
|
416
|
+
code: '1',
|
|
417
|
+
msg: error instanceof Error ? error.message : String(error)
|
|
418
|
+
});
|
|
419
|
+
// 继续尝试下一页
|
|
420
|
+
offset += limit;
|
|
421
|
+
page += 1;
|
|
422
|
+
|
|
423
|
+
// 如果没有获取到 total,可能需要退出循环
|
|
424
|
+
if (total === 0) {
|
|
425
|
+
hasMore = false;
|
|
426
|
+
} else {
|
|
427
|
+
hasMore = offset < total;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
385
430
|
}
|
|
386
431
|
|
|
387
|
-
|
|
388
|
-
|
|
432
|
+
const resultCode = failed.length.toString();
|
|
433
|
+
const resultMsg = failed.length === 0
|
|
434
|
+
? 'Success'
|
|
435
|
+
: `Completed with ${failed.length} failed page(s)`;
|
|
436
|
+
|
|
437
|
+
this.log(LoggerLevel.info, `[object.listWithIterator] Completed: code=${resultCode}, total=${total}, fetched=${results.length}, failed=${failed.length}`);
|
|
438
|
+
|
|
439
|
+
const result: any = {
|
|
440
|
+
code: resultCode,
|
|
441
|
+
msg: resultMsg,
|
|
442
|
+
items: results,
|
|
443
|
+
total
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
if (failed.length > 0) {
|
|
447
|
+
result.failed = failed;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return result;
|
|
389
451
|
},
|
|
390
452
|
|
|
391
453
|
metadata: {
|
|
@@ -431,6 +493,267 @@ class Client {
|
|
|
431
493
|
this.log(LoggerLevel.debug, `[object.metadata.fields] All fields metadata fetched: ${object_name}, code=${res.data.code}`);
|
|
432
494
|
this.log(LoggerLevel.trace, `[object.metadata.fields] Response: ${JSON.stringify(res.data)}`);
|
|
433
495
|
return res.data;
|
|
496
|
+
},
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* 导出数据对象元数据为 Markdown 文档
|
|
500
|
+
* @description 将数据对象的字段信息导出为详细的 Markdown 文档,包含字段类型、配置、选项等完整信息
|
|
501
|
+
* @param options 导出配置
|
|
502
|
+
* @param options.object_names 可选,要导出的对象名称数组。如果不传,则导出所有对象
|
|
503
|
+
* @returns Markdown 文档字符串
|
|
504
|
+
* @example
|
|
505
|
+
* ```typescript
|
|
506
|
+
* // 导出所有对象
|
|
507
|
+
* const markdown = await client.object.metadata.export2markdown();
|
|
508
|
+
*
|
|
509
|
+
* // 只导出指定对象
|
|
510
|
+
* const markdown = await client.object.metadata.export2markdown({
|
|
511
|
+
* object_names: ['object_store', 'object_order', '_user']
|
|
512
|
+
* });
|
|
513
|
+
*
|
|
514
|
+
* // 结合 listWithIterator 使用
|
|
515
|
+
* const allObjects = await client.object.listWithIterator();
|
|
516
|
+
* const objectNames = allObjects.items.map(obj => obj.apiName);
|
|
517
|
+
* const markdown = await client.object.metadata.export2markdown({
|
|
518
|
+
* object_names: objectNames
|
|
519
|
+
* });
|
|
520
|
+
*
|
|
521
|
+
* // 保存到文件
|
|
522
|
+
* fs.writeFileSync('objects_doc.md', markdown, 'utf-8');
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
export2markdown: async (options?: { object_names?: string[] }): Promise<string> => {
|
|
526
|
+
const objectNames = options?.object_names;
|
|
527
|
+
|
|
528
|
+
this.log(LoggerLevel.info, `[object.metadata.export2markdown] Starting markdown export${objectNames && objectNames.length > 0 ? ` for ${objectNames.length} objects` : ' for all objects'}`);
|
|
529
|
+
|
|
530
|
+
let items: any[] = [];
|
|
531
|
+
|
|
532
|
+
if (objectNames && objectNames.length > 0) {
|
|
533
|
+
// 如果指定了对象名称,只获取这些对象
|
|
534
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetching specified objects: ${objectNames.join(', ')}`);
|
|
535
|
+
|
|
536
|
+
// 先获取所有对象列表
|
|
537
|
+
const allObjects = await this.object.listWithIterator();
|
|
538
|
+
|
|
539
|
+
// 过滤出指定的对象
|
|
540
|
+
items = allObjects.items.filter((obj: any) => objectNames.includes(obj.apiName));
|
|
541
|
+
|
|
542
|
+
// 检查是否有不存在的对象
|
|
543
|
+
if (items.length < objectNames.length) {
|
|
544
|
+
const foundNames = items.map((obj: any) => obj.apiName);
|
|
545
|
+
const notFound = objectNames.filter(name => !foundNames.includes(name));
|
|
546
|
+
this.log(LoggerLevel.warn, `[object.metadata.export2markdown] Objects not found: ${notFound.join(', ')}`);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Found ${items.length}/${objectNames.length} matching objects`);
|
|
550
|
+
|
|
551
|
+
if (items.length === 0) {
|
|
552
|
+
this.log(LoggerLevel.warn, `[object.metadata.export2markdown] No matching objects found`);
|
|
553
|
+
return '# 数据对象字段文档\n\n> 未找到匹配的对象\n';
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
// 获取所有对象
|
|
557
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetching all objects`);
|
|
558
|
+
const allObjects = await this.object.listWithIterator();
|
|
559
|
+
items = allObjects.items || [];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetched ${items.length} objects`);
|
|
563
|
+
|
|
564
|
+
// 生成 Markdown 文档
|
|
565
|
+
let markdown = '# 数据对象字段文档\n\n';
|
|
566
|
+
markdown += `> 生成时间: ${dayjs().format('YYYY-MM-DD HH:mm:ss')}\n\n`;
|
|
567
|
+
markdown += `> 对象总数: ${items.length}\n\n`;
|
|
568
|
+
markdown += '---\n\n';
|
|
569
|
+
|
|
570
|
+
// 目录
|
|
571
|
+
markdown += '## 目录\n\n';
|
|
572
|
+
items.forEach((obj: any, index: number) => {
|
|
573
|
+
const chineseName = obj.label?.zh_CN || obj.label?.en_US || obj.apiName;
|
|
574
|
+
markdown += `${index + 1}. [${chineseName} (${obj.apiName})](#${obj.apiName.replace(/_/g, '')})\n`;
|
|
575
|
+
});
|
|
576
|
+
markdown += '\n---\n\n';
|
|
577
|
+
|
|
578
|
+
// 遍历每个对象
|
|
579
|
+
for (const obj of items) {
|
|
580
|
+
const chineseName = obj.label?.zh_CN || obj.label?.en_US || obj.apiName;
|
|
581
|
+
const englishName = obj.label?.en_US || '';
|
|
582
|
+
|
|
583
|
+
markdown += `## ${chineseName} \`${obj.apiName}\`\n\n`;
|
|
584
|
+
|
|
585
|
+
if (englishName && englishName !== chineseName) {
|
|
586
|
+
markdown += `**英文名称:** ${englishName}\n\n`;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
markdown += `**创建时间:** ${dayjs(obj.createdAt).format('YYYY-MM-DD HH:mm:ss')}\n\n`;
|
|
590
|
+
markdown += `**字段数量:** ${obj.fields?.length || 0}\n\n`;
|
|
591
|
+
|
|
592
|
+
// 字段表格
|
|
593
|
+
if (obj.fields && obj.fields.length > 0) {
|
|
594
|
+
// 对字段进行分类和排序
|
|
595
|
+
const systemFieldOrder = ['_name', '_createdBy', '_createdAt', '_updatedBy', '_updatedAt'];
|
|
596
|
+
const specialFieldTypes = ['formula', 'referenceField'];
|
|
597
|
+
|
|
598
|
+
let idField: any = null;
|
|
599
|
+
const normalFields: any[] = [];
|
|
600
|
+
const specialFields: any[] = [];
|
|
601
|
+
const systemFields: any[] = [];
|
|
602
|
+
|
|
603
|
+
obj.fields.forEach((field: any) => {
|
|
604
|
+
if (field.apiName === '_id') {
|
|
605
|
+
idField = field;
|
|
606
|
+
} else if (systemFieldOrder.includes(field.apiName)) {
|
|
607
|
+
systemFields.push(field);
|
|
608
|
+
} else if (specialFieldTypes.includes(field.type?.name)) {
|
|
609
|
+
specialFields.push(field);
|
|
610
|
+
} else {
|
|
611
|
+
normalFields.push(field);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
// 对系统字段按指定顺序排序
|
|
616
|
+
systemFields.sort((a, b) => {
|
|
617
|
+
return systemFieldOrder.indexOf(a.apiName) - systemFieldOrder.indexOf(b.apiName);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// 组合所有字段:_id + 正常字段 + 特殊字段 + 系统字段
|
|
621
|
+
const sortedFields: any[] = [];
|
|
622
|
+
if (idField) sortedFields.push(idField);
|
|
623
|
+
sortedFields.push(...normalFields);
|
|
624
|
+
sortedFields.push(...specialFields);
|
|
625
|
+
sortedFields.push(...systemFields);
|
|
626
|
+
|
|
627
|
+
markdown += '### 字段列表\n\n';
|
|
628
|
+
markdown += '| 中文名称 | API名称 | 类型 | 必填 | 唯一 | 其他设置 |\n';
|
|
629
|
+
markdown += '|---------|---------|------|------|------|----------|\n';
|
|
630
|
+
|
|
631
|
+
for (const field of sortedFields) {
|
|
632
|
+
// 转义 Markdown 表格中的特殊字符
|
|
633
|
+
const escapeMarkdown = (text: string): string => {
|
|
634
|
+
return text.replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
const label = escapeMarkdown(field.label?.zh_CN || field.label?.en_US || '-');
|
|
638
|
+
const apiName = field.apiName || '-';
|
|
639
|
+
const typeName = field.type?.name || '-';
|
|
640
|
+
const required = field.type?.settings?.required ? '✓' : '';
|
|
641
|
+
const unique = field.type?.settings?.unique ? '✓' : '';
|
|
642
|
+
|
|
643
|
+
// 构建其他设置信息
|
|
644
|
+
const otherSettings: string[] = [];
|
|
645
|
+
const settings = field.type?.settings || {};
|
|
646
|
+
|
|
647
|
+
// lookup 类型:标注关联的对象(外键)
|
|
648
|
+
if (field.type?.name === 'lookup' && settings.objectAPIName) {
|
|
649
|
+
otherSettings.push(`🔗 关联对象: \`${settings.objectAPIName}\``);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// referenceField 类型:引用字段
|
|
653
|
+
if (field.type?.name === 'referenceField') {
|
|
654
|
+
otherSettings.push(`⚙️ 系统自动维护,不需要写/更新`);
|
|
655
|
+
if (settings.guideFieldAPIName) {
|
|
656
|
+
otherSettings.push(`📎 引用自: \`${settings.guideFieldAPIName}\``);
|
|
657
|
+
}
|
|
658
|
+
if (settings.fieldAPIName) {
|
|
659
|
+
otherSettings.push(`📋 引用字段: \`${settings.fieldAPIName}\``);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// formula 类型:系统自动维护
|
|
664
|
+
if (field.type?.name === 'formula') {
|
|
665
|
+
otherSettings.push(`⚙️ 系统自动维护,不需要写/更新`);
|
|
666
|
+
if (settings.formula && Array.isArray(settings.formula)) {
|
|
667
|
+
// 优先显示中文公式,否则显示第一个
|
|
668
|
+
const zhFormula = settings.formula.find((f: any) => f.language_code === 2052);
|
|
669
|
+
const formulaText = zhFormula?.text || settings.formula[0]?.text;
|
|
670
|
+
if (formulaText) {
|
|
671
|
+
// 转义公式中的特殊字符
|
|
672
|
+
const escapedFormula = formulaText.replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
673
|
+
otherSettings.push(`公式: ${escapedFormula}`);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
if (settings.returnType) {
|
|
677
|
+
otherSettings.push(`返回类型: ${settings.returnType}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// 根据不同类型展示不同的设置
|
|
682
|
+
if (settings.maxLength) {
|
|
683
|
+
otherSettings.push(`最大长度:${settings.maxLength}`);
|
|
684
|
+
}
|
|
685
|
+
if (settings.decimalPlacesNumber !== undefined) {
|
|
686
|
+
otherSettings.push(`小数位:${settings.decimalPlacesNumber}`);
|
|
687
|
+
}
|
|
688
|
+
if (settings.displayAsPercentage) {
|
|
689
|
+
otherSettings.push('百分比显示');
|
|
690
|
+
}
|
|
691
|
+
if (settings.multiline) {
|
|
692
|
+
otherSettings.push('多行');
|
|
693
|
+
}
|
|
694
|
+
if (settings.multiple) {
|
|
695
|
+
otherSettings.push('多选');
|
|
696
|
+
}
|
|
697
|
+
if (settings.hierarchy) {
|
|
698
|
+
otherSettings.push('层级');
|
|
699
|
+
}
|
|
700
|
+
if (settings.displayStyle && field.type?.name !== 'lookup') {
|
|
701
|
+
otherSettings.push(`显示样式:${settings.displayStyle}`);
|
|
702
|
+
}
|
|
703
|
+
if (settings.referenceObjectApiName) {
|
|
704
|
+
otherSettings.push(`关联:${settings.referenceObjectApiName}`);
|
|
705
|
+
}
|
|
706
|
+
if (settings.rollUpType) {
|
|
707
|
+
otherSettings.push(`汇总:${settings.rollUpType}`);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// 如果是 option 类型,获取选项列表
|
|
711
|
+
if (field.type?.name === 'option') {
|
|
712
|
+
// 优先使用已有的 optionList,避免额外 API 请求
|
|
713
|
+
const options = settings.optionList;
|
|
714
|
+
if (options && options.length > 0) {
|
|
715
|
+
const optionTexts = options.map((opt: any) => {
|
|
716
|
+
const zhLabel = opt.label?.find((l: any) => l.language_code === 2052)?.text || '-';
|
|
717
|
+
return `${zhLabel}(\`${opt.apiName}\`)`;
|
|
718
|
+
});
|
|
719
|
+
otherSettings.push(`选项: ${optionTexts.join(', ')}`);
|
|
720
|
+
} else {
|
|
721
|
+
// 如果没有 optionList,再尝试单独请求(但这可能很慢)
|
|
722
|
+
try {
|
|
723
|
+
this.log(LoggerLevel.debug, `[object.metadata.export2markdown] Fetching option details for ${obj.apiName}.${field.apiName}`);
|
|
724
|
+
const optionsResult = await this.object.metadata.field({
|
|
725
|
+
object_name: obj.apiName,
|
|
726
|
+
field_name: field.apiName
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
const fetchedOptions = optionsResult?.data?.type?.settings?.optionList;
|
|
730
|
+
if (fetchedOptions && fetchedOptions.length > 0) {
|
|
731
|
+
const optionTexts = fetchedOptions.map((opt: any) => {
|
|
732
|
+
const zhLabel = opt.label?.find((l: any) => l.language_code === 2052)?.text || '-';
|
|
733
|
+
return `${zhLabel}(\`${opt.apiName}\`)`;
|
|
734
|
+
});
|
|
735
|
+
otherSettings.push(`选项: ${optionTexts.join(', ')}`);
|
|
736
|
+
}
|
|
737
|
+
} catch (error) {
|
|
738
|
+
this.log(LoggerLevel.warn, `[object.metadata.export2markdown] Failed to fetch option field details for ${obj.apiName}.${field.apiName}:`, error);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const otherSettingsStr = otherSettings.length > 0 ? otherSettings.join('<br>') : '-';
|
|
744
|
+
|
|
745
|
+
markdown += `| ${label} | \`${apiName}\` | ${typeName} | ${required} | ${unique} | ${otherSettingsStr} |\n`;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
markdown += '\n';
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
markdown += '---\n\n';
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
this.log(LoggerLevel.info, `[object.metadata.export2markdown] Markdown export completed`);
|
|
755
|
+
|
|
756
|
+
return markdown;
|
|
434
757
|
}
|
|
435
758
|
},
|
|
436
759
|
|