@dazitech/cli 3.0.8 → 3.1.0

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 (79) 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 +7 -2
  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 +21 -2
  9. package/dist/docs/onto/dazi_script_sdk_reference.md +246 -178
  10. package/dist/docs/onto/function-guide.md +123 -95
  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 +169 -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/345/274/200/345/217/221/344/274/230/345/214/226/346/200/273/347/273/223.md +242 -0
  14. 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 +339 -249
  15. package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +304 -173
  16. package/dist/docs/onto//350/204/232/346/234/254/350/277/220/350/241/214/345/270/270/350/247/201/351/224/231/350/257/257/345/244/204/347/220/206.md +242 -0
  17. 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 +361 -238
  18. 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 +385 -300
  19. package/dist/examples/index.json +22 -16
  20. package/dist/examples/onto/README.md +13 -5
  21. package/dist/examples/onto/_templates/ontology_function_template.py +50 -0
  22. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_account_breakdown.py +62 -0
  23. 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
  24. 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
  25. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_get_summary.py +61 -0
  26. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_mom_analysis.py +82 -0
  27. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_top_accounts.py +61 -0
  28. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_yoy_analysis.py +79 -0
  29. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/save_test_arguments.ps1 +38 -0
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_category_mount.py +85 -0
  38. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +169 -74
  39. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +16 -13
  40. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +19 -16
  41. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +48 -50
  42. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +3 -6
  43. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +11 -12
  44. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +6 -7
  45. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +5 -8
  46. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +3 -6
  47. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +32 -19
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_category_mount.py +82 -0
  56. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +240 -155
  57. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +59 -20
  58. package/dist/prompts/index.json +8 -1
  59. package/dist/prompts/onto/function-design.md +73 -53
  60. package/dist/prompts/onto/planning-design.md +104 -0
  61. package/dist/prompts/onto/script-publish-run.md +229 -194
  62. package/package.json +1 -1
  63. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_account_breakdown.py +0 -99
  64. 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
  65. 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
  66. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_get_summary.py +0 -76
  67. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_mom_analysis.py +0 -86
  68. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_top_accounts.py +0 -103
  69. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_yoy_analysis.py +0 -86
  70. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/save_test_arguments.ps1 +0 -27
  71. 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
  72. 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
  73. 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
  74. 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
  75. 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
  76. 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
  77. 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
  78. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/README.md +0 -25
  79. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/README.md +0 -5
