byteplan-cli 1.3.6 → 1.3.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byteplan-cli",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "BytePlan CLI - Command line tool for BytePlan API",
5
5
  "keywords": [
6
6
  "byteplan",
@@ -627,34 +627,27 @@ export async function queryModels(token, options = {}) {
627
627
  * @param {Object} params.customQuery - 自定义查询条件
628
628
  * @param {string[]} params.groupFields - 分组字段
629
629
  * @param {Array} params.functions - 聚合函数
630
+ * @param {Array} params.orderBy - 排序字段 [{field, direction}]
631
+ * @param {number} params.page - 页码(从 0 开始)
632
+ * @param {number} params.pageSize - 每页条数
630
633
  * @param {object} options - 可选配置
631
634
  * @param {object} options.menuHeaders - 自定义 menu headers
632
- * @returns {Promise<any>}
635
+ * @returns {Promise<{data: Array}>} 返回数据数组,无 total 字段
633
636
  */
634
637
  export async function getModelData(token, modelCode, params = {}, options = {}) {
635
- const { customQuery, groupFields, functions } = params;
638
+ const { customQuery, groupFields, functions, orderBy, page = 0, pageSize = 100 } = params;
636
639
  const { menuHeaders = {} } = options;
637
640
 
638
641
  // CLI 模式(仅支持简单查询)
639
- if (useCliMode && !token && !customQuery && !groupFields && !functions) {
642
+ if (useCliMode && !token && !customQuery && !groupFields && !functions && !orderBy) {
640
643
  await ensureCliInstalled();
641
- const pageNum = params.pageNum || 0;
642
- const pageSize = params.pageSize || 100;
643
644
  try {
644
- const result = await execCli(['data', 'query', modelCode, '-p', String(pageNum), '-s', String(pageSize)]);
645
+ const result = await execCli(['data', 'query', modelCode, '-p', String(page), '-s', String(pageSize)]);
645
646
  if (result.error) {
646
647
  throw new Error(result.message || '查询数据失败');
647
648
  }
648
- // CLI 结果转换为原有格式
649
- return {
650
- data: {
651
- headers: result.headers || [],
652
- data: result.data || [],
653
- pageNum: result.pagination?.pageNum || pageNum,
654
- pageSize: result.pagination?.pageSize || pageSize,
655
- total: result.pagination?.total || 0,
656
- }
657
- };
649
+ // CLI 返回格式: { data: [], pagination: { page, pageSize, total } }
650
+ return { data: result.data || [] };
658
651
  } catch (e) {
659
652
  console.warn(`⚠️ CLI 查询数据失败,回退到直接 API: ${e.message}`);
660
653
  useCliMode = false;
@@ -676,22 +669,15 @@ export async function getModelData(token, modelCode, params = {}, options = {})
676
669
 
677
670
  const body = {
678
671
  modelCode,
679
- params: {},
672
+ groupFields: groupFields || [],
673
+ functions: functions || [],
674
+ customQuery: customQuery || {},
675
+ orderBy: orderBy || [],
676
+ page,
677
+ pageSize,
680
678
  };
681
679
 
682
- if (customQuery) {
683
- body.params.customQuery = customQuery;
684
- }
685
-
686
- if (groupFields && groupFields.length > 0) {
687
- body.params.groupFields = groupFields;
688
- }
689
-
690
- if (functions && functions.length > 0) {
691
- body.params.functions = functions;
692
- }
693
-
694
- const response = await fetch(`${baseUrl}/foresight/api/bi/multdim/anls/ai/query`, {
680
+ const response = await fetch(`${baseUrl}/foresight/api/bi/multdim/anls/ai/query/by/co`, {
695
681
  method: 'POST',
696
682
  headers,
697
683
  body: JSON.stringify(body),
package/src/api.js CHANGED
@@ -30,7 +30,7 @@ const BASIC_AUTH = 'Basic UEM6bkxDbndkSWhpeldieWtIeXVaTTZUcFFEZDdLd0s5SVhESzhMR3
30
30
 
31
31
  const DEFAULT_MENU_HEADERS = {
32
32
  'x-menu-code': 'AI_REPORT',
33
- 'x-menu-id': '2008425412219936770',
33
+ 'x-menu-id': '2009191076595752963',
34
34
  'x-menu-params': 'null',
35
35
  'x-page-code': 'ai_report',
36
36
  };
@@ -326,7 +326,7 @@ export async function getModelColumns(token, modelCodes) {
326
326
  // 查询模型数据
327
327
  export async function getModelData(token, modelCode, params = {}) {
328
328
  const baseUrl = getBaseUrl();
329
- const response = await fetch(baseUrl + '/foresight/api/bi/multdim/anls/ai/query', {
329
+ const response = await fetch(baseUrl + '/foresight/api/bi/multdim/anls/ai/query/by/co', {
330
330
  method: 'POST',
331
331
  headers: {
332
332
  'accept': 'application/json, text/plain, */*',
@@ -336,11 +336,12 @@ export async function getModelData(token, modelCode, params = {}) {
336
336
  },
337
337
  body: JSON.stringify({
338
338
  modelCode,
339
- params: {
340
- pageNum: params.pageNum || 0,
341
- pageSize: params.pageSize || 100,
342
- ...params,
343
- },
339
+ groupFields: params.groupFields || [],
340
+ functions: params.functions || [],
341
+ customQuery: params.customQuery || {},
342
+ orderBy: params.orderBy || [],
343
+ page: params.page || params.pageNum || 0,
344
+ pageSize: params.pageSize || 100,
344
345
  }),
345
346
  });
346
347
 
package/src/cli.js CHANGED
@@ -372,23 +372,145 @@ dataCmd
372
372
  .description('Query data from a model')
373
373
  .option('-p, --page <number>', 'Page number (starting from 0)', '0')
374
374
  .option('-s, --size <number>', 'Page size', '100')
375
+ .option('-q, --query <json>', 'Query condition (JSON format)')
376
+ .option('-g, --group <fields>', 'Group fields (comma separated)')
377
+ .option('-f, --functions <json>', 'Aggregation functions (JSON array)')
378
+ .option('-o, --order <json>', 'Order by fields (JSON array)')
379
+ .addHelpText('after', `
380
+ Examples:
381
+ # Simple query
382
+ byteplan data query CBEC_PRODUCT -p 0 -s 10
383
+
384
+ # With filter condition
385
+ byteplan data query CBEC_PRODUCT -q '{"type":"condition","field":"category","operator":"=","value":"饮料"}'
386
+
387
+ # With multiple conditions (AND)
388
+ byteplan data query CBEC_PRODUCT -q '{"type":"group","logic":"AND","children":[{"type":"condition","field":"category","operator":"=","value":"饮料"},{"type":"condition","field":"selling_price","operator":">","value":"5"}]}'
389
+
390
+ # With grouping
391
+ byteplan data query CBEC_PRODUCT -g category,subcategory
392
+
393
+ # With aggregation functions
394
+ byteplan data query CBEC_ORDER -g product_id -f '[{"func":"sum","field":"quantity","alias":"total_qty"},{"func":"avg","field":"unit_price","alias":"avg_price"}]'
395
+
396
+ # Complete aggregation example
397
+ byteplan data query CBEC_ORDER -g product_id -q '{"type":"condition","field":"order_date","operator":"BETWEEN","value":["2024-01-01","2024-12-31"]}' -f '[{"func":"sum","field":"amount","alias":"total_amount"},{"func":"count","field":"id","alias":"order_count"}]'
398
+
399
+ # With sorting
400
+ byteplan data query CBEC_PRODUCT -o '[{"field":"selling_price","direction":"desc"},{"field":"product_name","direction":"asc"}]'
401
+
402
+ # Combined example with filter and sort
403
+ byteplan data query CBEC_PRODUCT -q '{"type":"condition","field":"category","operator":"=","value":"饮料"}' -o '[{"field":"selling_price","direction":"desc"}]' -s 5
404
+
405
+ Query Syntax:
406
+
407
+ 1. Single Condition:
408
+ {
409
+ "type": "condition",
410
+ "field": "字段名",
411
+ "operator": "=",
412
+ "value": "值"
413
+ }
414
+
415
+ 2. Condition Group (AND/OR):
416
+ {
417
+ "type": "group",
418
+ "logic": "AND",
419
+ "children": [
420
+ { "type": "condition", "field": "field1", "operator": "=", "value": "value1" },
421
+ { "type": "condition", "field": "field2", "operator": ">", "value": "value2" }
422
+ ]
423
+ }
424
+
425
+ Operators: =, !=, >, <, >=, <=, LIKE, IN, BETWEEN
426
+
427
+ Special Fields (DIM/LIST/LOV/LEVEL):
428
+ - Use field name directly (no .code or .name suffix)
429
+ - Use code value for filtering
430
+
431
+ Group Fields (-g):
432
+ Comma-separated field names for aggregation grouping
433
+ Example: -g category,subcategory
434
+
435
+ Aggregation Functions (-f):
436
+ JSON array of function objects:
437
+ [
438
+ { "func": "sum", "field": "数值字段", "alias": "别名" },
439
+ { "func": "avg", "field": "数值字段", "alias": "别名" },
440
+ { "func": "count", "field": "任意字段", "alias": "别名" },
441
+ { "func": "max", "field": "字段", "alias": "别名" },
442
+ { "func": "min", "field": "字段", "alias": "别名" }
443
+ ]
444
+
445
+ Supported functions: sum, avg, count, max, min
446
+
447
+ Note: When using aggregation functions, -g (group) is usually needed.
448
+ Without grouping, aggregation returns a single summary row.
449
+
450
+ Order By (-o):
451
+ JSON array of sort objects:
452
+ [
453
+ { "field": "字段名", "direction": "desc" },
454
+ { "field": "字段名", "direction": "asc" }
455
+ ]
456
+
457
+ direction: "asc" (升序) or "desc" (降序)
458
+ Example: -o '[{"field":"created_date","direction":"desc"}]'
459
+ `)
375
460
  .action(async (modelCode, options) => {
376
461
  try {
377
462
  await loginWithEnv();
378
- const result = await getModelData(null, modelCode, {
379
- pageNum: parseInt(options.page),
463
+
464
+ const params = {
465
+ page: parseInt(options.page),
380
466
  pageSize: parseInt(options.size),
381
- });
467
+ };
468
+
469
+ // 解析查询条件
470
+ if (options.query) {
471
+ try {
472
+ params.customQuery = JSON.parse(options.query);
473
+ } catch (e) {
474
+ printJSON({ error: true, message: 'Invalid JSON query: ' + e.message });
475
+ process.exit(1);
476
+ }
477
+ }
478
+
479
+ // 解析分组字段
480
+ if (options.group) {
481
+ params.groupFields = options.group.split(',').map(f => f.trim());
482
+ }
382
483
 
383
- // result.data 可能是数组(直接数据)或对象(包含 data 子字段)
384
- const rows = Array.isArray(result.data) ? result.data : (result.data?.data || []);
484
+ // 解析聚合函数
485
+ if (options.functions) {
486
+ try {
487
+ params.functions = JSON.parse(options.functions);
488
+ } catch (e) {
489
+ printJSON({ error: true, message: 'Invalid JSON functions: ' + e.message });
490
+ process.exit(1);
491
+ }
492
+ }
493
+
494
+ // 解析排序字段
495
+ if (options.order) {
496
+ try {
497
+ params.orderBy = JSON.parse(options.order);
498
+ } catch (e) {
499
+ printJSON({ error: true, message: 'Invalid JSON order: ' + e.message });
500
+ process.exit(1);
501
+ }
502
+ }
503
+
504
+ const result = await getModelData(null, modelCode, params);
505
+
506
+ // result.data 是数据数组
507
+ const rows = Array.isArray(result.data) ? result.data : [];
385
508
  printJSON({
386
- headers: result.headers || result.data?.headers || [],
387
509
  data: rows,
388
510
  pagination: {
389
- pageNum: result.pageNum || result.data?.pageNum || parseInt(options.page),
390
- pageSize: result.pageSize || result.data?.pageSize || parseInt(options.size),
391
- total: result.total || result.data?.total || 0,
511
+ page: parseInt(options.page),
512
+ pageSize: parseInt(options.size),
513
+ total: rows.length,
392
514
  },
393
515
  });
394
516
  } catch (error) {