@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,32 +1,175 @@
1
- """产品销售本体初始化脚本 — space__zlj
1
+ """产品销售本体初始化脚本 — space__misc_01
2
2
 
3
3
  初始化内容:
4
- 1. 创建物理表(4 张:产品表、销售表、客户维表、渠道维表)
4
+ 1. 创建物理表(dim_date + 3 维表 + 1 事实表)
5
5
  2. 注册表到空间
6
- 3. 注册表间关系(3 条:事实表维表)
7
- 4. 注册 Cube(5 个)及派生度量
6
+ 3. 注册表间关系(4 条,含 fact dim_date)
7
+ 4. 注册 Cube(4 个)及派生度量
8
8
  5. 定义对象类型(5 种)、绑定数据源、属性、链接
9
9
  6. 同步指标引用
10
+ 7. 配置 347 对齐分类(ads_categories + 桥表)
10
11
 
11
- 放置:项目/潘达石化/本体/ontos/产品销售本体方案/setup/sales_ontology_init.py
12
- 发布:dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/setup/sales_ontology_init.py --space space__zlj --type setup
12
+ 放置:资源/examples/onto/销售示例/setup/sales_ontology_init.py(复制到项目 ontos/<实现名>/setup/)
13
+ 发布:dazi onto script publish <item-path>/setup/sales_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_date": {
22
+ "display_name": "日期维表",
23
+ "description": "全空间共享日历,事实表通过 date_key 关联",
24
+ "columns": [
25
+ {"name": "date_key", "display_name": "日期键", "description": "YYYYMMDD,主键"},
26
+ {"name": "calendar_date", "display_name": "自然日"},
27
+ {"name": "year", "display_name": "公历年"},
28
+ {"name": "quarter", "display_name": "季度", "description": "1-4"},
29
+ {"name": "month", "display_name": "月", "description": "1-12"},
30
+ {"name": "week_of_year", "display_name": "周"},
31
+ {"name": "day_of_week", "display_name": "星期"},
32
+ {"name": "is_weekend", "display_name": "是否周末", "description": "0/1"},
33
+ {"name": "year_month", "display_name": "年月", "description": "如 2025-06"},
34
+ ],
35
+ },
36
+ "dim_product": {
37
+ "display_name": "产品维表",
38
+ "description": "可售产品主数据",
39
+ "columns": [
40
+ {"name": "product_id", "display_name": "产品 ID", "description": "主键"},
41
+ {"name": "product_code", "display_name": "产品编码"},
42
+ {"name": "product_name", "display_name": "产品名称"},
43
+ {"name": "product_category", "display_name": "产品大类"},
44
+ {"name": "product_subcategory", "display_name": "产品小类"},
45
+ {"name": "brand", "display_name": "品牌"},
46
+ {"name": "unit", "display_name": "计量单位"},
47
+ {"name": "list_price", "display_name": "挂牌价"},
48
+ {"name": "cost_price", "display_name": "成本单价"},
49
+ {"name": "status", "display_name": "状态", "description": "在售/停售"},
50
+ {"name": "created_at", "display_name": "创建时间"},
51
+ {"name": "updated_at", "display_name": "更新时间"},
52
+ ],
53
+ },
54
+ "dim_customer": {
55
+ "display_name": "客户维表",
56
+ "description": "购买方主数据",
57
+ "columns": [
58
+ {"name": "customer_id", "display_name": "客户 ID", "description": "主键"},
59
+ {"name": "customer_code", "display_name": "客户编码"},
60
+ {"name": "customer_name", "display_name": "客户名称"},
61
+ {"name": "customer_region", "display_name": "销售区域"},
62
+ {"name": "customer_type", "display_name": "客户类型"},
63
+ {"name": "industry", "display_name": "所属行业"},
64
+ {"name": "created_at", "display_name": "创建时间"},
65
+ ],
66
+ },
67
+ "dim_channel": {
68
+ "display_name": "渠道维表",
69
+ "description": "销售渠道主数据",
70
+ "columns": [
71
+ {"name": "channel_id", "display_name": "渠道 ID", "description": "主键"},
72
+ {"name": "channel_code", "display_name": "渠道编码"},
73
+ {"name": "channel_name", "display_name": "渠道名称"},
74
+ {"name": "channel_type", "display_name": "渠道类型"},
75
+ ],
76
+ },
77
+ "fact_sales_order_line": {
78
+ "display_name": "销售订单行事实表",
79
+ "description": "订单行粒度销售流水",
80
+ "columns": [
81
+ {"name": "order_id", "display_name": "订单 ID"},
82
+ {"name": "order_line_id", "display_name": "订单行 ID", "description": "主键组合"},
83
+ {"name": "date_key", "display_name": "日期键", "description": "关联 dim_date"},
84
+ {"name": "order_date", "display_name": "订单日期"},
85
+ {"name": "product_id", "display_name": "产品 ID", "description": "关联 dim_product"},
86
+ {"name": "product_category", "display_name": "产品大类", "description": "冗余"},
87
+ {"name": "customer_id", "display_name": "客户 ID", "description": "关联 dim_customer"},
88
+ {"name": "customer_region", "display_name": "销售区域", "description": "冗余"},
89
+ {"name": "customer_type", "display_name": "客户类型", "description": "冗余"},
90
+ {"name": "channel_id", "display_name": "渠道 ID", "description": "关联 dim_channel"},
91
+ {"name": "quantity", "display_name": "销售数量"},
92
+ {"name": "unit_price", "display_name": "成交单价"},
93
+ {"name": "discount_amount", "display_name": "折扣金额"},
94
+ {"name": "sales_amount", "display_name": "销售金额"},
95
+ {"name": "currency", "display_name": "币种"},
96
+ {"name": "order_status", "display_name": "订单状态"},
97
+ ],
98
+ },
99
+ }
100
+
101
+ # 与 347 + 附录 B 对齐;category 值必须是 347 标准中文名
102
+ CATEGORY_REGISTRY = {
103
+ "table": {
104
+ "时间维": ["dim_date"],
105
+ "维度表": ["dim_product", "dim_customer", "dim_channel"],
106
+ "事实表": ["fact_sales_order_line"],
107
+ },
108
+ "cube": {
109
+ "流程型": ["SalesCube"],
110
+ "主体型": ["ProductSalesCube", "CustomerSalesCube", "ChannelSalesCube"],
111
+ },
112
+ "object": {
113
+ "主数据": ["Product", "Customer"],
114
+ "参考": ["SalesChannel"],
115
+ "事务": ["SalesOrder"],
116
+ "分析": ["SalesAnalysis"],
117
+ },
118
+ "relation": {
119
+ "时间关联": [("fact_sales_order_line", "dim_date")],
120
+ "主数据关联": [
121
+ ("fact_sales_order_line", "dim_product"),
122
+ ("fact_sales_order_line", "dim_customer"),
123
+ ("fact_sales_order_line", "dim_channel"),
124
+ ],
125
+ },
126
+ "link": {
127
+ "归属关系": [
128
+ "order_contains_product",
129
+ "order_belongs_customer",
130
+ "order_via_channel",
131
+ "product_has_orders",
132
+ "customer_places_orders",
133
+ ],
134
+ "分析归因": [
135
+ "analysis_by_product",
136
+ "analysis_by_customer",
137
+ "analysis_by_channel",
138
+ ],
139
+ },
140
+ }
141
+
17
142
 
