@dazitech/cli 3.0.8 → 3.0.9

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.
Files changed (75) hide show
  1. package/README.md +1 -1
  2. package/dist/clis/dazi-app.js +1 -1
  3. package/dist/clis/dazi-flow.js +1 -1
  4. package/dist/clis/dazi-onto.js +1 -1
  5. package/dist/clis/dazi.js +1 -1
  6. package/dist/docs/flow/flow-project-guide.md +1 -1
  7. package/dist/docs/guides/troubleshooting.md +12 -1
  8. package/dist/docs/index.json +20 -1
  9. package/dist/docs/onto/dazi_script_sdk_reference.md +78 -12
  10. package/dist/docs/onto/function-guide.md +61 -33
  11. package/dist/docs/onto//346/234/254/344/275/223/345/210/206/347/261/273/350/247/204/345/210/222/344/270/216SDK/346/211/251/345/261/225/346/226/271/346/241/210.md +168 -0
  12. package/dist/docs/onto//346/234/254/344/275/223/345/221/275/345/220/215/350/247/204/350/214/203_/347/211/251/347/220/206/350/241/250Cube/344/270/216/345/257/271/350/261/241.md +402 -0
  13. package/dist/docs/onto//346/234/254/344/275/223/350/204/232/346/234/254/347/274/226/345/206/231/346/214/207/345/215/227.md +63 -1
  14. package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +123 -15
  15. package/dist/docs/onto//350/204/232/346/234/254/350/277/220/350/241/214/347/272/240/351/224/231_/345/225/206/345/212/241/346/210/220/346/234/254/346/226/271/346/241/210/345/274/200/345/217/221/350/277/207/347/250/213.md +213 -0
  16. package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/344/272/247/345/223/201/351/224/200/345/224/256/346/234/254/344/275/223/350/247/204/345/210/222/346/226/271/346/241/210.md +356 -233
  17. package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/345/210/251/346/266/246/345/210/206/346/236/220/346/234/254/344/275/223/346/226/271/346/241/210.md +379 -296
  18. package/dist/examples/index.json +23 -17
  19. package/dist/examples/onto/README.md +13 -5
  20. package/dist/examples/onto/_templates/ontology_function_template.py +50 -0
  21. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_account_breakdown.py +62 -0
  22. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_budget_vs_actual.py +69 -0
  23. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_cost_center_profit.py +64 -0
  24. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_get_summary.py +61 -0
  25. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_mom_analysis.py +82 -0
  26. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_top_accounts.py +61 -0
  27. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_yoy_analysis.py +79 -0
  28. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/save_test_arguments.ps1 +38 -0
  29. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.account_breakdown.json +1 -0
  30. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.budget_vs_actual.json +1 -0
  31. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.cost_center_profit.json +1 -0
  32. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.get_summary.json +1 -0
  33. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.mom_analysis.json +1 -0
  34. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.top_accounts.json +1 -0
  35. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.yoy_analysis.json +1 -0
  36. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +232 -74
  37. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +16 -13
  38. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +19 -16
  39. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +48 -50
  40. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +3 -6
  41. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +11 -12
  42. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +6 -7
  43. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +5 -8
  44. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +3 -6
  45. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +32 -19
  46. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.channel_mix.json +3 -6
  47. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.customer_segmentation.json +2 -7
  48. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.get_summary.json +2 -5
  49. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.mom_analysis.json +2 -5
  50. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.region_breakdown.json +2 -5
  51. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.top_products.json +2 -7
  52. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.yoy_analysis.json +2 -5
  53. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +291 -155
  54. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +59 -20
  55. package/dist/prompts/index.json +1 -1
  56. package/dist/prompts/onto/function-design.md +42 -22
  57. package/dist/prompts/onto/script-publish-run.md +15 -1
  58. package/package.json +1 -1
  59. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_account_breakdown.py +0 -99
  60. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_budget_vs_actual.py +0 -116
  61. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_cost_center_profit.py +0 -85
  62. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_get_summary.py +0 -76
  63. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_mom_analysis.py +0 -86
  64. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_top_accounts.py +0 -103
  65. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_yoy_analysis.py +0 -86
  66. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/save_test_arguments.ps1 +0 -27
  67. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.account_breakdown.json +0 -10
  68. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.budget_vs_actual.json +0 -10
  69. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.cost_center_profit.json +0 -9
  70. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.get_summary.json +0 -9
  71. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.mom_analysis.json +0 -9
  72. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.top_accounts.json +0 -11
  73. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.yoy_analysis.json +0 -9
  74. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/README.md +0 -25
  75. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/README.md +0 -5