@@ -0,0 +1,79 @@
1
+ """同比分析函数
2
+
3
+ 功能:对比当期与去年同期的利润指标变化
4
+ 参数:start_date, end_date
5
+
6
+ 放置:项目/DAZI_TEST/本体/ontos/利润分析示例/functions/profit_fn_yoy_analysis.py
7
+ 发布:dazi onto script publish 项目/DAZI_TEST/本体/ontos/利润分析示例/functions/profit_fn_yoy_analysis.py --space space__misc_01 --register-function-id profit.fn.yoy_analysis
8
+ """
9
+
10
+ from datetime import datetime, timedelta
11
+ import json
12
+
13
+
14
+ def _ontology_fn_body(p):
15
+ start_date = p.get("start_date", "2025-01-01")
16
+ end_date = p.get("end_date", datetime.now().strftime("%Y-%m-%d"))
17
+
18
+ start_dt = datetime.strptime(start_date, "%Y-%m-%d")
19
+ end_dt = datetime.strptime(end_date, "%Y-%m-%d")
20
+
21
+ last_year_start = (start_dt - timedelta(days=365)).strftime("%Y-%m-%d")
22
+ last_year_end = (end_dt - timedelta(days=365)).strftime("%Y-%m-%d")
23
+
24
+ output.print(f"当期: {start_date} ~ {end_date}")
25
+ output.print(f"去年同期: {last_year_start} ~ {last_year_end}")
26
+
27
+ def get_period_data(sd, ed):
28
+ sql = """
29
+ SELECT
30
+ sumIf(amount_signed, account_type='收入') as revenue,
31
+ sumIf(amount_signed, account_type='成本') as cost,
32
+ sumIf(amount_signed, account_type='费用') as expense
33
+ FROM fact_gl_journal_entry
34
+ WHERE posting_date >= '{sd}' AND posting_date <= '{ed}'
35
+ """.format(sd=sd, ed=ed)
36
+ result = p.space.sql.query_one(sql)
37
+ return {
38
+ "revenue": result.get("revenue", 0) or 0,
39
+ "cost": result.get("cost", 0) or 0,
40
+ "expense": result.get("expense", 0) or 0,
41
+ }
42
+
43
+ current = get_period_data(start_date, end_date)
44
+ last_year = get_period_data(last_year_start, last_year_end)
45
+
46
+ current_profit = current["revenue"] - current["cost"] - current["expense"]
47
+ last_year_profit = last_year["revenue"] - last_year["cost"] - last_year["expense"]
48
+
49
+ def calc_yoy(current, last):
50
+ if last == 0:
51
+ return 0 if current == 0 else None
52
+ return (current - last) / last
53
+
54
+ result = {
55
+ "period": f"{start_date} ~ {end_date}",
56
+ "last_year_period": f"{last_year_start} ~ {last_year_end}",
57
+ "current": {
58
+ "revenue": round(current["revenue"], 2),
59
+ "cost": round(current["cost"], 2),
60
+ "expense": round(current["expense"], 2),
61
+ "operating_profit": round(current_profit, 2),
62
+ },
63
+ "last_year": {
64
+ "revenue": round(last_year["revenue"], 2),
65
+ "cost": round(last_year["cost"], 2),
66
+ "expense": round(last_year["expense"], 2),
67
+ "operating_profit": round(last_year_profit, 2),
68
+ },
69
+ "yoy": {
70
+ "revenue": round(calc_yoy(current["revenue"], last_year["revenue"]), 4) if calc_yoy(current["revenue"], last_year["revenue"]) is not None else None,
71
+ "cost": round(calc_yoy(current["cost"], last_year["cost"]), 4) if calc_yoy(current["cost"], last_year["cost"]) is not None else None,
72
+ "expense": round(calc_yoy(current["expense"], last_year["expense"]), 4) if calc_yoy(current["expense"], last_year["expense"]) is not None else None,
73
+ "operating_profit": round(calc_yoy(current_profit, last_year_profit), 4) if calc_yoy(current_profit, last_year_profit) is not None else None,
74
+ },
75
+ }
76
+
77
+ p.function_result(result)
78
+
79
+
@@ -0,0 +1,38 @@
1
+ # save_test_arguments.ps1
2
+ # 批量保存各函数的 test_arguments
3
+ # 用法:在 dazi-work 根目录执行 .\项目\DAZI_TEST\本体\ontos\利润分析示例\functions\save_test_arguments.ps1
4
+
5
+ $spaceId = "space__misc_01"
6
+ $itemPath = "项目/DAZI_TEST/本体/ontos/利润分析示例/functions"
7
+
8
+ $functions = @(
9
+ @{fn_id="profit.fn.get_summary"; file="profit.fn.get_summary.json"},
10
+ @{fn_id="profit.fn.yoy_analysis"; file="profit.fn.yoy_analysis.json"},
11
+ @{fn_id="profit.fn.mom_analysis"; file="profit.fn.mom_analysis.json"},
12
+ @{fn_id="profit.fn.budget_vs_actual"; file="profit.fn.budget_vs_actual.json"},
13
+ @{fn_id="profit.fn.account_breakdown"; file="profit.fn.account_breakdown.json"},
14
+ @{fn_id="profit.fn.cost_center_profit"; file="profit.fn.cost_center_profit.json"},
15
+ @{fn_id="profit.fn.top_accounts"; file="profit.fn.top_accounts.json"}
16
+ )
17
+
18
+ Write-Host "获取函数列表..."
19
+ $fnList = & dazi onto function list --space $spaceId 2>$null
20
+
21
+ foreach ($fn in $functions) {
22
+ $fnId = $fn.fn_id
23
+ $fileName = $fn.file
24
+ $jsonPath = "$PSScriptRoot/test_arguments/$fileName"
25
+
26
+ $ofnId = $fnList | Where-Object { $_ -match $fnId } | ForEach-Object {
27
+ if ($_ -match 'id:\s*(\S+)') { $matches[1] }
28
+ }
29
+
30
+ if ($ofnId) {
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 未找到,请先发布函数"
35
+ }
36
+ }
37
+
38
+ Write-Host "完成"
@@ -0,0 +1 @@
1
+ {"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30","account_type":"收入"},"object_type_code":"Account"}
@@ -0,0 +1 @@
1
+ {"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"BudgetAnalysis"}
@@ -0,0 +1 @@
1
+ {"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"CostCenter"}
@@ -0,0 +1 @@
1
+ {"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"ProfitAnalysis"}
@@ -0,0 +1 @@
1
+ {"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"ProfitAnalysis"}
@@ -0,0 +1 @@
1
+ {"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30","limit":10},"object_type_code":"Account"}
@@ -0,0 +1 @@
1
+ {"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"ProfitAnalysis"}
@@ -0,0 +1,85 @@
1
+ """利润分析本体 — 平台分类挂载(CATEGORY_REGISTRY)
2
+
3
+ 与灌数脚本类似,**独立步骤、放在实施流程最后**:
4
+ init → seed → 发布全部函数 → **本脚本**。
5
+
6
+ 放置:资源/examples/onto/利润示例/setup/profit_category_mount.py
7
+ 发布:dazi onto script publish <item-path>/setup/profit_category_mount.py --space <space-id> --type setup
8
+ 规划对照:资源/docs/onto/规划示例_利润分析本体方案.md 附录 B
9
+ """
10
+
11
+ import json
12
+
13
+ # 与规划附录 B 一一对应;category 值必须是平台标准分类中文名
14
+ CATEGORY_REGISTRY = {
15
+ "table": {
16
+ "维度表": ["dim_account", "dim_cost_center"],
17
+ "事实表": ["fact_gl_journal_entry", "fact_budget_entry"],
18
+ },
19
+ "cube": {
20
+ "流程型": ["ActualCube", "BudgetCube"],
21
+ "主体型": ["AccountActualCube", "CostCenterActualCube", "TimeActualCube"],
22
+ },
23
+ "object": {
24
+ "主数据": ["Account", "CostCenter"],
25
+ "事务": ["JournalEntry", "BudgetLine"],
26
+ "分析": ["ProfitAnalysis", "BudgetAnalysis"],
27
+ },
28
+ "relation": {
29
+ "时间关联": [
30
+ ("fact_gl_journal_entry", "dim_date"),
31
+ ("fact_budget_entry", "dim_date"),
32
+ ],
33
+ "主数据关联": [
34
+ ("fact_gl_journal_entry", "dim_account"),
35
+ ("fact_gl_journal_entry", "dim_cost_center"),
36
+ ("fact_budget_entry", "dim_account"),
37
+ ("fact_budget_entry", "dim_cost_center"),
38
+ ],
39
+ "层级自关联": [("dim_account", "dim_account")],
40
+ },
41
+ "link": {
42
+ "归属关系": [
43
+ "entry_belongs_account",
44
+ "entry_belongs_cost_center",
45
+ "budget_for_account",
46
+ "budget_for_cost_center",
47
+ ],
48
+ "分析归因": [
49
+ "analysis_by_account",
50
+ "analysis_by_cost_center",
51
+ "account_contributes_profit",
52
+ "cost_center_contributes_profit",
53
+ ],
54
+ "层级关系": ["account_has_parent"],
55
+ "对比关系": ["budget_compared_to_actual"],
56
+ },
57
+ "function": {
58
+ "总览分析": ["profit.fn.get_summary"],
59
+ "趋势分析": ["profit.fn.yoy_analysis", "profit.fn.mom_analysis"],
60
+ "结构分析": ["profit.fn.account_breakdown", "profit.fn.top_accounts"],
61
+ "预实分析": ["profit.fn.budget_vs_actual"],
62
+ "组织分析": ["profit.fn.cost_center_profit"],
63
+ },
64
+ }
65
+
66
+
67
+ def main():
68
+ space_id = "space__misc_01"
69
+ s = space.get(space_id)
70
+
71
+ output.print("=== 利润分析 — 平台分类挂载 ===")
72
+ output.print(f"空间: {space_id}")
73
+
74
+ cat_counts = s.categories.apply_registry(CATEGORY_REGISTRY, skip_missing=True)
75
+ for kind, cnt in cat_counts.items():
76
+ if cnt:
77
+ output.print(f"OK 分类[{kind}] 挂载 {cnt} 项")
78
+
79
+ summary = {
80
+ "ok": True,
81
+ "space_id": space_id,
82
+ "category_mounts": cat_counts,
83
+ }
84
+ output.success("平台分类挂载完成")
85
+ output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
@@ -1,32 +1,122 @@
1
- """利润分析本体初始化脚本 — space__zlj
1
+ """利润分析本体初始化脚本 — space__misc_01
2
2
 
3
3
  初始化内容:
4
- 1. 创建物理表(4 张:科目表、成本中心维表、实际分录表、预算表)
5
- 2. 注册表到空间
6
- 3. 注册表间关系(6 条)
7
- 4. 注册 Cube(5 个)及派生度量
8
- 5. 定义对象类型(6 种)、绑定数据源、属性、链接
4
+ 1. 创建物理表(科目表、成本中心维表、实际分录表、预算表)
5
+ 2. 注册表到空间(含 display_name / description)
6
+ 3. 注册表间关系(7条)
7
+ 4. 注册Cube(5个)及派生度量
8
+ 5. 定义对象类型(6种)、绑定数据源、属性、链接
9
9
  6. 同步指标引用
10
+ 7. 平台分类挂载见 profit_category_mount.py(init/seed/函数 publish 之后执行)
10
11
 
11
- 放置:项目/潘达石化/本体/ontos/利润分析本体方案/setup/profit_ontology_init.py
12
- 发布:dazi onto script publish 项目/潘达石化/本体/ontos/利润分析本体方案/setup/profit_ontology_init.py --space space__zlj --type setup
12
+ 放置:资源/examples/onto/利润示例/setup/profit_ontology_init.py(复制到项目 ontos/<实现名>/setup/)
13
+ 发布:dazi onto script publish <item-path>/setup/profit_ontology_init.py --space <space-id> --type setup
14
+ 规划对照:资源/docs/onto/规划示例_利润分析本体方案.md
13
15
  """
14
16
 
15
17
  import json
16
18
 
19
+ # 与 规划示例_利润分析本体方案.md §2.3、§3.x 对齐:display_name=侧栏显示名,description=业务说明
20
+ TABLE_REGISTRY = {
21
+ "dim_account": {
22
+ "display_name": "科目维表",
23
+ "description": "会计科目主数据",
24
+ "columns": [
25
+ {"name": "account_id", "display_name": "科目 ID", "description": "主键"},
26
+ {"name": "account_code", "display_name": "科目编码"},
27
+ {"name": "account_name", "display_name": "科目名称"},
28
+ {"name": "account_type", "display_name": "科目类型", "description": "资产/负债/权益/收入/成本/费用"},
29
+ {"name": "pl_category", "display_name": "损益大类"},
30
+ {"name": "parent_account_id", "display_name": "上级科目"},
31
+ {"name": "account_level", "display_name": "层级"},
32
+ {"name": "is_leaf", "display_name": "末级"},
33
+ {"name": "normal_balance", "display_name": "余额方向", "description": "借/贷"},
34
+ {"name": "status", "display_name": "状态", "description": "启用/停用"},
35
+ {"name": "created_at", "display_name": "创建时间"},
36
+ ],
37
+ },
38
+ "dim_cost_center": {
39
+ "display_name": "成本中心维表",
40
+ "description": "组织/利润中心主数据",
41
+ "columns": [
42
+ {"name": "cost_center_id", "display_name": "成本中心 ID", "description": "主键"},
43
+ {"name": "cost_center_code", "display_name": "编码"},
44
+ {"name": "cost_center_name", "display_name": "名称"},
45
+ {"name": "department", "display_name": "部门"},
46
+ {"name": "company_code", "display_name": "公司代码"},
47
+ {"name": "profit_center", "display_name": "利润中心"},
48
+ {"name": "status", "display_name": "状态"},
49
+ {"name": "created_at", "display_name": "创建时间"},
50
+ ],
51
+ },
52
+ "fact_gl_journal_entry": {
53
+ "display_name": "总账实际分录",
54
+ "description": "凭证行粒度损益流水",
55
+ "columns": [
56
+ {"name": "entry_id", "display_name": "凭证 ID"},
57
+ {"name": "line_id", "display_name": "凭证行 ID"},
58
+ {"name": "date_key", "display_name": "日期键", "description": "关联 dim_date,YYYYMMDD"},
59
+ {"name": "posting_date", "display_name": "记账日期"},
60
+ {"name": "fiscal_year", "display_name": "会计年度"},
61
+ {"name": "fiscal_period", "display_name": "会计期间"},
62
+ {"name": "account_id", "display_name": "科目 ID", "description": "关联 dim_account"},
63
+ {"name": "account_code", "display_name": "科目编码", "description": "冗余"},
64
+ {"name": "account_name", "display_name": "科目名称", "description": "冗余"},
65
+ {"name": "account_type", "display_name": "科目类型", "description": "冗余"},
66
+ {"name": "pl_category", "display_name": "损益大类", "description": "冗余"},
67
+ {"name": "account_level", "display_name": "科目层级", "description": "冗余"},
68
+ {"name": "cost_center_id", "display_name": "成本中心 ID", "description": "关联 dim_cost_center"},
69
+ {"name": "cost_center_name", "display_name": "成本中心", "description": "冗余"},
70
+ {"name": "department", "display_name": "部门", "description": "冗余"},
71
+ {"name": "profit_center", "display_name": "利润中心", "description": "冗余"},
72
+ {"name": "debit_amount", "display_name": "借方"},
73
+ {"name": "credit_amount", "display_name": "贷方"},
74
+ {"name": "amount_signed", "display_name": "损益金额", "description": "收入为正、成本费用为负"},
75
+ {"name": "currency", "display_name": "币种"},
76
+ {"name": "voucher_no", "display_name": "凭证号"},
77
+ {"name": "source_system", "display_name": "来源系统"},
78
+ {"name": "description", "display_name": "摘要", "description": "凭证行摘要文本"},
79
+ {"name": "created_at", "display_name": "创建时间"},
80
+ ],
81
+ },
82
+ "fact_budget_entry": {
83
+ "display_name": "预算明细",
84
+ "description": "预算行粒度编制数据",
85
+ "columns": [
86
+ {"name": "budget_id", "display_name": "预算批次"},
87
+ {"name": "line_id", "display_name": "预算行 ID"},
88
+ {"name": "date_key", "display_name": "日期键", "description": "关联 dim_date"},
89
+ {"name": "budget_version", "display_name": "预算版本"},
90
+ {"name": "fiscal_year", "display_name": "预算年度"},
91
+ {"name": "fiscal_period", "display_name": "预算期间", "description": "1-12"},
92
+ {"name": "account_id", "display_name": "科目 ID", "description": "关联 dim_account"},
93
+ {"name": "account_code", "display_name": "科目编码", "description": "冗余"},
94
+ {"name": "account_name", "display_name": "科目名称", "description": "冗余"},
95
+ {"name": "account_type", "display_name": "科目类型", "description": "冗余"},
96
+ {"name": "pl_category", "display_name": "损益大类", "description": "冗余"},
97
+ {"name": "cost_center_id", "display_name": "成本中心 ID", "description": "关联 dim_cost_center"},
98
+ {"name": "cost_center_name", "display_name": "成本中心", "description": "冗余"},
99
+ {"name": "department", "display_name": "部门", "description": "冗余"},
100
+ {"name": "budget_amount", "display_name": "预算金额"},
101
+ {"name": "currency", "display_name": "币种"},
102
+ {"name": "status", "display_name": "状态", "description": "草稿/已发布"},
103
+ {"name": "created_at", "display_name": "创建时间"},
104
+ ],
105
+ },
106
+ }
17
107
 
18
108
  def main():
19
- space_id = "space__zlj"
109
+ space_id = "space__misc_01"
20
110
  s = space.get(space_id)
21
111
 
22
112
  output.print("=== 利润分析本体初始化 ===")
23
113
  output.print(f"空间: {space_id}")
24
114
 
25
115
  # 1. 创建物理表
26
- output.print("\n[1/9] 创建物理表...")
116
+ output.print("\n[1/10] 创建物理表...")
27
117
 
28
118
  s.sql.execute("""
29
- CREATE TABLE IF NOT EXISTS account_master (
119
+ CREATE TABLE IF NOT EXISTS dim_account (
30
120
  account_id String,
31
121
  account_code String,
32
122
  account_name String,
@@ -41,10 +131,10 @@ def main():
41
131
  ) ENGINE = MergeTree()
42
132
  ORDER BY (account_code)
43
133
  """)
44
- output.print("OK account_master")
134
+ output.print("OK dim_account")
45
135
 
46
136
  s.sql.execute("""
47
- CREATE TABLE IF NOT EXISTS cost_center_dimension (
137
+ CREATE TABLE IF NOT EXISTS dim_cost_center (
48
138
  cost_center_id String,
49
139
  cost_center_code String,
50
140
  cost_center_name String,
@@ -56,12 +146,13 @@ def main():
56
146
  ) ENGINE = MergeTree()
57
147
  ORDER BY (cost_center_id)
58
148
  """)
59
- output.print("OK cost_center_dimension")
149
+ output.print("OK dim_cost_center")
60
150
 
61
151
  s.sql.execute("""
62
- CREATE TABLE IF NOT EXISTS actual_journal_entry (
152
+ CREATE TABLE IF NOT EXISTS fact_gl_journal_entry (
63
153
  entry_id String,
64
154
  line_id String,
155
+ date_key Int32,
65
156
  posting_date Date,
66
157
  fiscal_year Int32,
67
158
  fiscal_period Int32,
@@ -84,14 +175,15 @@ def main():
84
175
  description String,
85
176
  created_at DateTime DEFAULT now()
86
177
  ) ENGINE = MergeTree()
87
- ORDER BY (posting_date, entry_id, line_id)
178
+ ORDER BY (date_key, entry_id, line_id)
88
179
  """)
89
- output.print("OK actual_journal_entry")
180
+ output.print("OK fact_gl_journal_entry")
90
181
 
91
182
  s.sql.execute("""
92
- CREATE TABLE IF NOT EXISTS budget_entry (
183
+ CREATE TABLE IF NOT EXISTS fact_budget_entry (
93
184
  budget_id String,
94
185
  line_id String,
186
+ date_key Int32,
95
187
  budget_version String,
96
188
  fiscal_year Int32,
97
189
  fiscal_period Int32,
@@ -110,88 +202,91 @@ def main():
110
202
  ) ENGINE = MergeTree()
111
203
  ORDER BY (fiscal_year, fiscal_period, account_id, line_id)
112
204
  """)
113
- output.print("OK budget_entry")
114
-
115
- # 2. 注册表
116
- output.print("\n[2/9] 注册表到空间...")
117
-
118
- for tbl, label in [
119
- ("account_master", "会计科目主数据表"),
120
- ("cost_center_dimension", "成本中心维表"),
121
- ("actual_journal_entry", "实际分录事实表"),
122
- ("budget_entry", "预算事实表"),
123
- ]:
124
- s.tables.register(tbl, label=label)
125
- s.tables.sync_columns(tbl)
126
- output.print(f"OK {tbl}")
205
+ output.print("OK fact_budget_entry")
206
+
207
+ # 2. 注册表(含 display_name / description)
208
+ output.print("\n[2/10] 注册表到空间...")
209
+
210
+ for tbl_name, meta in TABLE_REGISTRY.items():
211
+ s.tables.register_with_meta(
212
+ table_name=tbl_name,
213
+ display_name=meta["display_name"],
214
+ description=meta.get("description"),
215
+ columns=meta["columns"],
216
+ force_column_meta=True,
217
+ )
218
+ output.print(f"OK {tbl_name} ({meta['display_name']})")
127
219
 
128
220
  # 3. 注册表间关系
129
- output.print("\n[3/9] 注册表间关系...")
221
+ output.print("\n[3/10] 注册表间关系...")
130
222
 
131
223
  table_relationships = [
132
224
  {
133
- "from_table": "actual_journal_entry",
134
- "to_table": "account_master",
135
- "join_sql": "actual_journal_entry.account_id = account_master.account_id",
225
+ "from_table": "fact_gl_journal_entry",
226
+ "to_table": "dim_date",
227
+ "join_sql": "fact_gl_journal_entry.date_key = dim_date.date_key",
228
+ "join_keys": [{"from": "date_key", "to": "date_key"}],
229
+ "relationship_type": "many_to_one",
230
+ "description": "分录关联日历",
231
+ },
232
+ {
233
+ "from_table": "fact_budget_entry",
234
+ "to_table": "dim_date",
235
+ "join_sql": "fact_budget_entry.date_key = dim_date.date_key",
236
+ "join_keys": [{"from": "date_key", "to": "date_key"}],
237
+ "relationship_type": "many_to_one",
238
+ "description": "预算关联日历",
239
+ },
240
+ {
241
+ "from_table": "fact_gl_journal_entry",
242
+ "to_table": "dim_account",
243
+ "join_sql": "fact_gl_journal_entry.account_id = dim_account.account_id",
136
244
  "join_keys": [{"from": "account_id", "to": "account_id"}],
137
245
  "relationship_type": "many_to_one",
138
246
  "description": "实际分录关联会计科目",
139
247
  },
140
248
  {
141
- "from_table": "actual_journal_entry",
142
- "to_table": "cost_center_dimension",
143
- "join_sql": "actual_journal_entry.cost_center_id = cost_center_dimension.cost_center_id",
249
+ "from_table": "fact_gl_journal_entry",
250
+ "to_table": "dim_cost_center",
251
+ "join_sql": "fact_gl_journal_entry.cost_center_id = dim_cost_center.cost_center_id",
144
252
  "join_keys": [{"from": "cost_center_id", "to": "cost_center_id"}],
145
253
  "relationship_type": "many_to_one",
146
254
  "description": "实际分录关联成本中心",
147
255
  },
148
256
  {
149
- "from_table": "budget_entry",
150
- "to_table": "account_master",
151
- "join_sql": "budget_entry.account_id = account_master.account_id",
257
+ "from_table": "fact_budget_entry",
258
+ "to_table": "dim_account",
259
+ "join_sql": "fact_budget_entry.account_id = dim_account.account_id",
152
260
  "join_keys": [{"from": "account_id", "to": "account_id"}],
153
261
  "relationship_type": "many_to_one",
154
262
  "description": "预算关联会计科目",
155
263
  },
156
264
  {
157
- "from_table": "budget_entry",
158
- "to_table": "cost_center_dimension",
159
- "join_sql": "budget_entry.cost_center_id = cost_center_dimension.cost_center_id",
265
+ "from_table": "fact_budget_entry",
266
+ "to_table": "dim_cost_center",
267
+ "join_sql": "fact_budget_entry.cost_center_id = dim_cost_center.cost_center_id",
160
268
  "join_keys": [{"from": "cost_center_id", "to": "cost_center_id"}],
161
269
  "relationship_type": "many_to_one",
162
270
  "description": "预算关联成本中心",
163
271
  },
164
272
  {
165
- "from_table": "account_master",
166
- "to_table": "account_master",
167
- "join_sql": "account_master.parent_account_id = account_master.account_id",
273
+ "from_table": "dim_account",
274
+ "to_table": "dim_account",
275
+ "join_sql": "dim_account.parent_account_id = dim_account.account_id",
168
276
  "join_keys": [{"from": "parent_account_id", "to": "account_id"}],
169
277
  "relationship_type": "many_to_one",
170
278
  "description": "科目上级(树形)",
171
279
  },
172
- {
173
- "from_table": "budget_entry",
174
- "to_table": "actual_journal_entry",
175
- "join_sql": "budget_entry.account_id = actual_journal_entry.account_id AND budget_entry.cost_center_id = actual_journal_entry.cost_center_id AND budget_entry.fiscal_year = actual_journal_entry.fiscal_year AND budget_entry.fiscal_period = actual_journal_entry.fiscal_period",
176
- "join_keys": [
177
- {"from": "account_id", "to": "account_id"},
178
- {"from": "cost_center_id", "to": "cost_center_id"},
179
- {"from": "fiscal_year", "to": "fiscal_year"},
180
- {"from": "fiscal_period", "to": "fiscal_period"},
181
- ],
182
- "relationship_type": "many_to_one",
183
- "description": "预算与实际同维对齐",
184
- },
185
280
  ]
186
281
  for rel in table_relationships:
187
282
  rid = s.tables.add_relationship(**rel)
188
- output.print(f"OK {rel['from_table']} -> {rel['to_table']} ({rid})")
283
+ output.print(f"OK {rel['from_table']} -> {rel['to_table']}")
189
284
 
190
285
  # 4. 注册 Cube
191
- output.print("\n[4/9] 注册 Cube...")
286
+ output.print("\n[4/10] 注册 Cube...")
192
287
 
193
- actual = "actual_journal_entry"
194
- budget = "budget_entry"
288
+ actual = "fact_gl_journal_entry"
289
+ budget = "fact_budget_entry"
195
290
 
196
291
  s.register_cube(
197
292
  name="ActualCube",
@@ -206,6 +301,7 @@ def main():
206
301
  dimensions=[
207
302
  {"name": "entry_id", "col": "entry_id", "type": "string", "title": "凭证ID"},
208
303
  {"name": "line_id", "col": "line_id", "type": "string", "title": "行ID"},
304
+ {"name": "date_key", "col": "date_key", "type": "int", "title": "日期键"},
209
305
  {"name": "posting_date", "col": "posting_date", "type": "date", "title": "记账日期"},
210
306
  {"name": "fiscal_year", "col": "fiscal_year", "type": "int", "title": "会计年度"},
211
307
  {"name": "fiscal_period", "col": "fiscal_period", "type": "int", "title": "会计期间"},
@@ -294,17 +390,16 @@ def main():
294
390
  {"name": "line_count", "col": "line_id", "agg": "count", "title": "分录行数"},
295
391
  ],
296
392
  dimensions=[
393
+ {"name": "date_key", "col": "date_key", "type": "int", "title": "日期键"},
297
394
  {"name": "posting_date", "col": "posting_date", "type": "date", "title": "记账日期"},
298
395
  {"name": "fiscal_year", "col": "fiscal_year", "type": "int", "title": "会计年度"},
299
396
  {"name": "fiscal_period", "col": "fiscal_period", "type": "int", "title": "会计期间"},
300
- {"name": "year_month", "col": "posting_date", "type": "string", "title": "年月"},
301
- {"name": "quarter", "col": "posting_date", "type": "string", "title": "季度"},
302
397
  ],
303
398
  )
304
399
  output.print("OK TimeActualCube")
305
400
 
306
401
  # 5. 派生度量
307
- output.print("\n[5/9] 配置派生度量...")
402
+ output.print("\n[5/10] 配置派生度量...")
308
403
 
309
404
  def _pl_measures(cube_name, full=False):
310
405
  base = [
@@ -375,7 +470,7 @@ def main():
375
470
  output.print("OK 派生度量")
376
471
 
377
472
  # 6. 对象类型
378
- output.print("\n[6/9] 定义对象类型...")
473
+ output.print("\n[6/10] 定义对象类型...")
379
474
 
380
475
  object_types = [
381
476
  ("Account", "会计科目", "会计科目业务对象"),
@@ -390,7 +485,7 @@ def main():
390
485
  output.print(f"OK {code}")
391
486
 
392
487
  # 7. 绑定数据源
393
- output.print("\n[7/9] 绑定数据源...")
488
+ output.print("\n[7/10] 绑定数据源...")
394
489
 
395
490
  bindings = [
396
491
  ("Account", "AccountActualCube"),
@@ -405,7 +500,7 @@ def main():
405
500
  output.print(f"OK {obj} -> {cube}")
406
501
 
407
502
  # 8. 属性
408
- output.print("\n[8/9] 定义属性...")
503
+ output.print("\n[8/10] 定义属性...")
409
504
 
410
505
  def define_props(obj_code, props):
411
506
  for code, name, role, qn in props:
@@ -478,7 +573,7 @@ def main():
478
573
  output.print("OK BudgetAnalysis 属性 (6)")
479
574
 
480
575
  # 9. 链接类型
481
- output.print("\n[9/9] 定义链接与同步指标...")
576
+ output.print("\n[9/10] 定义链接与同步指标...")
482
577
 
483
578
  link_types = [
484
579
  ("entry_belongs_account", "分录归属科目", "JournalEntry", "Account", "分录行对应科目"),
@@ -509,7 +604,7 @@ def main():
509
604
  "ok": True,
510
605
  "space_id": space_id,
511
606
  "tables": 4,
512
- "table_relationships": 6,
607
+ "table_relationships": 7,
513
608
  "cubes": 5,
514
609
  "object_types": 6,
515
610
  "properties": 42,
@@ -518,4 +613,4 @@ def main():
518
613
 
519
614
  output.print("\n=== 利润分析本体初始化完成 ===")
520
615
  output.success("初始化成功")
521
- output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
616
+ output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))