18
143
  def main():
19
- space_id = "space__zlj"
144
+ space_id = "space__misc_01"
20
145
  s = space.get(space_id)
21
146
 
22
147
  output.print("=== 产品销售本体初始化 ===")
23
148
  output.print(f"空间: {space_id}")
24
149
 
25
150
  # 1. 创建物理表
26
- output.print("\n[1/9] 创建物理表...")
151
+ output.print("\n[1/10] 创建物理表...")
152
+
153
+ # dim_date(强制时间维)
154
+ s.sql.execute("""
155
+ CREATE TABLE IF NOT EXISTS dim_date (
156
+ date_key Int32,
157
+ calendar_date Date,
158
+ year Int16,
159
+ quarter Int8,
160
+ month Int8,
161
+ week_of_year Int8,
162
+ day_of_week Int8,
163
+ is_weekend UInt8,
164
+ year_month String
165
+ ) ENGINE = MergeTree()
166
+ ORDER BY (date_key)
167
+ """)
168
+ output.print("OK dim_date")
27
169
 
170
+ # dim_product
28
171
  s.sql.execute("""
29
- CREATE TABLE IF NOT EXISTS product_master (
172
+ CREATE TABLE IF NOT EXISTS dim_product (
30
173
  product_id String,
31
174
  product_code String,
32
175
  product_name String,
@@ -42,10 +185,11 @@ def main():
42
185
  ) ENGINE = MergeTree()
43
186
  ORDER BY (product_id)
44
187
  """)