@@ -1,10 +1,10 @@
1
- """利润分析演示数据灌入 — space__zlj
1
+ """利润分析演示数据灌入 — space__misc_01
2
2
 
3
3
  前置:先执行 profit_ontology_init.py 建表。
4
- 幂等:actual_journal_entry 已有数据则跳过。
4
+ 幂等:fact_gl_journal_entry 已有数据则跳过。
5
5
 
6
- 放置:项目/潘达石化/本体/ontos/利润分析本体方案/setup/profit_seed_data.py
7
- 发布:dazi onto script publish 项目/潘达石化/本体/ontos/利润分析本体方案/setup/profit_seed_data.py --space space__zlj --type data
6
+ 放置:项目/DAZI_TEST/本体/ontos/利润分析示例/setup/profit_seed_data.py
7
+ 发布:dazi onto script publish 项目/DAZI_TEST/本体/ontos/利润分析示例/setup/profit_seed_data.py --space space__misc_01 --type data
8
8
  """
9
9
 
10
10
  import json
@@ -34,9 +34,11 @@ def _make_entry_line(seq, posting_date, account, cc, amount):
34
34
  fp = posting_date.month
35
35
  entry_id = f"JE{posting_date.strftime('%Y%m%d')}{seq // 10:04d}"
36
36
  line_id = f"JL{posting_date.strftime('%Y%m%d')}{seq:05d}"
37
+ date_key = int(posting_date.strftime("%Y%m%d"))
37
38
  return {
38
39
  "entry_id": entry_id,
39
40
  "line_id": line_id,
41
+ "date_key": date_key,
40
42
  "posting_date": posting_date,
41
43
  "fiscal_year": fy,
42
44
  "fiscal_period": fp,
@@ -62,17 +64,17 @@ def _make_entry_line(seq, posting_date, account, cc, amount):
62
64
 
63
65
 
64
66
  def main():
65
- space_id = "space__zlj"
67
+ space_id = "space__misc_01"
66
68
  s = space.get(space_id)
67
69
 
68
70
  output.print("=== 利润分析演示数据灌入 ===")
69
71
 
70
72
  try:
71
- n = int(s.sql.query_one("SELECT count() FROM actual_journal_entry") or 0)
73
+ n = int(s.sql.query_one("SELECT count() FROM fact_gl_journal_entry") or 0)
72
74
  except Exception:
73
75
  n = 0
74
76
  if n > 0:
75
- output.print(f"actual_journal_entry 已有 {n} 行,跳过灌数")
77
+ output.print(f"fact_gl_journal_entry 已有 {n} 行,跳过灌数")
76
78
  output.print("__JSON_SUMMARY__" + json.dumps({"ok": True, "skipped": True, "rows": n}, ensure_ascii=True))
77
79
  return
78
80
 
@@ -101,8 +103,8 @@ def main():
101
103
  for c in cost_centers:
102
104
  c["created_at"] = _SEED_DT
103
105
 
104
- s.sql.insert_rows("account_master", accounts)
105
- s.sql.insert_rows("cost_center_dimension", cost_centers)
106
+ s.sql.insert_rows("dim_account", accounts)
107
+ s.sql.insert_rows("dim_cost_center", cost_centers)
106
108
  output.print("OK 维表数据")
107
109
 
108
110
  leaf_accounts = [a for a in accounts if a["is_leaf"]]
@@ -115,7 +117,6 @@ def main():
115
117
  start = date(2025, 1, 1)
116
118
  end = date(2026, 6, 30)
117
119
 
118
- # 科目基准金额(月)
119
120
  base_amounts = {
120
121
  "ACC6001": 800000,
121
122
  "ACC6050": 50000,
@@ -152,7 +153,7 @@ def main():
152
153
  seq += 1
153
154
  d += timedelta(days=1)
154
155
 
155
- inserted = s.sql.insert_rows("actual_journal_entry", fact_rows)
156
+ inserted = s.sql.insert_rows("fact_gl_journal_entry", fact_rows)
156
157
  output.print(f"OK 实际分录表插入 {inserted} 行")
157
158
 
158
159
  budget_rows = []
@@ -177,9 +178,11 @@ def main():
177
178
  if account["account_id"] == "ACC6603" and cc["cost_center_id"] != "CC05":
178
179
  continue
179
180
  budget_amt = round(base / 6 * random.uniform(0.95, 1.05), 2)
181
+ date_key = int(f"{year}{period:02d}01")
180
182
  budget_rows.append({
181
183
  "budget_id": f"BUD{year}",
182
184
  "line_id": f"BL{year}{period:02d}{bseq:05d}",
185
+ "date_key": date_key,
183
186
  "budget_version": version,
184
187
  "fiscal_year": year,
185
188
  "fiscal_period": period,
@@ -198,7 +201,7 @@ def main():
198
201
  })
199
202
  bseq += 1
200
203
 
201
- binserted = s.sql.insert_rows("budget_entry", budget_rows)
204
+ binserted = s.sql.insert_rows("fact_budget_entry", budget_rows)
202
205
  output.print(f"OK 预算表插入 {binserted} 行")
203
206
 
204
207
  summary = {
@@ -210,4 +213,4 @@ def main():
210
213
  "budget_inserted": binserted,
211
214
  }
212
215
  output.success("灌数完成")
213
- output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
216
+ output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
@@ -1,27 +1,24 @@
1
- """渠道销售占比 sales.fn.channel_mix
1
+ """渠道结构分析 sales.fn.channel_mix
2
2
 
3
3
  参数:start_date, end_date
4
- 返回:各渠道销售额、订单数及占比
4
+ 返回:各渠道销售额、销量、订单数及占比
5
5
 
6
6
  发布:
7
- dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/functions/sales_fn_channel_mix.py \\
8
- --space space__zlj --register-function-id sales.fn.channel_mix
9
-
10
- 保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
11
- 见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_channel_mix.py \
8
+ --space space__misc_01 --register-function-id sales.fn.channel_mix
12
9
  """
