@dazitech/cli 3.0.7 → 3.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/clis/dazi-app.js +1 -1
- package/dist/clis/dazi-flow.js +1 -1
- package/dist/clis/dazi-onto.js +73 -22
- package/dist/clis/dazi.js +266 -171
- package/dist/docs/flow/flow-project-guide.md +1 -1
- package/dist/docs/guides/quickstart.md +18 -4
- package/dist/docs/guides/troubleshooting.md +1 -1
- package/dist/docs/guides/workspace-v3.md +43 -23
- package/dist/docs/index.json +9 -3
- package/dist/docs/onto/action-guide.md +3 -3
- package/dist/docs/onto/dazi_script_sdk_reference.md +178 -174
- package/dist/docs/onto/dazi_script_seed_data_guide.md +158 -155
- package/dist/docs/onto/function-guide.md +37 -10
- package/dist/docs/onto/space-management.md +3 -1
- 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 +138 -34
- package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +73 -31
- 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 +497 -0
- 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 +597 -541
- package/dist/examples/index.json +202 -22
- package/dist/examples/onto/README.md +43 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_account_breakdown.py +99 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_budget_vs_actual.py +116 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_cost_center_profit.py +85 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_get_summary.py +76 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_mom_analysis.py +86 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_top_accounts.py +103 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_yoy_analysis.py +86 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/save_test_arguments.ps1 +27 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.account_breakdown.json +10 -0
- 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 +10 -0
- 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 +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.get_summary.json +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.mom_analysis.json +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.top_accounts.json +11 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.yoy_analysis.json +9 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +521 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +213 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/README.md +25 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +86 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +123 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +81 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +90 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +85 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +101 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +90 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +25 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.channel_mix.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.customer_segmentation.json +10 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.get_summary.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.mom_analysis.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.region_breakdown.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.top_products.json +10 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.yoy_analysis.json +8 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/README.md +5 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +403 -0
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +124 -0
- package/dist/prompts/index.json +2 -2
- package/dist/prompts/onto/action-design.md +4 -1
- package/dist/prompts/onto/function-design.md +9 -2
- package/dist/prompts/onto/rule-seed.md +5 -1
- package/dist/prompts/onto/script-publish-run.md +72 -24
- package/package.json +1 -1
- package/dist/examples/onto/function/profit_fn_customer_segmentation.py +0 -117
- package/dist/examples/onto/function/profit_fn_mom_analysis.py +0 -89
- package/dist/examples/onto/function/profit_fn_top_products.py +0 -89
- package/dist/examples/onto/function/profit_fn_yoy_analysis.py +0 -89
- package/dist/examples/onto/setup/profit_ontology_init.py +0 -388
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""产品销售本体初始化脚本 — space__zlj
|
|
2
|
+
|
|
3
|
+
初始化内容:
|
|
4
|
+
1. 创建物理表(4 张:产品表、销售表、客户维表、渠道维表)
|
|
5
|
+
2. 注册表到空间
|
|
6
|
+
3. 注册表间关系(3 条:事实表 → 维表)
|
|
7
|
+
4. 注册 Cube(5 个)及派生度量
|
|
8
|
+
5. 定义对象类型(5 种)、绑定数据源、属性、链接
|
|
9
|
+
6. 同步指标引用
|
|
10
|
+
|
|
11
|
+
放置:项目/潘达石化/本体/ontos/产品销售本体方案/setup/sales_ontology_init.py
|
|
12
|
+
发布:dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/setup/sales_ontology_init.py --space space__zlj --type setup
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main():
|
|
19
|
+
space_id = "space__zlj"
|
|
20
|
+
s = space.get(space_id)
|
|
21
|
+
|
|
22
|
+
output.print("=== 产品销售本体初始化 ===")
|
|
23
|
+
output.print(f"空间: {space_id}")
|
|
24
|
+
|
|
25
|
+
# 1. 创建物理表
|
|
26
|
+
output.print("\n[1/9] 创建物理表...")
|
|
27
|
+
|
|
28
|
+
s.sql.execute("""
|
|
29
|
+
CREATE TABLE IF NOT EXISTS product_master (
|
|
30
|
+
product_id String,
|
|
31
|
+
product_code String,
|
|
32
|
+
product_name String,
|
|
33
|
+
product_category String,
|
|
34
|
+
product_subcategory String,
|
|
35
|
+
brand String,
|
|
36
|
+
unit String,
|
|
37
|
+
list_price Float64,
|
|
38
|
+
cost_price Float64,
|
|
39
|
+
status String,
|
|
40
|
+
created_at DateTime DEFAULT now(),
|
|
41
|
+
updated_at DateTime DEFAULT now()
|
|
42
|
+
) ENGINE = MergeTree()
|
|
43
|
+
ORDER BY (product_id)
|
|
44
|
+
""")
|
|
45
|
+
output.print("OK product_master")
|
|
46
|
+
|
|
47
|
+
s.sql.execute("""
|
|
48
|
+
CREATE TABLE IF NOT EXISTS customer_dimension (
|
|
49
|
+
customer_id String,
|
|
50
|
+
customer_code String,
|
|
51
|
+
customer_name String,
|
|
52
|
+
customer_region String,
|
|
53
|
+
customer_type String,
|
|
54
|
+
industry String,
|
|
55
|
+
created_at DateTime DEFAULT now()
|
|
56
|
+
) ENGINE = MergeTree()
|
|
57
|
+
ORDER BY (customer_id)
|
|
58
|
+
""")
|
|
59
|
+
output.print("OK customer_dimension")
|
|
60
|
+
|
|
61
|
+
s.sql.execute("""
|
|
62
|
+
CREATE TABLE IF NOT EXISTS channel_dimension (
|
|
63
|
+
channel_id String,
|
|
64
|
+
channel_code String,
|
|
65
|
+
channel_name String,
|
|
66
|
+
channel_type String
|
|
67
|
+
) ENGINE = MergeTree()
|
|
68
|
+
ORDER BY (channel_id)
|
|
69
|
+
""")
|
|
70
|
+
output.print("OK channel_dimension")
|
|
71
|
+
|
|
72
|
+
s.sql.execute("""
|
|
73
|
+
CREATE TABLE IF NOT EXISTS sales_order_fact (
|
|
74
|
+
order_id String,
|
|
75
|
+
order_line_id String,
|
|
76
|
+
order_date Date,
|
|
77
|
+
product_id String,
|
|
78
|
+
product_category String,
|
|
79
|
+
customer_id String,
|
|
80
|
+
customer_region String,
|
|
81
|
+
customer_type String,
|
|
82
|
+
channel_id String,
|
|
83
|
+
quantity Int32,
|
|
84
|
+
unit_price Float64,
|
|
85
|
+
discount_amount Float64,
|
|
86
|
+
sales_amount Float64,
|
|
87
|
+
currency String,
|
|
88
|
+
order_status String
|
|
89
|
+
) ENGINE = MergeTree()
|
|
90
|
+
ORDER BY (order_date, order_id, order_line_id)
|
|
91
|
+
""")
|
|
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}")
|
|
106
|
+
|
|
107
|
+
# 3. 注册表间关系(数据空间「表间关系」,供 JOIN / 侧栏展示)
|
|
108
|
+
output.print("\n[3/9] 注册表间关系...")
|
|
109
|
+
|
|
110
|
+
table_relationships = [
|
|
111
|
+
{
|
|
112
|
+
"from_table": "sales_order_fact",
|
|
113
|
+
"to_table": "product_master",
|
|
114
|
+
"join_sql": "sales_order_fact.product_id = product_master.product_id",
|
|
115
|
+
"join_keys": [{"from": "product_id", "to": "product_id"}],
|
|
116
|
+
"relationship_type": "many_to_one",
|
|
117
|
+
"description": "销售订单行关联产品主数据",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"from_table": "sales_order_fact",
|
|
121
|
+
"to_table": "customer_dimension",
|
|
122
|
+
"join_sql": "sales_order_fact.customer_id = customer_dimension.customer_id",
|
|
123
|
+
"join_keys": [{"from": "customer_id", "to": "customer_id"}],
|
|
124
|
+
"relationship_type": "many_to_one",
|
|
125
|
+
"description": "销售订单行关联客户",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"from_table": "sales_order_fact",
|
|
129
|
+
"to_table": "channel_dimension",
|
|
130
|
+
"join_sql": "sales_order_fact.channel_id = channel_dimension.channel_id",
|
|
131
|
+
"join_keys": [{"from": "channel_id", "to": "channel_id"}],
|
|
132
|
+
"relationship_type": "many_to_one",
|
|
133
|
+
"description": "销售订单行关联销售渠道",
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
for rel in table_relationships:
|
|
137
|
+
rid = s.tables.add_relationship(**rel)
|
|
138
|
+
output.print(f"OK {rel['from_table']} -> {rel['to_table']} ({rid})")
|
|
139
|
+
|
|
140
|
+
# 4. 注册 Cube
|
|
141
|
+
output.print("\n[4/9] 注册 Cube...")
|
|
142
|
+
|
|
143
|
+
fact = "sales_order_fact"
|
|
144
|
+
common_measures = [
|
|
145
|
+
{"name": "quantity", "col": "quantity", "agg": "sum", "title": "销售数量"},
|
|
146
|
+
{"name": "sales_amount", "col": "sales_amount", "agg": "sum", "title": "销售金额"},
|
|
147
|
+
{"name": "discount", "col": "discount_amount", "agg": "sum", "title": "折扣金额"},
|
|
148
|
+
{"name": "order_count", "col": "order_id", "agg": "uniq", "title": "订单数"},
|
|
149
|
+
{"name": "line_count", "col": "order_line_id", "agg": "uniq", "title": "订单行数"},
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
s.register_cube(
|
|
153
|
+
name="SalesCube",
|
|
154
|
+
table=fact,
|
|
155
|
+
title="销售分析主Cube",
|
|
156
|
+
measures=common_measures,
|
|
157
|
+
dimensions=[
|
|
158
|
+
{"name": "order_id", "col": "order_id", "type": "string", "title": "订单ID"},
|
|
159
|
+
{"name": "order_line_id", "col": "order_line_id", "type": "string", "title": "订单行ID"},
|
|
160
|
+
{"name": "order_date", "col": "order_date", "type": "date", "title": "订单日期"},
|
|
161
|
+
{"name": "product_id", "col": "product_id", "type": "string", "title": "产品ID"},
|
|
162
|
+
{"name": "product_category", "col": "product_category", "type": "string", "title": "产品大类"},
|
|
163
|
+
{"name": "customer_id", "col": "customer_id", "type": "string", "title": "客户ID"},
|
|
164
|
+
{"name": "customer_region", "col": "customer_region", "type": "string", "title": "销售区域"},
|
|
165
|
+
{"name": "channel_id", "col": "channel_id", "type": "string", "title": "渠道ID"},
|
|
166
|
+
],
|
|
167
|
+
)
|
|
168
|
+
output.print("OK SalesCube")
|
|
169
|
+
|
|
170
|
+
s.register_cube(
|
|
171
|
+
name="ProductSalesCube",
|
|
172
|
+
table=fact,
|
|
173
|
+
title="产品销售Cube",
|
|
174
|
+
measures=[
|
|
175
|
+
{"name": "quantity", "col": "quantity", "agg": "sum", "title": "销售数量"},
|
|
176
|
+
{"name": "sales_amount", "col": "sales_amount", "agg": "sum", "title": "销售金额"},
|
|
177
|
+
{"name": "order_count", "col": "order_id", "agg": "uniq", "title": "订单数"},
|
|
178
|
+
],
|
|
179
|
+
dimensions=[
|
|
180
|
+
{"name": "product_id", "col": "product_id", "type": "string", "title": "产品ID"},
|
|
181
|
+
{"name": "product_category", "col": "product_category", "type": "string", "title": "产品大类"},
|
|
182
|
+
],
|
|
183
|
+
)
|
|
184
|
+
output.print("OK ProductSalesCube")
|
|
185
|
+
|
|
186
|
+
s.register_cube(
|
|
187
|
+
name="CustomerSalesCube",
|
|
188
|
+
table=fact,
|
|
189
|
+
title="客户销售Cube",
|
|
190
|
+
measures=[
|
|
191
|
+
{"name": "quantity", "col": "quantity", "agg": "sum", "title": "销售数量"},
|
|
192
|
+
{"name": "sales_amount", "col": "sales_amount", "agg": "sum", "title": "销售金额"},
|
|
193
|
+
{"name": "order_count", "col": "order_id", "agg": "uniq", "title": "订单数"},
|
|
194
|
+
],
|
|
195
|
+
dimensions=[
|
|
196
|
+
{"name": "customer_id", "col": "customer_id", "type": "string", "title": "客户ID"},
|
|
197
|
+
{"name": "customer_region", "col": "customer_region", "type": "string", "title": "销售区域"},
|
|
198
|
+
{"name": "customer_type", "col": "customer_type", "type": "string", "title": "客户类型"},
|
|
199
|
+
],
|
|
200
|
+
)
|
|
201
|
+
output.print("OK CustomerSalesCube")
|
|
202
|
+
|
|
203
|
+
s.register_cube(
|
|
204
|
+
name="ChannelSalesCube",
|
|
205
|
+
table=fact,
|
|
206
|
+
title="渠道销售Cube",
|
|
207
|
+
measures=[
|
|
208
|
+
{"name": "quantity", "col": "quantity", "agg": "sum", "title": "销售数量"},
|
|
209
|
+
{"name": "sales_amount", "col": "sales_amount", "agg": "sum", "title": "销售金额"},
|
|
210
|
+
{"name": "order_count", "col": "order_id", "agg": "uniq", "title": "订单数"},
|
|
211
|
+
],
|
|
212
|
+
dimensions=[
|
|
213
|
+
{"name": "channel_id", "col": "channel_id", "type": "string", "title": "渠道ID"},
|
|
214
|
+
],
|
|
215
|
+
)
|
|
216
|
+
output.print("OK ChannelSalesCube")
|
|
217
|
+
|
|
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
|
+
# 5. 派生度量
|
|
237
|
+
output.print("\n[5/9] 配置派生度量...")
|
|
238
|
+
|
|
239
|
+
s.upsert_derived_measures(
|
|
240
|
+
"SalesCube",
|
|
241
|
+
[
|
|
242
|
+
{
|
|
243
|
+
"name": "avg_unit_price",
|
|
244
|
+
"title": "平均单价",
|
|
245
|
+
"expression": "if(SalesCube.quantity > 0, SalesCube.sales_amount / SalesCube.quantity, 0)",
|
|
246
|
+
"description": "销售金额/销量",
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "avg_order_value",
|
|
250
|
+
"title": "客单价",
|
|
251
|
+
"expression": "if(SalesCube.order_count > 0, SalesCube.sales_amount / SalesCube.order_count, 0)",
|
|
252
|
+
"description": "销售金额/订单数",
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
)
|
|
256
|
+
s.upsert_derived_measures(
|
|
257
|
+
"ProductSalesCube",
|
|
258
|
+
[
|
|
259
|
+
{
|
|
260
|
+
"name": "avg_unit_price",
|
|
261
|
+
"title": "平均单价",
|
|
262
|
+
"expression": "if(ProductSalesCube.quantity > 0, ProductSalesCube.sales_amount / ProductSalesCube.quantity, 0)",
|
|
263
|
+
"description": "平均成交单价",
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
)
|
|
267
|
+
s.upsert_derived_measures(
|
|
268
|
+
"CustomerSalesCube",
|
|
269
|
+
[
|
|
270
|
+
{
|
|
271
|
+
"name": "avg_order_value",
|
|
272
|
+
"title": "客单价",
|
|
273
|
+
"expression": "if(CustomerSalesCube.order_count > 0, CustomerSalesCube.sales_amount / CustomerSalesCube.order_count, 0)",
|
|
274
|
+
"description": "客户客单价",
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
)
|
|
278
|
+
output.print("OK 派生度量")
|
|
279
|
+
|
|
280
|
+
# 6. 对象类型
|
|
281
|
+
output.print("\n[6/9] 定义对象类型...")
|
|
282
|
+
|
|
283
|
+
object_types = [
|
|
284
|
+
("Product", "产品", "可售产品业务对象"),
|
|
285
|
+
("Customer", "客户", "购买方业务对象"),
|
|
286
|
+
("SalesChannel", "销售渠道", "渠道业务对象"),
|
|
287
|
+
("SalesOrder", "销售订单", "订单/订单行业务对象"),
|
|
288
|
+
("SalesAnalysis", "销售分析", "多维度销售指标聚合对象"),
|
|
289
|
+
]
|
|
290
|
+
for code, name, desc in object_types:
|
|
291
|
+
s.onto.define_object_type(code, name, description=desc)
|
|
292
|
+
output.print(f"OK {code}")
|
|
293
|
+
|
|
294
|
+
# 7. 绑定数据源
|
|
295
|
+
output.print("\n[7/9] 绑定数据源...")
|
|
296
|
+
|
|
297
|
+
bindings = [
|
|
298
|
+
("Product", "ProductSalesCube"),
|
|
299
|
+
("Customer", "CustomerSalesCube"),
|
|
300
|
+
("SalesChannel", "ChannelSalesCube"),
|
|
301
|
+
("SalesOrder", "SalesCube"),
|
|
302
|
+
("SalesAnalysis", "SalesCube"),
|
|
303
|
+
]
|
|
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] 定义链接与同步指标...")
|
|
366
|
+
|
|
367
|
+
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", "指标按渠道切片"),
|
|
376
|
+
]
|
|
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
|
+
)
|
|
385
|
+
output.print(f"OK {code}")
|
|
386
|
+
|
|
387
|
+
s.sync_metric_refs()
|
|
388
|
+
output.print("OK sync_metric_refs")
|
|
389
|
+
|
|
390
|
+
summary = {
|
|
391
|
+
"ok": True,
|
|
392
|
+
"space_id": space_id,
|
|
393
|
+
"tables": 4,
|
|
394
|
+
"table_relationships": 3,
|
|
395
|
+
"cubes": 5,
|
|
396
|
+
"object_types": 5,
|
|
397
|
+
"properties": 29,
|
|
398
|
+
"link_types": 8,
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
output.print("\n=== 产品销售本体初始化完成 ===")
|
|
402
|
+
output.success("初始化成功")
|
|
403
|
+
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
|
package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""产品销售演示数据灌入 — space__zlj
|
|
2
|
+
|
|
3
|
+
前置:先执行 sales_ontology_init.py 建表。
|
|
4
|
+
幂等:sales_order_fact 已有数据则跳过。
|
|
5
|
+
|
|
6
|
+
放置:项目/潘达石化/本体/ontos/产品销售本体方案/setup/sales_seed_data.py
|
|
7
|
+
发布:dazi onto script publish 项目/潘达石化/本体/ontos/产品销售本体方案/setup/sales_seed_data.py --space space__zlj --type data
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import random
|
|
12
|
+
from datetime import date, datetime, timedelta
|
|
13
|
+
|
|
14
|
+
_SEED_DT = datetime(2025, 1, 1, 0, 0, 0)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
space_id = "space__zlj"
|
|
19
|
+
s = space.get(space_id)
|
|
20
|
+
|
|
21
|
+
output.print("=== 产品销售演示数据灌入 ===")
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
n = int(s.sql.query_one("SELECT count() FROM sales_order_fact") or 0)
|
|
25
|
+
except Exception:
|
|
26
|
+
n = 0
|
|
27
|
+
if n > 0:
|
|
28
|
+
output.print(f"sales_order_fact 已有 {n} 行,跳过灌数")
|
|
29
|
+
output.print("__JSON_SUMMARY__" + json.dumps({"ok": True, "skipped": True, "rows": n}, ensure_ascii=True))
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
products = [
|
|
33
|
+
{"product_id": "P001", "product_code": "OIL-92", "product_name": "92号汽油", "product_category": "成品油", "product_subcategory": "汽油", "brand": "潘达", "unit": "吨", "list_price": 8200.0, "cost_price": 7100.0, "status": "在售"},
|
|
34
|
+
{"product_id": "P002", "product_code": "OIL-0", "product_name": "0号柴油", "product_category": "成品油", "product_subcategory": "柴油", "brand": "潘达", "unit": "吨", "list_price": 7500.0, "cost_price": 6500.0, "status": "在售"},
|
|
35
|
+
{"product_id": "P003", "product_code": "CHEM-LLDPE", "product_name": "线性低密度聚乙烯", "product_category": "化工品", "product_subcategory": "聚乙烯", "brand": "潘达", "unit": "吨", "list_price": 9200.0, "cost_price": 8100.0, "status": "在售"},
|
|
36
|
+
{"product_id": "P004", "product_code": "CHEM-PP", "product_name": "聚丙烯PP", "product_category": "化工品", "product_subcategory": "聚丙烯", "brand": "潘达", "unit": "吨", "list_price": 8800.0, "cost_price": 7800.0, "status": "在售"},
|
|
37
|
+
{"product_id": "P005", "product_code": "LUB-BASE", "product_name": "润滑油基础油", "product_category": "润滑油", "product_subcategory": "基础油", "brand": "潘达", "unit": "吨", "list_price": 6800.0, "cost_price": 5900.0, "status": "在售"},
|
|
38
|
+
{"product_id": "P006", "product_code": "COKE-1", "product_name": "石油焦", "product_category": "副产品", "product_subcategory": "石油焦", "brand": "潘达", "unit": "吨", "list_price": 3200.0, "cost_price": 2600.0, "status": "在售"},
|
|
39
|
+
]
|
|
40
|
+
for p in products:
|
|
41
|
+
p["created_at"] = _SEED_DT
|
|
42
|
+
p["updated_at"] = _SEED_DT
|
|
43
|
+
|
|
44
|
+
customers = [
|
|
45
|
+
{"customer_id": "C001", "customer_code": "CUS-001", "customer_name": "华东油品经销", "customer_region": "华东", "customer_type": "战略", "industry": "经销"},
|
|
46
|
+
{"customer_id": "C002", "customer_code": "CUS-002", "customer_name": "华北炼化贸易", "customer_region": "华北", "customer_type": "VIP", "industry": "贸易"},
|
|
47
|
+
{"customer_id": "C003", "customer_code": "CUS-003", "customer_name": "华南化工采购", "customer_region": "华南", "customer_type": "VIP", "industry": "化工"},
|
|
48
|
+
{"customer_id": "C004", "customer_code": "CUS-004", "customer_name": "西南区域物流", "customer_region": "西南", "customer_type": "普通", "industry": "物流"},
|
|
49
|
+
{"customer_id": "C005", "customer_code": "CUS-005", "customer_name": "华中制造集团", "customer_region": "华中", "customer_type": "普通", "industry": "制造"},
|
|
50
|
+
]
|
|
51
|
+
for c in customers:
|
|
52
|
+
c["created_at"] = _SEED_DT
|
|
53
|
+
|
|
54
|
+
channels = [
|
|
55
|
+
{"channel_id": "CH01", "channel_code": "DIR", "channel_name": "直销", "channel_type": "直销"},
|
|
56
|
+
{"channel_id": "CH02", "channel_code": "DIST", "channel_name": "经销渠道", "channel_type": "经销"},
|
|
57
|
+
{"channel_id": "CH03", "channel_code": "B2B", "channel_name": "B2B平台", "channel_type": "线上"},
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
s.sql.insert_rows("product_master", products)
|
|
61
|
+
s.sql.insert_rows("customer_dimension", customers)
|
|
62
|
+
s.sql.insert_rows("channel_dimension", channels)
|
|
63
|
+
output.print("OK 维表数据")
|
|
64
|
+
|
|
65
|
+
random.seed(627)
|
|
66
|
+
regions = {c["customer_id"]: c["customer_region"] for c in customers}
|
|
67
|
+
ctypes = {c["customer_id"]: c["customer_type"] for c in customers}
|
|
68
|
+
pcats = {p["product_id"]: p["product_category"] for p in products}
|
|
69
|
+
prices = {p["product_id"]: p["list_price"] for p in products}
|
|
70
|
+
|
|
71
|
+
fact_rows = []
|
|
72
|
+
line_seq = 1
|
|
73
|
+
start = date(2025, 1, 1)
|
|
74
|
+
end = date(2026, 6, 30)
|
|
75
|
+
days = (end - start).days + 1
|
|
76
|
+
|
|
77
|
+
for d_offset in range(days):
|
|
78
|
+
order_date = start + timedelta(days=d_offset)
|
|
79
|
+
if random.random() > 0.35:
|
|
80
|
+
continue
|
|
81
|
+
daily_orders = random.randint(2, 8)
|
|
82
|
+
for _ in range(daily_orders):
|
|
83
|
+
order_id = f"O{order_date.strftime('%Y%m%d')}{random.randint(100, 999)}"
|
|
84
|
+
customer_id = random.choice(list(regions.keys()))
|
|
85
|
+
channel_id = random.choice(["CH01", "CH02", "CH03"])
|
|
86
|
+
lines = random.randint(1, 3)
|
|
87
|
+
for _ in range(lines):
|
|
88
|
+
product_id = random.choice(list(pcats.keys()))
|
|
89
|
+
qty = random.randint(5, 200)
|
|
90
|
+
unit_price = round(prices[product_id] * random.uniform(0.92, 1.0), 2)
|
|
91
|
+
discount = round(unit_price * qty * random.uniform(0, 0.05), 2)
|
|
92
|
+
sales_amount = round(unit_price * qty - discount, 2)
|
|
93
|
+
fact_rows.append({
|
|
94
|
+
"order_id": order_id,
|
|
95
|
+
"order_line_id": f"L{line_seq:06d}",
|
|
96
|
+
"order_date": order_date,
|
|
97
|
+
"product_id": product_id,
|
|
98
|
+
"product_category": pcats[product_id],
|
|
99
|
+
"customer_id": customer_id,
|
|
100
|
+
"customer_region": regions[customer_id],
|
|
101
|
+
"customer_type": ctypes[customer_id],
|
|
102
|
+
"channel_id": channel_id,
|
|
103
|
+
"quantity": qty,
|
|
104
|
+
"unit_price": unit_price,
|
|
105
|
+
"discount_amount": discount,
|
|
106
|
+
"sales_amount": sales_amount,
|
|
107
|
+
"currency": "CNY",
|
|
108
|
+
"order_status": random.choice(["已完成", "已完成", "已发货"]),
|
|
109
|
+
})
|
|
110
|
+
line_seq += 1
|
|
111
|
+
|
|
112
|
+
inserted = s.sql.insert_rows("sales_order_fact", fact_rows)
|
|
113
|
+
output.print(f"OK 销售事实表插入 {inserted} 行")
|
|
114
|
+
|
|
115
|
+
summary = {
|
|
116
|
+
"ok": True,
|
|
117
|
+
"space_id": space_id,
|
|
118
|
+
"products": len(products),
|
|
119
|
+
"customers": len(customers),
|
|
120
|
+
"channels": len(channels),
|
|
121
|
+
"fact_inserted": inserted,
|
|
122
|
+
}
|
|
123
|
+
output.success("灌数完成")
|
|
124
|
+
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
|
package/dist/prompts/index.json
CHANGED
|
@@ -40,10 +40,13 @@ def main(params: dict, context: dict, s=None) -> dict:
|
|
|
40
40
|
return {"status": "ok"}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
脚本落盘路径:`项目/<业务名>/本体/ontos/<实现名>/setup/` 或 `functions/`(按脚本类型选择)。
|
|
44
|
+
`space_id` 取自 `ontos/<实现名>/README.md`。
|
|
45
|
+
|
|
43
46
|
发布命令(v3;**勿用** `dazi-onto`):
|
|
44
47
|
|
|
45
48
|
```powershell
|
|
46
|
-
dazi onto script publish
|
|
49
|
+
dazi onto script publish 项目/<业务名>/本体/ontos/<实现名>/setup/<file>.py --space <space-id> --register-action-id <action_code> --register-action-permission-tag "finance.write"
|
|
47
50
|
```
|
|
48
51
|
|
|
49
52
|
详见提示词 `onto/script-publish-run`。
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
4. 包含完整的 docstring(中文),说明:函数目的、参数说明、返回值说明
|
|
20
20
|
5. 包含基本的错误处理(try/except)
|
|
21
21
|
6. 引用的搭子数据空间对象通过 `s` 上下文访问
|
|
22
|
+
7. 在脚本顶部定义 `TEST_ARGUMENTS` 常量(与 `functions/test_arguments/<function_id>.json` 同步),供发布后 `save-test-arguments` 入库
|
|
22
23
|
|
|
23
24
|
## 输出格式
|
|
24
25
|
|
|
@@ -36,11 +37,17 @@ def main(params: dict, s=None) -> dict:
|
|
|
36
37
|
# 实现
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
脚本落盘路径:`项目/<业务名>/本体/ontos/<实现名>/functions/<file>.py`
|
|
41
|
+
`space_id` 取自同目录上级的 `README.md`(`ontos/<实现名>/README.md`)。
|
|
42
|
+
|
|
39
43
|
发布与运行(v3,工作区根目录;**勿用** `dazi-onto`):
|
|
40
44
|
|
|
41
45
|
```powershell
|
|
42
|
-
dazi onto script publish
|
|
46
|
+
dazi onto script publish 项目/<业务名>/本体/ontos/<实现名>/functions/<file>.py --space <space-id> --register-function-id <id>
|
|
43
47
|
dazi onto function run <id> --space <space-id>
|
|
48
|
+
# run 验证通过后,保存 test_arguments(侧栏「运行函数」预填;须用 ofn_xxx 内部 id,见 script-publish-run §7)
|
|
49
|
+
dazi onto function save-test-arguments <ofn_internal_id> --space <space-id> `
|
|
50
|
+
--arguments-json-file 项目/<业务名>/本体/ontos/<实现名>/functions/test_arguments/<id>.json
|
|
44
51
|
```
|
|
45
52
|
|
|
46
|
-
详见提示词 `onto/script-publish-run`(侧栏 帮助 →
|
|
53
|
+
详见提示词 `onto/script-publish-run`(侧栏 帮助 → 提示词),含 `test_arguments` JSON 格式与批量 `save_test_arguments.ps1`。
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
## 种子脚本模板
|
|
15
15
|
|
|
16
|
+
建议将种子脚本保存到 `项目/<业务名>/本体/ontos/<实现名>/setup/<stem>_seed_rules.py`(与同实现的 init/seed 脚本并列)。
|
|
17
|
+
`space_id` 取自 `ontos/<实现名>/README.md`。
|
|
18
|
+
|
|
16
19
|
```python
|
|
17
20
|
def main(params: dict, s=None) -> dict:
|
|
18
21
|
"""
|
|
@@ -34,9 +37,10 @@ def main(params: dict, s=None) -> dict:
|
|
|
34
37
|
return {"upserted": len(rules), "result": result}
|
|
35
38
|
```
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
发布与执行(v3;**勿用** `dazi-onto`):
|
|
38
41
|
|
|
39
42
|
```powershell
|
|
43
|
+
dazi onto script publish 项目/<业务名>/本体/ontos/<实现名>/setup/<stem>_seed_rules.py --space <space-id> --type data
|
|
40
44
|
dazi onto rule run-seed --space <space-id> --stem <seed_file_stem>
|
|
41
45
|
```
|
|
42
46
|
|