45
- output.print("OK product_master")
188
+ output.print("OK dim_product")
46
189
 
190
+ # dim_customer
47
191
  s.sql.execute("""
48
- CREATE TABLE IF NOT EXISTS customer_dimension (
192
+ CREATE TABLE IF NOT EXISTS dim_customer (
49
193
  customer_id String,
50
194
  customer_code String,
51
195
  customer_name String,
@@ -56,10 +200,11 @@ def main():
56
200
  ) ENGINE = MergeTree()
57
201
  ORDER BY (customer_id)
58
202
  """)
59
- output.print("OK customer_dimension")
203
+ output.print("OK dim_customer")
60
204
 
205
+ # dim_channel
61
206
  s.sql.execute("""
62
- CREATE TABLE IF NOT EXISTS channel_dimension (
207
+ CREATE TABLE IF NOT EXISTS dim_channel (
63
208
  channel_id String,
64
209
  channel_code String,
65
210
  channel_name String,
@@ -67,12 +212,14 @@ def main():
67
212
  ) ENGINE = MergeTree()
68
213
  ORDER BY (channel_id)
69
214
  """)
70
- output.print("OK channel_dimension")
215
+ output.print("OK dim_channel")
71
216
 
217
+ # fact_sales_order_line(含 date_key)
72
218
  s.sql.execute("""
73
- CREATE TABLE IF NOT EXISTS sales_order_fact (
219
+ CREATE TABLE IF NOT EXISTS fact_sales_order_line (
74
220
  order_id String,
75
221
  order_line_id String,
222
+ date_key Int32,
76
223
  order_date Date,
77
224
  product_id String,
78
225
  product_category String,
@@ -87,60 +234,68 @@ def main():
87
234
  currency String,
88
235
  order_status String
89
236
  ) ENGINE = MergeTree()
90
- ORDER BY (order_date, order_id, order_line_id)
237
+ ORDER BY (date_key, order_id, order_line_id)
91
238
  """)
92
- output.print("OK sales_order_fact")
93
-
94
- # 2. 注册表
95
- output.print("\n[2/9] 注册表到空间...")
96
-
97
- for tbl, label in [
98
- ("product_master", "产品主数据表"),
99
- ("customer_dimension", "客户维度表"),
100
- ("channel_dimension", "销售渠道表"),
101
- ("sales_order_fact", "销售订单事实表"),
102
- ]:
103
- s.tables.register(tbl, label=label)
104
- s.tables.sync_columns(tbl)
105
- output.print(f"OK {tbl}")
239
+ output.print("OK fact_sales_order_line")
240
+
241
+ # 2. 注册表(含 display_name / description)
242
+ output.print("\n[2/10] 注册表到空间...")
243
+
244
+ for tbl_name, meta in TABLE_REGISTRY.items():
245
+ s.tables.register_with_meta(
246
+ table_name=tbl_name,
247
+ display_name=meta["display_name"],
248
+ description=meta.get("description"),
249
+ columns=meta["columns"],
250
+ force_column_meta=True,
251
+ )
252
+ output.print(f"OK {tbl_name} ({meta['display_name']})")
106
253
 
