apaas-oapi-client 0.1.34 → 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 CHANGED
@@ -16,6 +16,7 @@
16
16
  - ✅ record 单条查询
17
17
  - ✅ record 单条更新、批量更新
18
18
  - ✅ record 单条删除、批量删除
19
+ - ✅ **导出数据对象文档为 Markdown**
19
20
  - ✅ 内置 Bottleneck 限流器
20
21
  - ✅ 自定义日志等级
21
22
 
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
@@ -287,6 +287,242 @@ class Client {
287
287
  this.log(LoggerLevel.debug, `[object.metadata.fields] All fields metadata fetched: ${object_name}, code=${res.data.code}`);
288
288
  this.log(LoggerLevel.trace, `[object.metadata.fields] Response: ${JSON.stringify(res.data)}`);
289
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;
290
526
  }
291
527
  },
292
528
  search: {
@@ -157,6 +157,36 @@ declare class Client {
157
157
  fields: (params: {
158
158
  object_name: string;
159
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>;
160
190
  };
161
191
  search: {
162
192
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apaas-oapi-client",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "main": "dist/index.js",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
package/src/index.ts CHANGED
@@ -493,6 +493,267 @@ class Client {
493
493
  this.log(LoggerLevel.debug, `[object.metadata.fields] All fields metadata fetched: ${object_name}, code=${res.data.code}`);
494
494
  this.log(LoggerLevel.trace, `[object.metadata.fields] Response: ${JSON.stringify(res.data)}`);
495
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;
496
757
  }
497
758
  },
498
759