dataops-sdk 0.3.0__py3-none-any.whl

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.

Potentially problematic release.


This version of dataops-sdk might be problematic. Click here for more details.

Files changed (79) hide show
  1. dataops_sdk/__init__.py +84 -0
  2. dataops_sdk/constants.py +6 -0
  3. dataops_sdk/exceptions.py +31 -0
  4. dataops_sdk/runner/__init__.py +46 -0
  5. dataops_sdk/runner/datasource_factory.py +83 -0
  6. dataops_sdk/runner/schemas/nacos/__init__.py +5 -0
  7. dataops_sdk/runner/schemas/nacos/config.py +40 -0
  8. dataops_sdk/runner/schemas/nacos/constants.py +235 -0
  9. dataops_sdk/runner/schemas/nacos/instance.py +103 -0
  10. dataops_sdk/runner/schemas/nacos/nacos_exception.py +44 -0
  11. dataops_sdk/runner/schemas/nacos/naming_param.py +62 -0
  12. dataops_sdk/runner/schemas/nacos/naming_request.py +84 -0
  13. dataops_sdk/runner/schemas/nacos/naming_response.py +50 -0
  14. dataops_sdk/runner/schemas/nacos/preserved_metadata_key.py +10 -0
  15. dataops_sdk/runner/schemas/nacos/service.py +119 -0
  16. dataops_sdk/runner/utils/__init__.py +42 -0
  17. dataops_sdk/runner/utils/common.py +209 -0
  18. dataops_sdk/runner/utils/dbConn_base.py +256 -0
  19. dataops_sdk/runner/utils/dbConn_sync.py +485 -0
  20. dataops_sdk/runner/utils/duckdb_manager.py +203 -0
  21. dataops_sdk/runner/utils/file_transfer_base.py +307 -0
  22. dataops_sdk/runner/utils/ftp_file_transfer.py +242 -0
  23. dataops_sdk/runner/utils/log.py +50 -0
  24. dataops_sdk/runner/utils/nacos.py +96 -0
  25. dataops_sdk/runner/utils/neo4j_sync.py +236 -0
  26. dataops_sdk/runner/utils/queue_base.py +224 -0
  27. dataops_sdk/runner/utils/queue_kafka.py +150 -0
  28. dataops_sdk/runner/utils/sftp_file_transfer.py +243 -0
  29. dataops_sdk/schemas/__init__.py +32 -0
  30. dataops_sdk/schemas/alarm.py +38 -0
  31. dataops_sdk/schemas/api.py +37 -0
  32. dataops_sdk/schemas/data_table.py +437 -0
  33. dataops_sdk/schemas/datasource.py +26 -0
  34. dataops_sdk/schemas/dict_.py +13 -0
  35. dataops_sdk/schemas/enums.py +120 -0
  36. dataops_sdk/schemas/group.py +40 -0
  37. dataops_sdk/schemas/hazard.py +151 -0
  38. dataops_sdk/schemas/meta.py +175 -0
  39. dataops_sdk/schemas/query.py +175 -0
  40. dataops_sdk/schemas/report.py +22 -0
  41. dataops_sdk/schemas/skill.py +70 -0
  42. dataops_sdk/schemas/table.py +65 -0
  43. dataops_sdk/schemas/user.py +52 -0
  44. dataops_sdk/schemas/widget.py +651 -0
  45. dataops_sdk/schemas/workflow.py +63 -0
  46. dataops_sdk/services/__init__.py +110 -0
  47. dataops_sdk/services/alarm.py +324 -0
  48. dataops_sdk/services/api.py +28 -0
  49. dataops_sdk/services/auth.py +120 -0
  50. dataops_sdk/services/data_table.py +485 -0
  51. dataops_sdk/services/dataset.py +127 -0
  52. dataops_sdk/services/datasource.py +57 -0
  53. dataops_sdk/services/dict_.py +69 -0
  54. dataops_sdk/services/hazard.py +260 -0
  55. dataops_sdk/services/meta.py +523 -0
  56. dataops_sdk/services/metrics.py +42 -0
  57. dataops_sdk/services/ref.py +66 -0
  58. dataops_sdk/services/report.py +36 -0
  59. dataops_sdk/services/skill.py +276 -0
  60. dataops_sdk/services/table.py +340 -0
  61. dataops_sdk/services/widget.py +143 -0
  62. dataops_sdk/services/workflow.py +102 -0
  63. dataops_sdk/session.py +292 -0
  64. dataops_sdk/tiptap/__init__.py +30 -0
  65. dataops_sdk/tiptap/builder.py +75 -0
  66. dataops_sdk/tiptap/constants.py +133 -0
  67. dataops_sdk/tiptap/markdown_parser.py +294 -0
  68. dataops_sdk/tiptap/models.py +43 -0
  69. dataops_sdk/tiptap/parse_utils.py +889 -0
  70. dataops_sdk/tiptap/render_utils.py +846 -0
  71. dataops_sdk/tiptap/renderers.py +51 -0
  72. dataops_sdk/tiptap/tool.py +629 -0
  73. dataops_sdk/utils/__init__.py +15 -0
  74. dataops_sdk/utils/common.py +211 -0
  75. dataops_sdk/utils/utils.py +120 -0
  76. dataops_sdk-0.3.0.dist-info/METADATA +25 -0
  77. dataops_sdk-0.3.0.dist-info/RECORD +79 -0
  78. dataops_sdk-0.3.0.dist-info/WHEEL +5 -0
  79. dataops_sdk-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,651 @@