107
- # 3. 注册表间关系(数据空间「表间关系」,供 JOIN / 侧栏展示)
108
- output.print("\n[3/9] 注册表间关系...")
254
+ # 3. 注册表间关系(4 条,含 fact → dim_date)
255
+ output.print("\n[3/10] 注册表间关系...")
109
256
 
110
257
  table_relationships = [
111
258
  {
112
- "from_table": "sales_order_fact",
113
- "to_table": "product_master",
114
- "join_sql": "sales_order_fact.product_id = product_master.product_id",
259
+ "from_table": "fact_sales_order_line",
260
+ "to_table": "dim_date",
261
+ "join_sql": "fact_sales_order_line.date_key = dim_date.date_key",
262
+ "join_keys": [{"from": "date_key", "to": "date_key"}],
263
+ "relationship_type": "many_to_one",
264
+ "description": "销售订单行关联日期",
265
+ },
266
+ {
267
+ "from_table": "fact_sales_order_line",
268
+ "to_table": "dim_product",
269
+ "join_sql": "fact_sales_order_line.product_id = dim_product.product_id",
115
270
  "join_keys": [{"from": "product_id", "to": "product_id"}],
116
271
  "relationship_type": "many_to_one",
117
- "description": "销售订单行关联产品主数据",
272
+ "description": "销售订单行关联产品",
118
273
  },
119
274
  {
120
- "from_table": "sales_order_fact",
121
- "to_table": "customer_dimension",
122
- "join_sql": "sales_order_fact.customer_id = customer_dimension.customer_id",
275
+ "from_table": "fact_sales_order_line",
276
+ "to_table": "dim_customer",
277
+ "join_sql": "fact_sales_order_line.customer_id = dim_customer.customer_id",
123
278
  "join_keys": [{"from": "customer_id", "to": "customer_id"}],
124
279
  "relationship_type": "many_to_one",
125
280
  "description": "销售订单行关联客户",
126
281
  },
127
282
  {
128
- "from_table": "sales_order_fact",
129
- "to_table": "channel_dimension",
130
- "join_sql": "sales_order_fact.channel_id = channel_dimension.channel_id",
283
+ "from_table": "fact_sales_order_line",
284
+ "to_table": "dim_channel",
285
+ "join_sql": "fact_sales_order_line.channel_id = dim_channel.channel_id",
131
286
  "join_keys": [{"from": "channel_id", "to": "channel_id"}],
132
287
  "relationship_type": "many_to_one",
133
- "description": "销售订单行关联销售渠道",
288
+ "description": "销售订单行关联渠道",
134
289
  },
135
290
  ]
136
291
  for rel in table_relationships:
137
292
  rid = s.tables.add_relationship(**rel)
138
293
  output.print(f"OK {rel['from_table']} -> {rel['to_table']} ({rid})")
139
294
 
140
- # 4. 注册 Cube
141
- output.print("\n[4/9] 注册 Cube...")
295
+ # 4. 注册 Cube(4 个)
296
+ output.print("\n[4/10] 注册 Cube...")
142
297
 
143
- fact = "sales_order_fact"
298
+ fact = "fact_sales_order_line"
144
299
  common_measures = [
145
300
  {"name": "quantity", "col": "quantity", "agg": "sum", "title": "销售数量"},
146
301
  {"name": "sales_amount", "col": "sales_amount", "agg": "sum", "title": "销售金额"},
@@ -149,6 +304,7 @@ def main():
149
304
  {"name": "line_count", "col": "order_line_id", "agg": "uniq", "title": "订单行数"},
150
305
  ]
151
306
 