13
10
 
14
11
  TEST_ARGUMENTS = {
15
12
  "v": 1,
16
13
  "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
17
- "object_type_code": "SalesChannel",
14
+ "object_type_code": "SalesAnalysis",
18
15
  }
19
16
 
20
17
 
21
18
  def _build_where(start_date, end_date):
22
- clauses = ["f.order_status IN ('已完成', '已发货')"]
19
+ clauses = ["order_status IN ('已完成', '已发货')"]
23
20
  if start_date and end_date:
24
- clauses.append(f"f.order_date >= '{start_date}' AND f.order_date <= '{end_date}'")
21
+ clauses.append(f"order_date >= '{start_date}' AND order_date <= '{end_date}'")
25
22
  return "WHERE " + " AND ".join(clauses)
26
23
 
27
24
 
@@ -34,11 +31,13 @@ def _ontology_fn_body(p):
34
31
  sql = f"""
35
32
  SELECT
36
33
  f.channel_id,
37
- any(c.channel_name) AS channel_name,
34
+ any(dch.channel_name) AS channel_name,
35
+ any(dch.channel_type) AS channel_type,
38
36
  sum(f.sales_amount) AS sales_amount,
37
+ sum(f.quantity) AS quantity,
39
38
  uniq(f.order_id) AS order_count
40
- FROM sales_order_fact AS f
41
- LEFT JOIN channel_dimension AS c ON f.channel_id = c.channel_id
39
+ FROM fact_sales_order_line AS f
40
+ LEFT JOIN dim_channel AS dch ON f.channel_id = dch.channel_id
42
41
  {where_clause}
43
42
  GROUP BY f.channel_id
44
43
  ORDER BY sales_amount DESC
@@ -47,25 +46,29 @@ def _ontology_fn_body(p):
47
46
  result = p.sql.query(sql)
48
47
  if not result:
49
48
  return p.function_result(
50
- columns=["channel_id", "channel_name", "sales_amount", "order_count", "share_pct"],
49
+ columns=["channel_id", "channel_name", "channel_type", "sales_amount", "quantity", "order_count", "share_pct"],
51
50
  data=[],
52
51
  row_count=0,
53
52
  )
54
53
 
55
54
  total = sum(float(r.get("sales_amount", 0) or 0) for r in result)
55
+
56
56
  data = []
57
57
  for row in result:
58
58
  sales_amount = float(row.get("sales_amount", 0) or 0)
59
+ share_pct = sales_amount / total if total > 0 else 0.0
59
60
  data.append({
60
61
  "channel_id": str(row.get("channel_id", "")),
61
62
  "channel_name": str(row.get("channel_name", "")),
63
+ "channel_type": str(row.get("channel_type", "")),
62
64
  "sales_amount": round(sales_amount, 2),
65
+ "quantity": int(row.get("quantity", 0) or 0),
63
66
  "order_count": int(row.get("order_count", 0) or 0),
64
- "share_pct": round(sales_amount / total if total > 0 else 0.0, 4),
67
+ "share_pct": round(share_pct, 4),
65
68
  })
66
69
 
67
70
  return p.function_result(
68
- columns=["channel_id", "channel_name", "sales_amount", "order_count", "share_pct"],
71
+ columns=["channel_id", "channel_name", "channel_type", "sales_amount", "quantity", "order_count", "share_pct"],
69
72
  data=data,
70
73
  row_count=len(data),
71
74
  )
@@ -1,14 +1,11 @@
1
- """客户销售分层 sales.fn.customer_segmentation
1
+ """客户分层分析 sales.fn.customer_segmentation
2
2
 
3
- 参数:metric(sales_amount|quantity), method(quartile|percentile), start_date, end_date
4
- 返回:客户销售额及分层(VIP/High/Medium/Low)
3
+ 参数:metric(sales_amount|quantity), method(quartile|total), start_date, end_date
4
+ 返回:客户分层结果(VIP/战略/普通)
5
5
 
6
6
  发布:
7
- dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/functions/sales_fn_customer_segmentation.py \\
8
- --space space__zlj --register-function-id sales.fn.customer_segmentation
9
-
10
- 保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
11
- 见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_customer_segmentation.py \
8
+ --space space__misc_01 --register-function-id sales.fn.customer_segmentation
12
9
  """