1
+ from enum import StrEnum
2
+ from typing import Annotated, Any, Literal
3
+
4
+ from pydantic import BaseModel, Field, model_validator
5
+
6
+ from .meta import Meta
7
+
8
+ # ============================================================
9
+ # 共享字段类型(所有卡片类型共用)
10
+ # ============================================================
11
+
12
+
13
+ class DimensionField(BaseModel):
14
+ """维度字段"""
15
+
16
+ id: Annotated[str, Field(description="维度ID")]
17
+ name: Annotated[str, Field(description="字段名")]
18
+ title: Annotated[str, Field(description="显示标题")]
19
+ dbType: Annotated[str | None, Field(description="数据库类型")] = None
20
+ relId: Annotated[str | None, Field(description="关联ID")] = None
21
+ relType: Annotated[str | None, Field(description="关联类型")] = None
22
+
23
+
24
+ class MeasureField(BaseModel):
25
+ """度量字段"""
26
+
27
+ id: Annotated[str, Field(description="度量ID")]
28
+ name: Annotated[str, Field(description="字段名")]
29
+ title: Annotated[str, Field(description="显示标题")]
30
+ dbType: Annotated[str | None, Field(description="数据库类型")] = None
31
+ relId: Annotated[str | None, Field(description="关联ID")] = None
32
+ relType: Annotated[str | None, Field(description="关联类型")] = None
33
+ aggregation: Annotated[
34
+ Literal["avg", "count", "distinct_count", "max", "min", "sum"] | None,
35
+ Field(description="聚合方式"),
36
+ ] = None
37
+
38
+
39
+ class FilterField(BaseModel):
40
+ """过滤字段"""
41
+
42
+ id: Annotated[str, Field(description="过滤ID")]
43
+ name: Annotated[str, Field(description="字段名")]
44
+ title: Annotated[str, Field(description="显示标题")]
45
+ operator: Annotated[str | None, Field(description="操作符")] = None
46
+ value: Annotated[Any, Field(description="过滤值")] = None
47
+
48
+
49
+ class SortField(BaseModel):
50
+ """排序字段"""
51
+
52
+ id: Annotated[str, Field(description="排序ID")]
53
+ relId: Annotated[str | None, Field(description="关联ID")] = None
54
+ relType: Annotated[Literal["dimension", "measure"] | None, Field(description="关联类型")] = None
55
+ title: Annotated[str | None, Field(description="显示标题")] = None
56
+ asc: Annotated[bool, Field(description="是否升序")] = True
57
+
58
+
59
+ # ============================================================
60
+ # Chart 卡片 Schema (v1 + v2 统一)
61
+ # 前端 IdealChartView / IdealChartViewV2 共用
62
+ # 后端 subType="chart",数据 key 也叫 "chart"
63
+ # ============================================================
64
+
65
+
66
+ class ChartType(StrEnum):
67
+ """图表子类型"""
68
+
69
+ LINE = "line"
70
+ BAR = "bar"
71
+ PIE = "pie"
72
+ DOUGHNUT = "doughnut"
73
+ RADAR = "radar"
74
+ SCATTER = "scatter"
75
+ FUNNEL = "funnel"
76
+
77
+
78
+ class TextStyle(BaseModel):
79
+ """文本样式(legend/xAxis/yAxis/label等使用)"""
80
+
81
+ fontSize: Annotated[int | None, Field(description="字体大小")] = None
82
+ color: Annotated[str | None, Field(description="颜色")] = None
83
+ fontWeight: Annotated[Literal["normal", "bold"] | None, Field(description="字重")] = None
84
+ fontStyle: Annotated[Literal["normal", "italic"] | None, Field(description="字体风格")] = None
85
+ textDecoration: Annotated[Literal["none", "underline"] | None, Field(description="装饰")] = None
86
+ textAlign: Annotated[Literal["left", "center", "right"] | None, Field(description="对齐")] = (
87
+ None
88
+ )
89
+
90
+
91
+ class ChartAxis(BaseModel):
92
+ """坐标轴配置"""
93
+
94
+ show: Annotated[bool, Field(description="是否显示")] = True
95
+ showLabel: Annotated[bool, Field(description="是否显示标签")] = True
96
+ labelRotate: Annotated[
97
+ Literal[0, 45, 90, -45, -90] | None, Field(description="标签旋转角度")
98
+ ] = None
99
+ labelGap: Annotated[int | None, Field(description="标签间距")] = None
100
+ showAxisLine: Annotated[bool, Field(description="是否显示轴线")] = True
101
+ labelTextStyle: Annotated[TextStyle | None, Field(description="标签文本样式")] = None
102
+
103
+
104
+ class ChartLegend(BaseModel):
105
+ """图例配置"""
106
+
107
+ show: Annotated[bool, Field(description="是否显示")] = True
108
+ position: Annotated[
109
+ Literal["top", "bottom", "left", "right"] | None, Field(description="位置")
110
+ ] = None
111
+ textStyle: Annotated[TextStyle | None, Field(description="文本样式")] = None
112
+
113
+
114
+ class ChartLabel(BaseModel):
115
+ """标签配置"""
116
+
117
+ show: Annotated[bool, Field(description="是否显示")] = True
118
+
119
+
120
+ class ChartGrid(BaseModel):
121
+ """网格配置"""
122
+
123
+ show: Annotated[bool, Field(description="是否显示")] = True
124
+
125
+
126
+ class ChartBackground(BaseModel):
127
+ """背景配置"""
128
+
129
+ color: Annotated[
130
+ Literal["default", "#ffffff", "#f5f7fa", "#e8f4fd", "#1a1a2e", "#0d1b2a"] | str | None,
131
+ Field(description="背景色"),
132
+ ] = "default"
133
+
134
+
135
+ class ChartAxisTitle(BaseModel):
136
+ """轴标题配置"""
137
+
138
+ xAxis: Annotated[TextStyle | None, Field(description="X轴标题")] = None
139
+ yAxis: Annotated[TextStyle | None, Field(description="Y轴标题")] = None
140
+
141
+
142
+ class LineSeriesConfig(BaseModel):
143
+ """折线图系列配置"""
144
+
145
+ lineStyle: Annotated[TextStyle | None, Field(description="线条样式")] = None
146
+ itemStyle: Annotated[TextStyle | None, Field(description="数据项样式")] = None
147
+ symbol: Annotated[
148
+ Literal["circle", "rect", "triangle", "diamond", "pin", "arrow", "none"] | None,
149
+ Field(description="图标类型"),
150
+ ] = "circle"
151
+ symbolSize: Annotated[int | None, Field(description="图标大小")] = None
152
+ yAxisIndex: Annotated[Literal[0, 1] | None, Field(description="Y轴索引")] = None
153
+ label: Annotated[TextStyle | None, Field(description="标签")] = None
154
+
155
+
156
+ class BarSeriesConfig(BaseModel):
157
+ """柱状图系列配置"""
158
+
159
+ borderStyle: Annotated[TextStyle | None, Field(description="边框样式")] = None
160
+ yAxisIndex: Annotated[Literal[0, 1] | None, Field(description="Y轴索引")] = None
161
+ label: Annotated[TextStyle | None, Field(description="标签")] = None
162
+
163
+
164
+ class PieLabel(BaseModel):
165
+ """饼图标签配置"""
166
+
167
+ show: Annotated[bool, Field(description="是否显示")] = True
168
+ position: Annotated[
169
+ Literal["default", "outside", "inside", "center"] | None, Field(description="位置")
170
+ ] = None
171
+ content: Annotated[
172
+ list[Literal["category", "value", "percent"]] | None, Field(description="显示内容")
173
+ ] = None
174
+ textStyle: Annotated[TextStyle | None, Field(description="文本样式")] = None
175
+
176
+
177
+ class PieSeriesConfig(BaseModel):
178
+ """饼图系列配置"""
179
+
180
+ borderColor: Annotated[str | None, Field(description="边框颜色")] = None
181
+ donutHole: Annotated[
182
+ Literal["0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%"] | None,
183
+ Field(description="环形挖空"),
184
+ ] = "0%"
185
+ startAngle: Annotated[int | None, Field(description="起始角度")] = None
186
+ label: Annotated[PieLabel | None, Field(description="标签")] = None
187
+
188
+
189
+ class SectorConfig(BaseModel):
190
+ """扇区配置"""
191
+
192
+ backgroundColor: Annotated[str | None, Field(description="背景色")] = None
193
+ borderColor: Annotated[str | None, Field(description="边框颜色")] = None
194
+
195
+
196
+ class ChartOptions(BaseModel):
197
+ """完整图表选项"""
198
+
199
+ theme: Annotated[str | None, Field(description="主题")] = None
200
+ gradientFill: Annotated[bool | None, Field(description="渐变填充")] = None
201
+ legend: Annotated[ChartLegend | None, Field(description="图例")] = None
202
+ label: Annotated[ChartLabel | None, Field(description="标签")] = None
203
+ xAxis: Annotated[ChartAxis | None, Field(description="X轴")] = None
204
+ yAxis: Annotated[ChartAxis | None, Field(description="Y轴")] = None
205
+ grid: Annotated[ChartGrid | None, Field(description="网格")] = None
206
+ fontStyle: Annotated[TextStyle | None, Field(description="字体样式")] = None
207
+ background: Annotated[ChartBackground | None, Field(description="背景")] = None
208
+ titleStyle: Annotated[TextStyle | None, Field(description="标题样式")] = None
209
+ axisTitle: Annotated[ChartAxisTitle | None, Field(description="轴标题")] = None
210
+ series: Annotated[LineSeriesConfig | BarSeriesConfig | None, Field(description="系列配置")] = (
211
+ None
212
+ )
213
+ seriesList: Annotated[
214
+ list[LineSeriesConfig | BarSeriesConfig],
215
+ Field(description="系列列表", default_factory=list),
216
+ ]
217
+ pieSeries: Annotated[PieSeriesConfig | None, Field(description="饼图系列")] = None
218
+ sectors: Annotated[list[SectorConfig], Field(description="扇区配置", default_factory=list)]
219
+
220
+
221
+ class WidgetChartData(BaseModel):
222
+ """图表卡片"""
223
+
224
+ type: Annotated[Literal["chart"], Field(description="卡片类型")] = "chart"
225
+ chartType: Annotated[ChartType, Field(description="图表类型")]
226
+ dimensions: Annotated[list[DimensionField], Field(description="维度列表", default_factory=list)]
227
+ measures: Annotated[list[MeasureField], Field(description="度量列表", default_factory=list)]
228
+ filters: Annotated[list[FilterField], Field(description="过滤条件", default_factory=list)]
229
+ sorts: Annotated[list[SortField], Field(description="排序列表", default_factory=list)]
230
+ pageSize: Annotated[int | None, Field(description="页面大小")] = 1000
231
+ options: Annotated[ChartOptions | None, Field(description="图表选项")] = None
232
+ datasource: Annotated[str, Field(description="数据源")]
233
+ metaType: Annotated[str, Field(description="元数据类型")]
234
+ metaName: Annotated[str, Field(description="元数据名称")]
235
+
236
+
237
+ # ============================================================
238
+ # Metric 卡片 Schema
239
+ # ============================================================
240
+
241
+
242
+ class MetricFontSizeType(StrEnum):
243
+ """指标字体大小类型"""
244
+
245
+ PRESET = "preset"
246
+ CUSTOM = "custom"
247
+
248
+
249
+ class MetricTitleStyle(BaseModel):
250
+ """指标标题样式"""
251
+
252
+ text: Annotated[str, Field(description="标题文本")] = "指标卡"
253
+ color: Annotated[str | None, Field(description="颜色")] = None
254
+ fontSize: Annotated[int | None, Field(description="字体大小")] = None
255
+
256
+
257
+ class MetricValueStyle(BaseModel):
258
+ """指标值样式"""
259
+
260
+ color: Annotated[str | None, Field(description="颜色")] = None
261
+ fontSize: Annotated[int | None, Field(description="字体大小")] = None
262
+
263
+
264
+ class MetricBackground(BaseModel):
265
+ """指标背景样式"""
266
+
267
+ color: Annotated[str | None, Field(description="背景色")] = None
268
+
269
+
270
+ class MetricStyle(BaseModel):
271
+ """指标样式配置"""
272
+
273
+ fontSizeType: Annotated[MetricFontSizeType | None, Field(description="字体大小类型")] = None
274
+ title: Annotated[MetricTitleStyle | None, Field(description="标题样式")] = None
275
+ value: Annotated[MetricValueStyle | None, Field(description="值样式")] = None
276
+ background: Annotated[MetricBackground | None, Field(description="背景样式")] = None
277
+
278
+
279
+ class MetricFormatType(StrEnum):
280
+ """指标数值格式类型"""
281
+
282
+ NUMBER = "number"
283
+ PERCENT = "percent"
284
+ THOUSAND = "thousand"
285
+ CUSTOM = "custom"
286
+
287
+
288
+ class MetricSuffixStyle(BaseModel):
289
+ """指标后缀样式"""
290
+
291
+ color: Annotated[str | None, Field(description="颜色")] = None
292
+ fontSize: Annotated[int | None, Field(description="字体大小")] = None
293
+
294
+
295
+ class MetricFormat(BaseModel):
296
+ """指标数值格式配置"""
297
+
298
+ type: Annotated[MetricFormatType | None, Field(description="格式类型")] = None
299
+ precision: Annotated[Literal[0, 1, 2, 3, 4] | None, Field(description="精度")] = None
300
+ prefix: Annotated[str | None, Field(description="前缀")] = None
301
+ suffix: Annotated[str | None, Field(description="后缀")] = None
302
+ suffixStyle: Annotated[MetricSuffixStyle | None, Field(description="后缀样式")] = None
303
+ thousandSeparator: Annotated[bool | None, Field(description="千分位分隔符")] = None
304
+
305
+
306
+ class WidgetMetricData(BaseModel):
307
+ """指标卡片"""
308
+
309
+ type: Annotated[Literal["metric"], Field(description="卡片类型")] = "metric"
310
+ style: Annotated[MetricStyle | None, Field(description="样式配置")] = None
311
+ format: Annotated[MetricFormat | None, Field(description="格式配置")] = None
312
+ dimensions: Annotated[list[DimensionField], Field(description="维度列表", default_factory=list)]
313
+ measures: Annotated[list[MeasureField], Field(description="度量列表", default_factory=list)]
314
+ filters: Annotated[list[FilterField], Field(description="过滤条件", default_factory=list)]
315
+ datasource: Annotated[str, Field(description="数据源")]
316
+ metaType: Annotated[str, Field(description="元数据类型")]
317
+ metaName: Annotated[str, Field(description="元数据名称")]
318
+
319
+
320
+ # ============================================================
321
+ # Detail 卡片 Schema
322
+ # ============================================================
323
+
324
+
325
+ class DetailDirection(StrEnum):
326
+ """明细卡片方向"""
327
+
328
+ HORIZONTAL = "horizontal"
329
+ VERTICAL = "vertical"
330
+
331
+
332
+ class DetailSize(StrEnum):
333
+ """明细卡片尺寸"""
334
+
335
+ LARGE = "large"
336
+ DEFAULT = "default"
337
+ SMALL = "small"
338
+
339
+
340
+ class DetailConfig(BaseModel):
341
+ """明细卡片配置"""
342
+
343
+ title: Annotated[str | None, Field(description="标题")] = None
344
+ column: Annotated[int, Field(description="列数", ge=1, le=10)] = 2
345
+ border: Annotated[bool, Field(description="是否显示边框")] = True
346
+ direction: Annotated[DetailDirection, Field(description="方向")] = DetailDirection.HORIZONTAL
347
+ size: Annotated[DetailSize, Field(description="尺寸")] = DetailSize.DEFAULT
348
+ labelWidth: Annotated[str | None, Field(description="标签宽度")] = None
349
+
350
+
351
+ class WidgetDetailData(BaseModel):
352
+ """明细卡片"""
353
+
354
+ type: Annotated[Literal["detail"], Field(description="卡片类型")] = "detail"
355
+ config: Annotated[DetailConfig, Field(description="配置")]
356
+ dimensions: Annotated[list[DimensionField], Field(description="维度列表", default_factory=list)]
357
+ measures: Annotated[list[MeasureField], Field(description="度量列表", default_factory=list)]
358
+ filters: Annotated[list[FilterField], Field(description="过滤条件", default_factory=list)]
359
+ datasource: Annotated[str, Field(description="数据源")]
360
+ metaType: Annotated[str, Field(description="元数据类型")]
361
+ metaName: Annotated[str, Field(description="元数据名称")]
362
+
363
+
364
+ # ============================================================
365
+ # Topology 卡片 Schema
366
+ # ============================================================
367
+
368
+
369
+ class TopologyNodeStyle(BaseModel):
370
+ """拓扑节点样式"""
371
+
372
+ shape: Annotated[str | None, Field(description="形状")] = None
373
+ color: Annotated[str | None, Field(description="颜色")] = None
374
+ size: Annotated[str | None, Field(description="大小")] = None
375
+
376
+
377
+ class TopologyEdgeStyle(BaseModel):
378
+ """拓扑边样式"""
379
+
380
+ color: Annotated[str | None, Field(description="颜色")] = None
381
+ lineStyle: Annotated[str | None, Field(description="线条样式")] = None
382
+ width: Annotated[int | None, Field(description="宽度")] = None
383
+
384
+
385
+ class WorkflowInputItem(BaseModel):
386
+ """工作流输入参数"""
387
+
388
+ name: Annotated[str, Field(description="参数名")]
389
+ title: Annotated[str | None, Field(description="显示标题")] = None
390
+ type: Annotated[str, Field(description="参数类型")]
391
+ required: Annotated[bool, Field(description="是否必填")] = False
392
+ defaultValue: Annotated[Any, Field(description="默认值")] = None
393
+ description: Annotated[str | None, Field(description="描述")] = None
394
+
395
+
396
+ class WorkflowTreeNode(BaseModel):
397
+ """工作流树节点"""
398
+
399
+ id: Annotated[str, Field(description="节点ID")]
400
+ label: Annotated[str, Field(description="显示标签")]
401
+ name: Annotated[str | None, Field(description="节点名称")] = None
402
+ type: Annotated[Literal["group", "workflow"], Field(description="节点类型")]
403
+ children: Annotated[list["WorkflowTreeNode"], Field(description="子节点", default_factory=list)]
404
+ code: Annotated[str | None, Field(description="编码")] = None
405
+
406
+
407
+ class WorkflowConfig(BaseModel):
408
+ """工作流配置"""
409
+
410
+ flowCode: Annotated[str | None, Field(description="流程编码")] = None
411
+ params: Annotated[dict[str, Any] | None, Field(description="流程参数")] = None
412
+ topologyStyle: Annotated[dict[str, Any] | None, Field(description="拓扑样式")] = None
413
+
414
+
415
+ class WidgetTopologyData(BaseModel):
416
+ """拓扑卡片"""
417
+
418
+ type: Annotated[Literal["topology"], Field(description="卡片类型")] = "topology"
419
+ layoutType: Annotated[str, Field(description="布局类型")] = "dagre"
420
+ nodeStyle: Annotated[TopologyNodeStyle | None, Field(description="节点样式")] = None
421
+ edgeStyle: Annotated[TopologyEdgeStyle | None, Field(description="边样式")] = None
422
+ metaType: Annotated[str, Field(description="元数据类型")]
423
+ metaName: Annotated[str, Field(description="元数据名称")]
424
+ workflowConfig: Annotated[WorkflowConfig | None, Field(description="工作流配置")] = None
425
+ inputItems: Annotated[
426
+ list[WorkflowInputItem], Field(description="输入项", default_factory=list)
427
+ ]
428
+ treeData: Annotated[list[WorkflowTreeNode], Field(description="树形数据", default_factory=list)]
429
+
430
+
431
+ # ============================================================
432
+ # 旧版 Table 卡片 Schema(向后兼容)
433
+ # ============================================================
434
+
435
+
436
+ class WidgetDimension(BaseModel):
437
+ """卡片表格维度"""
438
+
439
+ name: Annotated[str, Field(description="字段名")]
440
+ title: Annotated[str, Field(description="显示标题")]
441
+ nullable: Annotated[bool, Field(description="是否可空")]
442
+ dbType: Annotated[str, Field(description="数据库类型")]
443
+ length: Annotated[int, Field(description="长度")]
444
+ labelKey: Annotated[bool | None, Field(description="是否为标签字段")] = None
445
+ pk: Annotated[bool | None, Field(description="是否主键")] = None
446
+ relId: Annotated[str | None, Field(description="关联ID")] = None
447
+ relType: Annotated[str | None, Field(description="关联类型")] = None
448
+ id: Annotated[str, Field(description="维度ID")]
449
+ type: Annotated[str | None, Field(description="类型")] = None
450
+ remark: Annotated[str | None, Field(description="备注")] = None
451
+ format: Annotated["WidgetFormat | None", Field(description="格式化")] = None
452
+ link: Annotated["WidgetLink | None", Field(description="链接")] = None
453
+ cellStyle: Annotated["WidgetCellStyle | None", Field(description="单元格样式")] = None
454
+ typeExt: Annotated["WidgetTypeExt | None", Field(description="类型扩展")] = None
455
+
456
+
457
+ class WidgetQuery(BaseModel):
458
+ """卡片表格查询条件"""
459
+
460
+ id: Annotated[str, Field(description="查询条件ID")]
461
+ name: Annotated[str, Field(description="字段名")]
462
+ title: Annotated[str, Field(description="显示标题")]
463
+ dbType: Annotated[str, Field(description="数据库类型")]
464
+ plugin: Annotated["WidgetPlugin", Field(description="插件配置")]
465
+ condition: Annotated["WidgetCondition", Field(description="条件配置")]
466
+
467
+
468
+ class WidgetFormat(BaseModel):
469
+ """格式化配置"""
470
+
471
+ type: Annotated[str, Field(description="格式类型")]
472
+
473
+
474
+ class WidgetLink(BaseModel):
475
+ """链接配置"""
476
+
477
+ type: Annotated[str, Field(description="链接类型")]
478
+
479
+
480
+ class WidgetCellStyle(BaseModel):
481
+ """单元格样式"""
482
+
483
+ rules: Annotated[list, Field(description="样式规则")] = []
484
+
485
+
486
+ class WidgetPlugin(BaseModel):
487
+ """插件配置"""
488
+
489
+ type: Annotated[str, Field(description="插件类型")]
490
+
491
+
492
+ class WidgetCondition(BaseModel):
493
+ """查询条件"""
494
+
495
+ expression: Annotated[str, Field(description="表达式")] = "eq"
496
+
497
+
498
+ class WidgetTypeExt(BaseModel):
499
+ """类型扩展"""
500
+
501
+ ref__table: Annotated["WidgetRefTable | None", Field(description="引用表")] = None
502
+ ref__valueField: Annotated["WidgetRefValueField | None", Field(description="引用值字段")] = None
503
+
504
+
505
+ class WidgetRefTable(BaseModel):
506
+ """引用表"""
507
+
508
+ type: Annotated[str, Field(description="类型")] = "table"
509
+ code: Annotated[str | None, Field(description="编码")] = None
510
+
511
+
512
+ class WidgetRefValueField(BaseModel):
513
+ """引用值字段"""
514
+
515
+ type: Annotated[str, Field(description="类型")] = "table"
516
+ code: Annotated[str | None, Field(description="编码")] = None
517
+ subtype: Annotated[str | None, Field(description="子类型")] = None
518
+ subcode: Annotated[str | None, Field(description="子编码")] = None
519
+
520
+
521
+ class WidgetTable(BaseModel):
522
+ """卡片表格配置(兼容旧版)"""
523
+
524
+ type: Annotated[Literal["table"], Field(description="卡片类型")] = "table"
525
+ filters: Annotated[list, Field(description="过滤条件")] = []
526
+ datasource: Annotated[str, Field(description="数据源")]
527
+ metaType: Annotated[str, Field(description="元数据类型")]
528
+ metaName: Annotated[str, Field(description="元数据名称")]
529
+ dimensions: Annotated[list[WidgetDimension], Field(description="维度列表")] = []
530
+ querys: Annotated[list[WidgetQuery], Field(description="查询条件列表")] = []
531
+
532
+
533
+ # ============================================================
534
+ # GIS 卡片 Schema (v1)
535
+ # 前端 IdealGisView 共用
536
+ # 后端 subType="gis",数据 key 为 "gis"
537
+ # ============================================================
538
+
539
+
540
+ class GisLayer(BaseModel):
541
+ """GIS 图层"""
542
+
543
+ id: Annotated[str, Field(description="图层ID")]
544
+ name: Annotated[str, Field(description="图层名称")]
545
+ color: Annotated[str | None, Field(description="颜色")] = None
546
+ layerType: Annotated[str | None, Field(description="图层类型")] = None
547
+
548
+
549
+ class WidgetGisData(BaseModel):
550
+ """GIS 卡片"""
551
+
552
+ type: Annotated[Literal["gis"], Field(description="卡片类型")] = "gis"
553
+ mapToken: Annotated[str | None, Field(description="Mapbox Token")] = None
554
+ layers: Annotated[list[GisLayer], Field(description="图层列表", default_factory=list)]
555
+ datasource: Annotated[str, Field(description="数据源")]
556
+ metaType: Annotated[str, Field(description="元数据类型")]
557
+ metaName: Annotated[str, Field(description="元数据名称")]
558
+
559
+
560
+ # ============================================================
561
+ # AI 设计卡片 Schema (v1)
562
+ # 前端 IdealStatisticView 共用
563
+ # 后端 subType="statistic",数据 key 为 "statistic"
564
+ # ============================================================
565
+
566
+
567
+ class WidgetStatisticData(BaseModel):
568
+ """AI 设计卡片"""
569
+
570
+ type: Annotated[Literal["statistic"], Field(description="卡片类型")] = "statistic"
571
+ cssText: Annotated[str | None, Field(description="自定义 CSS")] = None
572
+ datasource: Annotated[str, Field(description="数据源")]
573
+ metaType: Annotated[str, Field(description="元数据类型")]
574
+ metaName: Annotated[str, Field(description="元数据名称")]
575
+
576
+
577
+ # ============================================================
578
+ # 统一卡片数据联合类型(带 pydantic discriminator)
579
+ # ============================================================
580
+
581
+
582
+ # 所有卡片数据类型的联合(用于 pydantic discriminated union)
583
+ # pydantic 根据每个子类的 `type` 字段自动识别具体类型
584
+ WidgetData = Annotated[
585
+ WidgetChartData
586
+ | WidgetMetricData
587
+ | WidgetDetailData
588
+ | WidgetTopologyData
589
+ | WidgetTable
590
+ | WidgetGisData
591
+ | WidgetStatisticData,
592
+ Field(discriminator="type"),
593
+ ]
594
+
595
+
596
+ class WidgetMetaData(BaseModel):
597
+ """卡片元数据.
598
+
599
+ 后端 rawData 结构: {"<subType>": {...}, "tenantId": ..., "code": ..., ...}
600
+ 其中 subType 的值 (chart/metric/detail/topology/table/gis/statistic) 对应子类型 key 名称。
601
+
602
+ 通过 model_validator 将 meta.data[subType] 路由到 widget_data 字段,
603
+ 使 meta.data 成为 discriminated union 类型,方便 CLI 层做子类型分派渲染。
604
+ """
605
+
606
+ widget_data: Annotated[
607
+ WidgetChartData
608
+ | WidgetMetricData
609
+ | WidgetDetailData
610
+ | WidgetTopologyData
611
+ | WidgetTable
612
+ | WidgetGisData
613
+ | WidgetStatisticData,
614
+ Field(discriminator="type", description="卡片子类型数据(按 subType 路由)"),
615
+ ]
616
+ tenantId: Annotated[str, Field(description="租户ID")]
617
+ enterpriseCuid: Annotated[str, Field(description="企业CUID")]
618
+ name: Annotated[str, Field(description="名称")]
619
+ code: Annotated[str, Field(description="编码")]
620
+ title: Annotated[str, Field(description="标题")]
621
+ subType: Annotated[str | None, Field(description="子类型")] = None
622
+ groupCode: Annotated[str | None, Field(description="分组编码")] = None
623
+
624
+ @model_validator(mode="before")
625
+ @classmethod
626
+ def _route_subtype(cls, values: dict) -> dict:
627
+ """根据 subType 将 meta.data[subType] 路由到 widget_data 字段."""
628
+ sub_type: str | None = values.get("subType")
629
+ data: dict = values.get("data") or {}
630
+ if sub_type and sub_type in data:
631
+ # 子类型数据在 meta.data[subType] 中
632
+ values["widget_data"] = data[sub_type]
633
+ elif not sub_type:
634
+ # 兜底:遍历所有已知子类型 key,找第一个匹配的
635
+ for key in (
636
+ "chart",
637
+ "metric",
638
+ "detail",
639
+ "topology",
640
+ "table",
641
+ "gis",
642
+ "statistic",
643
+ ):
644
+ if key in data:
645
+ values["widget_data"] = data[key]
646
+ break
647
+ return values
648
+
649
+
650
+ class WidgetMeta(Meta[WidgetMetaData]):
651
+ """卡片 Meta"""