307
+ # SalesCube(Process · 销售分析主 Cube)
152
308
  s.register_cube(
153
309
  name="SalesCube",
154
310
  table=fact,
@@ -157,16 +313,19 @@ def main():
157
313
  dimensions=[
158
314
  {"name": "order_id", "col": "order_id", "type": "string", "title": "订单ID"},
159
315
  {"name": "order_line_id", "col": "order_line_id", "type": "string", "title": "订单行ID"},
316
+ {"name": "date_key", "col": "date_key", "type": "int32", "title": "日期键"},
160
317
  {"name": "order_date", "col": "order_date", "type": "date", "title": "订单日期"},
161
318
  {"name": "product_id", "col": "product_id", "type": "string", "title": "产品ID"},
162
319
  {"name": "product_category", "col": "product_category", "type": "string", "title": "产品大类"},
163
320
  {"name": "customer_id", "col": "customer_id", "type": "string", "title": "客户ID"},
164
321
  {"name": "customer_region", "col": "customer_region", "type": "string", "title": "销售区域"},
322
+ {"name": "customer_type", "col": "customer_type", "type": "string", "title": "客户类型"},
165
323
  {"name": "channel_id", "col": "channel_id", "type": "string", "title": "渠道ID"},
166
324
  ],
167
325
  )
168
326
  output.print("OK SalesCube")
169
327
 
328
+ # ProductSalesCube(Subject · 产品销售)
170
329
  s.register_cube(
171
330
  name="ProductSalesCube",
172
331
  table=fact,
@@ -183,6 +342,7 @@ def main():
183
342
  )
184
343
  output.print("OK ProductSalesCube")
185
344
 
345
+ # CustomerSalesCube(Subject · 客户销售)
186
346
  s.register_cube(
187
347
  name="CustomerSalesCube",
188
348
  table=fact,
@@ -200,6 +360,7 @@ def main():
200
360
  )
201
361
  output.print("OK CustomerSalesCube")
202
362
 
363
+ # ChannelSalesCube(Subject · 渠道销售)
203
364
  s.register_cube(
204
365
  name="ChannelSalesCube",
205
366
  table=fact,
@@ -215,26 +376,8 @@ def main():
215
376
  )
216
377
  output.print("OK ChannelSalesCube")
217
378
 
218
- s.register_cube(
219
- name="TimeSalesCube",
220
- table=fact,
221
- title="时间维度销售Cube",
222
- measures=[
223
- {"name": "sales_amount", "col": "sales_amount", "agg": "sum", "title": "销售金额"},
224
- {"name": "quantity", "col": "quantity", "agg": "sum", "title": "销售数量"},
225
- {"name": "order_count", "col": "order_id", "agg": "uniq", "title": "订单数"},
226
- ],
227
- dimensions=[
228
- {"name": "order_date", "col": "order_date", "type": "date", "title": "订单日期"},
229
- {"name": "year_month", "col": "order_date", "type": "string", "title": "年月"},
230
- {"name": "quarter", "col": "order_date", "type": "string", "title": "季度"},
231
- {"name": "year", "col": "order_date", "type": "date", "title": "年份"},
232
- ],
233
- )
234
- output.print("OK TimeSalesCube")
235
-
236
379
  # 5. 派生度量
237
- output.print("\n[5/9] 配置派生度量...")
380
+ output.print("\n[5/10] 配置派生度量...")
238
381
 
239
382
  s.upsert_derived_measures(
240
383
  "SalesCube",
@@ -253,6 +396,8 @@ def main():
253
396
  },
254
397
  ],
255
398
  )
399
+ output.print("OK SalesCube 派生度量")
400
+
256
401
  s.upsert_derived_measures(
257
402
  "ProductSalesCube",
258
403
  [
@@ -264,6 +409,8 @@ def main():
264
409
  },
265
410
  ],
266
411
  )
412
+ output.print("OK ProductSalesCube 派生度量")
413
+
267
414
  s.upsert_derived_measures(
268
415
  "CustomerSalesCube",
269
416
  [
@@ -275,10 +422,10 @@ def main():
275
422
  },
276
423
  ],
277
424
  )
278
- output.print("OK 派生度量")
425
+ output.print("OK CustomerSalesCube 派生度量")
279
426
 
280
427
  # 6. 对象类型
281
- output.print("\n[6/9] 定义对象类型...")
428
+ output.print("\n[6/10] 定义对象类型...")
282
429
 