13
10
 
14
11
  TEST_ARGUMENTS = {
@@ -37,72 +34,73 @@ def _ontology_fn_body(p):
37
34
  start_date = params.get("start_date", "")
38
35
  end_date = params.get("end_date", "")
39
36
  where_clause = _build_where(start_date, end_date)
40
- order_col = "quantity" if metric == "quantity" else "sales_amount"
41
37
 
42
38
  sql = f"""
43
39
  SELECT
44
- customer_id,
45
- customer_region,
46
- sum(sales_amount) AS sales_amount,
47
- sum(quantity) AS quantity
48
- FROM sales_order_fact
40
+ f.customer_id,
41
+ any(dc.customer_name) AS customer_name,
42
+ f.customer_type,
43
+ sum(f.sales_amount) AS sales_amount,
44
+ sum(f.quantity) AS quantity,
45
+ uniq(f.order_id) AS order_count
46
+ FROM fact_sales_order_line AS f
47
+ LEFT JOIN dim_customer AS dc ON f.customer_id = dc.customer_id
49
48
  {where_clause}
50
- GROUP BY customer_id, customer_region
51
- ORDER BY {order_col} DESC
49
+ GROUP BY f.customer_id, f.customer_type
50
+ ORDER BY sales_amount DESC
52
51
  """
53
52
 
54
53
  result = p.sql.query(sql)
55
54
  if not result:
56
55
  return p.function_result(
57
- columns=["customer_id", "customer_region", "sales_amount", "quantity", "segment"],
56
+ columns=["customer_id", "customer_name", "customer_type", "sales_amount", "quantity", "order_count", "segment"],
58
57
  data=[],
59
58
  row_count=0,
60
59
  )
61
60
 
62
- values = [float(row.get(order_col, 0) or 0) for row in result]
63
- total = len(values)
64
- if total == 0:
65
- return p.function_result(
66
- columns=["customer_id", "customer_region", "sales_amount", "quantity", "segment"],
67
- data=[],
68
- row_count=0,
69
- )
61
+ total_key = "quantity" if metric == "quantity" else "sales_amount"
62
+ total = sum(float(r.get(total_key, 0) or 0) for r in result)
70
63
 
71
- sorted_vals = sorted(values, reverse=True)
72
- if method == "percentile":
73
- p80_idx = min(int(total * 0.8), total - 1)
74
- p50_idx = min(int(total * 0.5), total - 1)
75
- p20_idx = min(int(total * 0.2), total - 1)
76
- else:
77
- p80_idx = min(int(total * 0.25), total - 1)
78
- p50_idx = min(int(total * 0.5), total - 1)
79
- p20_idx = min(int(total * 0.75), total - 1)
80
- p80 = sorted_vals[p80_idx]
81
- p50 = sorted_vals[p50_idx]
82
- p20 = sorted_vals[p20_idx]
83
-
84
- def get_segment(val):
85
- if val >= p80:
86
- return "VIP"
87
- if val >= p50:
88
- return "High"
89
- if val >= p20:
90
- return "Medium"
91
- return "Low"
64
+ if method == "quartile":
65
+ values = sorted([float(r.get(total_key, 0) or 0) for r in result], reverse=True)
66
+ n = len(values)
67
+ p75 = values[min(int(n * 0.25), n - 1)]
68
+ p50 = values[min(int(n * 0.50), n - 1)]
69
+ p25 = values[min(int(n * 0.75), n - 1)]
92
70
 
93
71
  data = []
94
72
  for row in result:
95
- val = float(row.get(order_col, 0) or 0)
73
+ val = float(row.get(total_key, 0) or 0)
74
+ if method == "quartile":
75
+ if val >= p75:
76
+ segment = "VIP"
77
+ elif val >= p50:
78
+ segment = "战略"
79
+ elif val >= p25:
80
+ segment = "普通"
81
+ else:
82
+ segment = "低价值"
83
+ else:
84
+ share = val / total if total > 0 else 0
85
+ if share >= 0.6:
86
+ segment = "核心"
87
+ elif share >= 0.3:
88
+ segment = "重要"
89
+ else:
90
+ segment = "普通"
91
+
96
92
  data.append({
97
93
  "customer_id": str(row.get("customer_id", "")),
98
- "customer_region": str(row.get("customer_region", "")),
94
+ "customer_name": str(row.get("customer_name", "")),
95
+ "customer_type": str(row.get("customer_type", "")),
99
96
  "sales_amount": round(float(row.get("sales_amount", 0) or 0), 2),
100
97
  "quantity": int(row.get("quantity", 0) or 0),
101
- "segment": get_segment(val),
98
+ "order_count": int(row.get("order_count", 0) or 0),
99
+ "segment": segment,
102
100
  })
103
101
 
104
102
  return p.function_result(
105
- columns=["customer_id", "customer_region", "sales_amount", "quantity", "segment"],
103
+ columns=["customer_id", "customer_name", "customer_type", "sales_amount", "quantity", "order_count", "segment"],
106
104
  data=data,
107
105
  row_count=len(data),
108
106
  )
@@ -4,11 +4,8 @@
4
4
  返回:总销售额、总销量、订单数、客单价、动销 SKU 数
5
5
 
6
6
  发布:
7
- dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/functions/sales_fn_get_summary.py \\
8
- --space space__zlj --register-function-id sales.fn.get_summary
9
-
10
- 保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
11
- 见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_get_summary.py \
8
+ --space space__misc_01 --register-function-id sales.fn.get_summary
12
9
  """
13
10
 
14
11
  TEST_ARGUMENTS = {
@@ -41,7 +38,7 @@ def _ontology_fn_body(p):
41
38
  sum(quantity) AS total_quantity,
42
39
  uniq(order_id) AS order_count,
43
40
  uniq(product_id) AS product_count
44
- FROM sales_order_fact
41
+ FROM fact_sales_order_line
45
42
  {where_clause}
46
43
  """
47
44
 
@@ -4,11 +4,8 @@
4
4
  返回:月度销售额、销量、订单数及环比增长率
5
5
 
6
6
  发布:
7
- dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/functions/sales_fn_mom_analysis.py \\
8
- --space space__zlj --register-function-id sales.fn.mom_analysis
9
-
10
- 保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
11
- 见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_mom_analysis.py \
8
+ --space space__misc_01 --register-function-id sales.fn.mom_analysis
12
9
  """
13
10
 
14
11
  TEST_ARGUMENTS = {
@@ -33,20 +30,21 @@ def _ontology_fn_body(p):
33
30
 
34
31
  sql = f"""
35
32
  SELECT
36
- formatDateTime(order_date, '%Y-%m') AS year_month,
33
+ toYear(order_date) AS year,
34
+ toMonth(order_date) AS month,
37
35
  sum(sales_amount) AS sales_amount,
38
36
  sum(quantity) AS quantity,
39
37
  uniq(order_id) AS order_count
40
- FROM sales_order_fact
38
+ FROM fact_sales_order_line
41
39
  {where_clause}
42
- GROUP BY formatDateTime(order_date, '%Y-%m')
43
- ORDER BY year_month
40
+ GROUP BY toYear(order_date), toMonth(order_date)
41
+ ORDER BY year, month
44
42
  """
45
43
 
46
44
  result = p.sql.query(sql)
47
45
  if not result:
48
46
  return p.function_result(
49
- columns=["year_month", "sales_amount", "quantity", "order_count", "mom_growth"],
47
+ columns=["year", "month", "sales_amount", "quantity", "order_count", "mom_growth"],
50
48
  data=[],
51
49
  row_count=0,
52
50
  )
@@ -60,7 +58,8 @@ def _ontology_fn_body(p):
60
58
  else:
61
59
  mom_growth = 0.0
62
60
  data.append({
63
- "year_month": str(row.get("year_month", "")),
61
+ "year": int(row.get("year", 0)),
62
+ "month": int(row.get("month", 0)),
64
63
  "sales_amount": round(sales_amount, 2),
65
64
  "quantity": int(row.get("quantity", 0) or 0),
66
65
  "order_count": int(row.get("order_count", 0) or 0),
@@ -69,7 +68,7 @@ def _ontology_fn_body(p):
69
68
  prev_sales = sales_amount
70
69
 
71
70
  return p.function_result(
72
- columns=["year_month", "sales_amount", "quantity", "order_count", "mom_growth"],
71
+ columns=["year", "month", "sales_amount", "quantity", "order_count", "mom_growth"],
73
72
  data=data,
74
73
  row_count=len(data),
75
74
  )
@@ -4,11 +4,8 @@
4
4
  返回:各区域销售额、销量、订单数及占比
5
5
 
6
6
  发布:
7
- dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/functions/sales_fn_region_breakdown.py \\
8
- --space space__zlj --register-function-id sales.fn.region_breakdown
9
-
10
- 保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
11
- 见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_region_breakdown.py \
8
+ --space space__misc_01 --register-function-id sales.fn.region_breakdown
12
9
  """
13
10
 
14
11
  TEST_ARGUMENTS = {
@@ -37,7 +34,7 @@ def _ontology_fn_body(p):
37
34
  sum(sales_amount) AS sales_amount,
38
35
  sum(quantity) AS quantity,
39
36
  uniq(order_id) AS order_count
40
- FROM sales_order_fact
37
+ FROM fact_sales_order_line
41
38
  {where_clause}
42
39
  GROUP BY customer_region
43
40
  ORDER BY sales_amount DESC
@@ -52,15 +49,17 @@ def _ontology_fn_body(p):
52
49
  )
53
50
 
54
51
  total = sum(float(r.get("sales_amount", 0) or 0) for r in result)
52
+
55
53
  data = []
56
54
  for row in result:
57
55
  sales_amount = float(row.get("sales_amount", 0) or 0)
56
+ share_pct = sales_amount / total if total > 0 else 0.0
58
57
  data.append({
59
58
  "customer_region": str(row.get("customer_region", "")),
60
59
  "sales_amount": round(sales_amount, 2),
61
60
  "quantity": int(row.get("quantity", 0) or 0),
62
61
  "order_count": int(row.get("order_count", 0) or 0),
63
- "share_pct": round(sales_amount / total if total > 0 else 0.0, 4),
62
+ "share_pct": round(share_pct, 4),
64
63
  })
65
64
 
66
65
  return p.function_result(
@@ -4,11 +4,8 @@
4
4
  返回:产品排行及销售占比
5
5
 
6
6
  发布:
7
- dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/functions/sales_fn_top_products.py \\
8
- --space space__zlj --register-function-id sales.fn.top_products
9
-
10
- 保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
11
- 见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_top_products.py \
8
+ --space space__misc_01 --register-function-id sales.fn.top_products
12
9
  """
13
10
 
14
11
  TEST_ARGUMENTS = {
@@ -42,12 +39,12 @@ def _ontology_fn_body(p):
42
39
  sql = f"""
43
40
  SELECT
44
41
  f.product_id,
45
- any(pm.product_name) AS product_name,
42
+ any(dp.product_name) AS product_name,
46
43
  f.product_category,
47
44
  sum(f.sales_amount) AS sales_amount,
48
45
  sum(f.quantity) AS quantity
49
- FROM sales_order_fact AS f
50
- LEFT JOIN product_master AS pm ON f.product_id = pm.product_id
46
+ FROM fact_sales_order_line AS f
47
+ LEFT JOIN dim_product AS dp ON f.product_id = dp.product_id
51
48
  {where_clause}
52
49
  GROUP BY f.product_id, f.product_category
53
50
  ORDER BY {order_by}
@@ -4,11 +4,8 @@
4
4
  返回:年度销售额、销量、订单数及同比增长率
5
5
 
6
6
  发布:
7
- dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/functions/sales_fn_yoy_analysis.py \\
8
- --space space__zlj --register-function-id sales.fn.yoy_analysis
9
-
10
- 保存测试参数(function run 验证通过后;侧栏「运行函数」预填):
11
- 见 functions/README.md;推荐 save_test_arguments.ps1(CLI 须 ofn_xxx 内部 id,勿直接用 function_id)
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/functions/sales_fn_yoy_analysis.py \
8
+ --space space__misc_01 --register-function-id sales.fn.yoy_analysis
12
9
  """
13
10
 
14
11
  TEST_ARGUMENTS = {
@@ -37,7 +34,7 @@ def _ontology_fn_body(p):
37
34
  sum(sales_amount) AS sales_amount,
38
35
  sum(quantity) AS quantity,
39
36
  uniq(order_id) AS order_count
40
- FROM sales_order_fact
37
+ FROM fact_sales_order_line
41
38
  {where_clause}
42
39
  GROUP BY toYear(order_date)
43
40
  ORDER BY year
@@ -1,25 +1,38 @@
1
- # 批量保存函数 test_arguments 到平台(space__zlj)
2
- # 用法:在 dazi-work 根目录执行 .\项目\潘达石化\本体\ontos\产品销售本体方案\functions\save_test_arguments.ps1
1
+ # save_test_arguments.ps1
2
+ # 批量保存各函数的 test_arguments
3
+ # 用法:在 dazi-work 根目录执行 .\项目\DAZI_TEST\本体\ontos\销售本体示例\functions\save_test_arguments.ps1
3
4
 
4
- $ErrorActionPreference = "Stop"
5
- $space = "space__zlj"
6
- $base = "项目/潘达石化/本体/ontos/产品销售本体方案/functions/test_arguments"
5
+ $spaceId = "space__misc_01"
6
+ $itemPath = "项目/DAZI_TEST/本体/ontos/销售本体示例/functions"
7
7
 
8
- $raw = dazi onto function list --space $space 2>&1 | Out-String
9
- $jsonPart = ($raw -split '__JSON_SUMMARY__')[-1].Trim()
10
- $data = $jsonPart | ConvertFrom-Json
8
+ $functions = @(
9
+ @{fn_id="sales.fn.get_summary"; file="sales.fn.get_summary.json"},
10
+ @{fn_id="sales.fn.yoy_analysis"; file="sales.fn.yoy_analysis.json"},
11
+ @{fn_id="sales.fn.mom_analysis"; file="sales.fn.mom_analysis.json"},
12
+ @{fn_id="sales.fn.top_products"; file="sales.fn.top_products.json"},
13
+ @{fn_id="sales.fn.customer_segmentation"; file="sales.fn.customer_segmentation.json"},
14
+ @{fn_id="sales.fn.region_breakdown"; file="sales.fn.region_breakdown.json"},
15
+ @{fn_id="sales.fn.channel_mix"; file="sales.fn.channel_mix.json"}
16
+ )
11
17
 
12
- foreach ($fn in $data.data.functions) {
13
- $fid = $fn.function_id
14
- $ofn = $fn.id
15
- $file = Join-Path $base "$fid.json"
16
- if (-not (Test-Path $file)) {
17
- Write-Host "SKIP 无 JSON: $fid"
18
- continue
18
+ # 先获取 function list 获取 ofn_xxx 内部 id
19
+ Write-Host "获取函数列表..."
20
+ $fnList = & dazi onto function list --space $spaceId --output json 2>$null | ConvertFrom-Json
21
+
22
+ foreach ($fn in $functions) {
23
+ $fnId = $fn.fn_id
24
+ $fileName = $fn.file
25
+ $jsonPath = "$PSScriptRoot/test_arguments/$fileName"
26
+
27
+ # 查找 ofn_xxx 内部 id
28
+ $fnInfo = $fnList | Where-Object { $_.function_id -eq $fnId }
29
+ if ($fnInfo) {
30
+ $ofnId = $fnInfo.id
31
+ Write-Host "保存 $fnId (ofnId=$ofnId)..."
32
+ & dazi onto function save-test-arguments $ofnId --space $spaceId --arguments-json-file $jsonPath
33
+ } else {
34
+ Write-Host "函数 $fnId 未找到,请先发布函数"
19
35
  }
20
- Write-Host "save $fid ..."
21
- # 注意:CLI 须用平台内部 id(ofn_xxx),不能用 function_id 字符串
22
- dazi onto function save-test-arguments $ofn --space $space --arguments-json-file $file
23
36
  }
24
37
 
25
- Write-Host "完成。"
38
+ Write-Host "完成"
@@ -1,8 +1,5 @@
1
1
  {
2
2
  "v": 1,
3
- "arguments": {
4
- "start_date": "2025-01-01",
5
- "end_date": "2026-06-30"
6
- },
7
- "object_type_code": "SalesChannel"
8
- }
3
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
4
+ "object_type_code": "SalesAnalysis"
5
+ }
@@ -1,10 +1,5 @@
1
1
  {
2
2
  "v": 1,
3
- "arguments": {
4
- "metric": "sales_amount",
5
- "method": "quartile",
6
- "start_date": "2025-01-01",
7
- "end_date": "2026-06-30"
8
- },
3
+ "arguments": {"metric": "sales_amount", "method": "quartile", "start_date": "2025-01-01", "end_date": "2026-06-30"},
9
4
  "object_type_code": "Customer"
10
- }
5
+ }
@@ -1,8 +1,5 @@
1
1
  {
2
2
  "v": 1,
3
- "arguments": {
4
- "start_date": "2025-01-01",
5
- "end_date": "2026-06-30"
6
- },
3
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
7
4
  "object_type_code": "SalesAnalysis"
8
- }
5
+ }
@@ -1,8 +1,5 @@
1
1
  {
2
2
  "v": 1,
3
- "arguments": {
4
- "start_date": "2025-01-01",
5
- "end_date": "2026-06-30"
6
- },
3
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
7
4
  "object_type_code": "SalesAnalysis"
8
- }
5
+ }
@@ -1,8 +1,5 @@
1
1
  {
2
2
  "v": 1,
3
- "arguments": {
4
- "start_date": "2025-01-01",
5
- "end_date": "2026-06-30"
6
- },
3
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
7
4
  "object_type_code": "SalesAnalysis"
8
- }
5
+ }
@@ -1,10 +1,5 @@
1
1
  {
2
2
  "v": 1,
3
- "arguments": {
4
- "limit": 5,
5
- "metric": "sales_amount",
6
- "start_date": "2025-01-01",
7
- "end_date": "2026-06-30"
8
- },
3
+ "arguments": {"limit": 5, "metric": "sales_amount", "start_date": "2025-01-01", "end_date": "2026-06-30"},
9
4
  "object_type_code": "Product"
10
- }
5
+ }
@@ -1,8 +1,5 @@
1
1
  {
2
2
  "v": 1,
3
- "arguments": {
4
- "start_date": "2025-01-01",
5
- "end_date": "2026-06-30"
6
- },
3
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
7
4
  "object_type_code": "SalesAnalysis"
8
- }
5
+ }