283
430
  object_types = [
284
431
  ("Product", "产品", "可售产品业务对象"),
@@ -292,7 +439,7 @@ def main():
292
439
  output.print(f"OK {code}")
293
440
 
294
441
  # 7. 绑定数据源
295
- output.print("\n[7/9] 绑定数据源...")
442
+ output.print("\n[7/10] 绑定数据源...")
296
443
 
297
444
  bindings = [
298
445
  ("Product", "ProductSalesCube"),
@@ -301,103 +448,92 @@ def main():
301
448
  ("SalesOrder", "SalesCube"),
302
449
  ("SalesAnalysis", "SalesCube"),
303
450
  ]
304
- for obj, cube in bindings:
305
- s.onto.bind_source(obj, "dazi_cube", config={"cube": cube})
306
- output.print(f"OK {obj} -> {cube}")
307
-
308
- # 8. 属性
309
- output.print("\n[8/9] 定义属性...")
310
-
311
- def define_props(obj_code, props):
312
- for code, name, role, qn in props:
313
- s.onto.define_property(obj_code, code, name, semantic_role=role, qualified_name=qn)
314
-
315
- define_props("Product", [
316
- ("id", "产品ID", "dimension", "ProductSalesCube.product_id"),
317
- ("category", "产品大类", "dimension", "ProductSalesCube.product_category"),
318
- ("quantity", "累计销量", "measure", "ProductSalesCube.quantity"),
319
- ("sales_amount", "累计销售额", "measure", "ProductSalesCube.sales_amount"),
320
- ("order_count", "订单数", "measure", "ProductSalesCube.order_count"),
321
- ("avg_unit_price", "平均单价", "measure", "ProductSalesCube.avg_unit_price"),
322
- ])
323
- output.print("OK Product 属性 (6)")
324
-
325
- define_props("Customer", [
326
- ("id", "客户ID", "dimension", "CustomerSalesCube.customer_id"),
327
- ("region", "销售区域", "dimension", "CustomerSalesCube.customer_region"),
328
- ("type", "客户类型", "dimension", "CustomerSalesCube.customer_type"),
329
- ("sales_amount", "累计销售额", "measure", "CustomerSalesCube.sales_amount"),
330
- ("order_count", "订单数", "measure", "CustomerSalesCube.order_count"),
331
- ("avg_order_value", "客单价", "measure", "CustomerSalesCube.avg_order_value"),
332
- ])
333
- output.print("OK Customer 属性 (6)")
334
-
335
- define_props("SalesChannel", [
336
- ("id", "渠道ID", "dimension", "ChannelSalesCube.channel_id"),
337
- ("sales_amount", "销售额", "measure", "ChannelSalesCube.sales_amount"),
338
- ("order_count", "订单数", "measure", "ChannelSalesCube.order_count"),
339
- ])
340
- output.print("OK SalesChannel 属性 (3)")
341
-
342
- define_props("SalesOrder", [
343
- ("id", "订单ID", "dimension", "SalesCube.order_id"),
344
- ("line_id", "订单行", "dimension", "SalesCube.order_line_id"),
345
- ("date", "订单日期", "dimension", "SalesCube.order_date"),
346
- ("quantity", "数量", "measure", "SalesCube.quantity"),
347
- ("sales_amount", "销售金额", "measure", "SalesCube.sales_amount"),
348
- ("unit_price", "成交单价", "measure", "SalesCube.avg_unit_price"),
349
- ])
350
- output.print("OK SalesOrder 属性 (6)")
351
-
352
- define_props("SalesAnalysis", [
353
- ("date", "日期", "dimension", "SalesCube.order_date"),
354
- ("product_category", "产品大类", "dimension", "SalesCube.product_category"),
355
- ("customer_region", "销售区域", "dimension", "SalesCube.customer_region"),
356
- ("channel_id", "渠道", "dimension", "SalesCube.channel_id"),
357
- ("quantity", "销量", "measure", "SalesCube.quantity"),
358
- ("sales_amount", "销售额", "measure", "SalesCube.sales_amount"),
359
- ("order_count", "订单数", "measure", "SalesCube.order_count"),
360
- ("avg_order_value", "客单价", "measure", "SalesCube.avg_order_value"),
361
- ])
362
- output.print("OK SalesAnalysis 属性 (8)")
363
-
364
- # 9. 链接类型
365
- output.print("\n[9/9] 定义链接与同步指标...")
451
+ for code, cube in bindings:
452
+ s.onto.bind_source(code, "dazi_cube", config={"cube": cube})
453
+ output.print(f"OK {code} -> {cube}")
454
+
455
+ # 8. 定义属性
456
+ output.print("\n[8/10] 定义属性...")
457
+
458
+ properties = [
459
+ # Product 属性
460
+ ("Product", "id", "产品ID", "dimension", "ProductSalesCube.product_id"),
461
+ ("Product", "category", "产品大类", "dimension", "ProductSalesCube.product_category"),
462
+ ("Product", "quantity", "累计销量", "measure", "ProductSalesCube.quantity"),
463
+ ("Product", "sales_amount", "累计销售额", "measure", "ProductSalesCube.sales_amount"),
464
+ ("Product", "order_count", "订单数", "measure", "ProductSalesCube.order_count"),
465
+ ("Product", "avg_unit_price", "平均单价", "measure", "ProductSalesCube.avg_unit_price"),
466
+ # Customer 属性
467
+ ("Customer", "id", "客户ID", "dimension", "CustomerSalesCube.customer_id"),
468
+ ("Customer", "region", "销售区域", "dimension", "CustomerSalesCube.customer_region"),
469
+ ("Customer", "type", "客户类型", "dimension", "CustomerSalesCube.customer_type"),
470
+ ("Customer", "quantity", "累计销量", "measure", "CustomerSalesCube.quantity"),
471
+ ("Customer", "sales_amount", "累计销售额", "measure", "CustomerSalesCube.sales_amount"),
472
+ ("Customer", "order_count", "订单数", "measure", "CustomerSalesCube.order_count"),
473
+ ("Customer", "avg_order_value", "客单价", "measure", "CustomerSalesCube.avg_order_value"),
474
+ # SalesChannel 属性
475
+ ("SalesChannel", "id", "渠道ID", "dimension", "ChannelSalesCube.channel_id"),
476
+ ("SalesChannel", "quantity", "累计销量", "measure", "ChannelSalesCube.quantity"),
477
+ ("SalesChannel", "sales_amount", "累计销售额", "measure", "ChannelSalesCube.sales_amount"),
478
+ ("SalesChannel", "order_count", "订单数", "measure", "ChannelSalesCube.order_count"),
479
+ # SalesOrder 属性
480
+ ("SalesOrder", "id", "订单ID", "dimension", "SalesCube.order_id"),
481
+ ("SalesOrder", "line_id", "订单行", "dimension", "SalesCube.order_line_id"),
482
+ ("SalesOrder", "date", "订单日期", "dimension", "SalesCube.order_date"),
483
+ ("SalesOrder", "quantity", "数量", "measure", "SalesCube.quantity"),
484
+ ("SalesOrder", "sales_amount", "销售金额", "measure", "SalesCube.sales_amount"),
485
+ ("SalesOrder", "unit_price", "成交单价", "measure", "SalesCube.avg_unit_price"),
486
+ # SalesAnalysis 属性
487
+ ("SalesAnalysis", "total_sales", "总销售额", "measure", "SalesCube.sales_amount"),
488
+ ("SalesAnalysis", "total_quantity", "总销量", "measure", "SalesCube.quantity"),
489
+ ("SalesAnalysis", "order_count", "订单数", "measure", "SalesCube.order_count"),
490
+ ("SalesAnalysis", "avg_order_value", "客单价", "measure", "SalesCube.avg_order_value"),
491
+ ("SalesAnalysis", "avg_unit_price", "平均单价", "measure", "SalesCube.avg_unit_price"),
492
+ ]
493
+ for obj_code, prop_code, title, role, qualified_name in properties:
494
+ s.onto.define_property(obj_code, prop_code, title, semantic_role=role, qualified_name=qualified_name)
495
+ output.print(f"OK {len(properties)} 个属性")
496
+
497
+ # 9. 定义链接类型
498
+ output.print("\n[9/10] 定义链接类型...")
366
499
 
367
500
  link_types = [
368
- ("order_contains_product", "订单包含产品", "SalesOrder", "Product", "订单行对应产品"),
369
- ("order_belongs_customer", "订单归属客户", "SalesOrder", "Customer", "订单归属客户"),
370
- ("order_via_channel", "订单经渠道成交", "SalesOrder", "SalesChannel", "订单销售渠道"),
371
- ("product_has_orders", "产品有订单", "Product", "SalesOrder", "产品被哪些订单购买"),
372
- ("customer_places_orders", "客户下单", "Customer", "SalesOrder", "客户历史订单"),
373
- ("analysis_by_product", "分析归因产品", "SalesAnalysis", "Product", "指标按产品切片"),
374
- ("analysis_by_customer", "分析归因客户", "SalesAnalysis", "Customer", "指标按客户切片"),
375
- ("analysis_by_channel", "分析归因渠道", "SalesAnalysis", "SalesChannel", "指标按渠道切片"),
501
+ ("order_contains_product", "订单包含产品", "SalesOrder", "Product"),
502
+ ("order_belongs_customer", "订单归属客户", "SalesOrder", "Customer"),
503
+ ("order_via_channel", "订单经渠道成交", "SalesOrder", "SalesChannel"),
504
+ ("product_has_orders", "产品有订单", "Product", "SalesOrder"),
505
+ ("customer_places_orders", "客户下单", "Customer", "SalesOrder"),
506
+ ("analysis_by_product", "分析归因产品", "SalesAnalysis", "Product"),
507
+ ("analysis_by_customer", "分析归因客户", "SalesAnalysis", "Customer"),
508
+ ("analysis_by_channel", "分析归因渠道", "SalesAnalysis", "SalesChannel"),
376
509
  ]
377
- for code, name, from_obj, to_obj, desc in link_types:
378
- s.onto.define_link_type(
379
- code=code,
380
- name=name,
381
- from_object_type_code=from_obj,
382
- to_object_type_code=to_obj,
383
- description=desc,
384
- )
510
+ for code, name, from_obj, to_obj in link_types:
511
+ s.onto.define_link_type(code, name, from_object_type_code=from_obj, to_object_type_code=to_obj)
385
512
  output.print(f"OK {code}")
386
513
 
514
+ # 同步指标引用
387
515
  s.sync_metric_refs()
388
516
  output.print("OK sync_metric_refs")
389
517
 
518
+ # 10. 347 对齐分类
519
+ output.print("\n[10/10] 配置对象分类(347 对齐)...")
520
+
521
+ cat_counts = s.categories.apply_registry(CATEGORY_REGISTRY, skip_missing=True)
522
+ for kind, cnt in cat_counts.items():
523
+ if cnt:
524
+ output.print(f"OK 分类[{kind}] 挂载 {cnt} 项")
525
+
390
526
  summary = {
391
527
  "ok": True,
392
528
  "space_id": space_id,
393
- "tables": 4,
394
- "table_relationships": 3,
395
- "cubes": 5,
529
+ "tables": 5,
530
+ "table_relationships": 4,
531
+ "cubes": 4,
396
532
  "object_types": 5,
397
- "properties": 29,
398
- "link_types": 8,
533
+ "properties": len(properties),
534
+ "link_types": len(link_types),
535
+ "category_mounts": cat_counts,
399
536
  }
400
-
401
- output.print("\n=== 产品销售本体初始化完成 ===")
402
- output.success("初始化成功")
403
- output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
537
+ output.success("本体初始化完成")
538
+ output.print(f"space_id: {space_id}")
539
+ output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=False))