@axiom-lattice/gateway 2.1.39 → 2.1.41
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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +15 -0
- package/dist/index.js +464 -170
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +415 -118
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
- package/public/sdk/README.md +695 -0
- package/public/sdk/dashboard-engine-skill.md +1122 -0
- package/public/sdk/dashboard-single-file-spec.md +357 -0
- package/public/sdk/data-query-sdk-skill.md +307 -0
- package/public/sdk/data-query-sdk.d.ts +252 -0
- package/public/sdk/data-query-sdk.js +970 -0
- package/public/sdk/occupancy-dashboard.html +363 -0
- package/public/sdk/test-dashboard.html +690 -0
- package/src/__tests__/data-query.test.ts +77 -0
- package/src/controllers/data-query.ts +236 -0
- package/src/controllers/metrics-configs.ts +29 -25
- package/src/controllers/workspace.ts +95 -1
- package/src/index.ts +11 -0
- package/src/routes/index.ts +11 -0
- package/src/schemas/data-query.ts +69 -0
- package/src/schemas/index.ts +3 -0
- package/src/services/agent_task_consumer.ts +2 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# Single-File Dashboard Specification (Data Query SDK v3.0 + ECharts 5.x)
|
|
2
|
+
|
|
3
|
+
**Role:** Senior Data Visualization Expert & Frontend Architect (SDK Deep Integration)
|
|
4
|
+
|
|
5
|
+
**Objective:** Generate a complete, interactive, runnable single-file Dashboard based on Data Query SDK v3.0 and ECharts 5.x, with modern SaaS visual quality.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. SDK Technical Specification (Must Strictly Follow)
|
|
10
|
+
|
|
11
|
+
### 1.1 Resource Inclusion (Required)
|
|
12
|
+
|
|
13
|
+
In the generated HTML file, the SDK script **must** be included first among script tags:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<script src="http://localhost:4001/sdk/data-query-sdk.js"></script>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 1.2 Class Definition and Initialization
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
const sdk = new DataQuerySDK({
|
|
23
|
+
baseURL: 'http://localhost:4001',
|
|
24
|
+
serverKey: 'prod',
|
|
25
|
+
datasourceId: '1'
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 1.3 Method Signature: `sdk.query(params)`
|
|
30
|
+
|
|
31
|
+
**Input `params` structure:**
|
|
32
|
+
|
|
33
|
+
| Field | Type | Required | Description |
|
|
34
|
+
|----------|----------|----------|-------------|
|
|
35
|
+
| `metrics` | `string[]` | ✅ | e.g. `['sales_amt', 'order_cnt']` |
|
|
36
|
+
| `groupBy` | `string[]` | ❌ | Dimension names or granularity expressions: `date__day`, `region` |
|
|
37
|
+
| `filters` | `Array<{dimension: string, operator: string, values: any[]}>` | ❌ | Filter conditions |
|
|
38
|
+
| `orderBy` | `Array<{field: string, direction: 'ASC'\|'DESC'}>` | ❌ | Sort rules |
|
|
39
|
+
| `limit` | `number` | ❌ | 1–20000 |
|
|
40
|
+
|
|
41
|
+
**Core rules:**
|
|
42
|
+
|
|
43
|
+
- **Do not** use granularity expressions in `filters` (e.g. `date__month`); use **raw dimension names** only (e.g. `date`).
|
|
44
|
+
- For date filtering, use `operator: 'BETWEEN'` and `values: ['YYYY-MM-DD', 'YYYY-MM-DD']`.
|
|
45
|
+
|
|
46
|
+
### 1.4 Response Structure
|
|
47
|
+
|
|
48
|
+
SDK returns a Promise resolving to:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"code": 200,
|
|
53
|
+
"data": {
|
|
54
|
+
"columns": [
|
|
55
|
+
{ "name": "date__month", "type": "date" },
|
|
56
|
+
{ "name": "sales_amt", "type": "numeric" }
|
|
57
|
+
],
|
|
58
|
+
"rows": [
|
|
59
|
+
["2025-01-01", 150000],
|
|
60
|
+
["2025-02-01", 180000]
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 2. Data Adaptation and ECharts Configuration Patterns
|
|
69
|
+
|
|
70
|
+
### 2.1 Universal Adapter Function
|
|
71
|
+
|
|
72
|
+
Generated code **must** include this function to convert SDK data into ECharts `dataset.source` format:
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
function adaptToECharts(sdkResponse) {
|
|
76
|
+
const header = sdkResponse.data.columns.map(c => c.name);
|
|
77
|
+
return [header, ...sdkResponse.data.rows];
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2.2 Pattern A: KPI Sparkline (Mini Trend)
|
|
82
|
+
|
|
83
|
+
Use for **Tier 1**. Hide axes to emphasize trend.
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const sparklineOption = {
|
|
87
|
+
dataset: { source: adaptToECharts(sdkData) },
|
|
88
|
+
xAxis: { type: 'category', show: false },
|
|
89
|
+
yAxis: { type: 'value', show: false },
|
|
90
|
+
series: [{ type: 'line', smooth: true, areaStyle: { opacity: 0.1 }, symbol: 'none' }]
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 2.3 Pattern B: Multi-Series Stacked Area Chart
|
|
95
|
+
|
|
96
|
+
Use for **Tier 2**. Show dimension breakdown (e.g. region) over time.
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const trendOption = {
|
|
100
|
+
dataset: { source: adaptToECharts(sdkData) },
|
|
101
|
+
tooltip: { trigger: 'axis' },
|
|
102
|
+
legend: {},
|
|
103
|
+
xAxis: { type: 'category' },
|
|
104
|
+
yAxis: { type: 'value' },
|
|
105
|
+
series: [{ type: 'line', stack: 'Total', areaStyle: {}, smooth: true }]
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 2.4 Pattern C: Dimension Ranking Bar Chart
|
|
110
|
+
|
|
111
|
+
Use for **Tier 3**. Top N by dimension.
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const barOption = {
|
|
115
|
+
dataset: { source: adaptToECharts(sdkData) },
|
|
116
|
+
xAxis: { type: 'value' },
|
|
117
|
+
yAxis: { type: 'category' },
|
|
118
|
+
series: [{ type: 'bar', encode: { x: 'sales_amt', y: 'shop_name' } }]
|
|
119
|
+
};
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 3. Dashboard Architecture (Tiered Layout)
|
|
125
|
+
|
|
126
|
+
### Tier 1: Hero Metrics (Top)
|
|
127
|
+
|
|
128
|
+
- **Components:** Large KPI cards + mini ECharts sparklines.
|
|
129
|
+
- **Logic:** Query last 30 days totals, with or without daily `groupBy`.
|
|
130
|
+
|
|
131
|
+
### Tier 2: Driver Trends (Middle)
|
|
132
|
+
|
|
133
|
+
- **Components:** ECharts stacked area or grouped bar charts.
|
|
134
|
+
- **Logic:** Dimension trends; handle multi-series (e.g. Region over time).
|
|
135
|
+
|
|
136
|
+
### Tier 3: Diagnostic Details (Bottom)
|
|
137
|
+
|
|
138
|
+
- **Components:** Data table with heat gradient or heatmap.
|
|
139
|
+
- **Logic:** Use `limit: 100` and `orderBy` for root-cause analysis.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 4. Interaction and Linkage
|
|
144
|
+
|
|
145
|
+
### 4.1 Global Filters
|
|
146
|
+
|
|
147
|
+
Implement a UI with:
|
|
148
|
+
|
|
149
|
+
- Date range picker.
|
|
150
|
+
- Dimension selector(s) (e.g. region, category).
|
|
151
|
+
|
|
152
|
+
### 4.2 Chart Click Linkage
|
|
153
|
+
|
|
154
|
+
Listen to ECharts `click` and sync with global filter:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
chart.on('click', (params) => {
|
|
158
|
+
const selectedDim = params.name;
|
|
159
|
+
// Update global filter state, then call refreshAllCharts()
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 5. Output Format and Code Rules
|
|
166
|
+
|
|
167
|
+
- **Single file:** One complete `.html` file including CSS and all JS.
|
|
168
|
+
- **Resources:** Include the SDK script URL specified above.
|
|
169
|
+
- **Robustness:** Implement simple exponential-backoff retry for SDK calls.
|
|
170
|
+
- **Visual:** Clean business style, rounded corners, ample whitespace.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 6. Visual Enhancement Specification (Modern SaaS Quality)
|
|
175
|
+
|
|
176
|
+
Generated dashboards **must** apply the following to achieve a modern, professional SaaS look. Do not remove or simplify the above SDK/data/tier/interaction requirements; add these on top.
|
|
177
|
+
|
|
178
|
+
### 6.1 Icon Library: Lucide Icons
|
|
179
|
+
|
|
180
|
+
- **Include:** Lucide Icons via CDN (e.g. `https://unpkg.com/lucide@latest` or `lucide-static` script).
|
|
181
|
+
- **Usage:** Use `data-lucide` attribute on elements; call `lucide.createIcons()` after DOM updates.
|
|
182
|
+
- **Where:** KPI cards (e.g. `data-lucide="trending-up"`, `data-lucide="dollar-sign"`, `data-lucide="bar-chart-2"`), filter bar (e.g. `data-lucide="calendar"`, `data-lucide="filter"`), and section headers where appropriate.
|
|
183
|
+
- **Effect:** Clear visual cues and consistent “app” feel.
|
|
184
|
+
|
|
185
|
+
### 6.2 Typography: Google Fonts (Inter or Equivalent)
|
|
186
|
+
|
|
187
|
+
- **Include:** One primary font from Google Fonts (e.g. Inter) in `<head>`:
|
|
188
|
+
```html
|
|
189
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
190
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
191
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
192
|
+
```
|
|
193
|
+
- **Apply:** Set `font-family: 'Inter', sans-serif` (or chosen font) on `body` or a root container.
|
|
194
|
+
- **Effect:** Improved readability and professional typography vs. system defaults.
|
|
195
|
+
|
|
196
|
+
### 6.3 UI Style: Shadcn-Inspired (Tailwind)
|
|
197
|
+
|
|
198
|
+
- **Refined radius:** Use consistent rounded corners (e.g. `rounded-lg` / `rounded-xl` for cards, `rounded-md` for inputs and buttons).
|
|
199
|
+
- **Shadows:** Subtle elevation: e.g. `shadow-sm` for inputs, `shadow` or `shadow-md` for cards; avoid heavy flat blocks.
|
|
200
|
+
- **Borders:** Light borders where needed (e.g. `border border-gray-200/80` or `border-border` if using CSS variables); optional subtle ring on focus for inputs/buttons.
|
|
201
|
+
- **Color:** Neutral background (e.g. `bg-slate-50` or `bg-gray-50`), white or near-white cards; accent for primary actions (e.g. blue or brand color); success/warning/danger for KPI deltas and status.
|
|
202
|
+
- **Effect:** Cohesive, polished component look similar to Shadcn UI.
|
|
203
|
+
|
|
204
|
+
### 6.4 Glass-Card / Layered Cards
|
|
205
|
+
|
|
206
|
+
- **Concept:** Optional “glass” effect for key cards: semi-transparent background with backdrop blur (e.g. `bg-white/80 dark:bg-white/90 backdrop-blur-sm`) and a subtle border (e.g. `border border-white/20` or `border-gray-200/60`).
|
|
207
|
+
- **Where:** Tier 1 KPI cards and/or the filter bar; keep tables and dense charts on solid backgrounds for readability.
|
|
208
|
+
- **Effect:** Light, modern layering and depth without clutter.
|
|
209
|
+
|
|
210
|
+
### 6.5 Loading States
|
|
211
|
+
|
|
212
|
+
- **During data fetch:** Show loading state for each tier (or per widget) instead of blank space.
|
|
213
|
+
- **Options:** Skeleton placeholders (e.g. animated `bg-gray-200` / `animate-pulse` for numbers and chart areas), or a small spinner with “Loading…” text.
|
|
214
|
+
- **Placement:** Same layout as final content (e.g. skeleton height similar to sparkline, table rows, or chart container).
|
|
215
|
+
- **Clear transition:** When data arrives, replace loading UI with real content; avoid flash of wrong layout.
|
|
216
|
+
- **Effect:** Clear feedback and perceived performance.
|
|
217
|
+
|
|
218
|
+
### 6.6 Responsive Layout (Tailwind)
|
|
219
|
+
|
|
220
|
+
- **Breakpoints:** Use Tailwind responsive prefixes (`sm:`, `md:`, `lg:`) for grid and flex:
|
|
221
|
+
- Tier 1: e.g. 1 column on mobile, 2–4 columns on larger screens.
|
|
222
|
+
- Tier 2: Full width or 2-column grid on large screens.
|
|
223
|
+
- Tier 3: Horizontal scroll for wide tables when needed (`overflow-x-auto`).
|
|
224
|
+
- **Touch:** Ensure buttons and filters are comfortably tappable; charts should resize on `window.resize` (ECharts `resize()`).
|
|
225
|
+
- **Effect:** Usable and refined on all screen sizes.
|
|
226
|
+
|
|
227
|
+
### 6.7 Summary Checklist
|
|
228
|
+
|
|
229
|
+
| Item | Requirement |
|
|
230
|
+
|-----------------|------------|
|
|
231
|
+
| Lucide Icons | Included; `data-lucide` + `lucide.createIcons()` on KPI/filters/headers |
|
|
232
|
+
| Google Font | Inter or equivalent; applied to body |
|
|
233
|
+
| Shadcn-style | Rounded corners, subtle shadows, light borders, neutral + accent palette |
|
|
234
|
+
| Glass-card | Optional glass effect on hero/filter cards |
|
|
235
|
+
| Loading states | Skeleton or spinner per section while loading |
|
|
236
|
+
| Responsive | Tailwind breakpoints; ECharts resize on resize |
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 7. Complete Code Example (Reference Paradigm)
|
|
241
|
+
|
|
242
|
+
The following is a minimal reference that satisfies **Sections 1–5** and aligns with **Section 6**. Expand with full Tier 2/3 and all visual enhancements as specified above.
|
|
243
|
+
|
|
244
|
+
```html
|
|
245
|
+
<!DOCTYPE html>
|
|
246
|
+
<html lang="zh-CN">
|
|
247
|
+
<head>
|
|
248
|
+
<meta charset="UTF-8">
|
|
249
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
250
|
+
<title>业务监控看板</title>
|
|
251
|
+
<!-- Google Font -->
|
|
252
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
253
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
254
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
255
|
+
<!-- Tailwind -->
|
|
256
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
257
|
+
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
|
258
|
+
<!-- Lucide Icons -->
|
|
259
|
+
<script src="https://unpkg.com/lucide@latest"></script>
|
|
260
|
+
<!-- Required: SDK -->
|
|
261
|
+
<script src="http://localhost:4001/sdk/data-query-sdk.js"></script>
|
|
262
|
+
<style>
|
|
263
|
+
body { font-family: 'Inter', sans-serif; background-color: #f8fafc; }
|
|
264
|
+
.card { background: white; border-radius: 12px; padding: 20px; box-shadow: 0 1px 3px rgb(0 0 0 / 0.08); border: 1px solid rgb(0 0 0 / 0.06); }
|
|
265
|
+
.glass-card { background: rgba(255,255,255,0.85); backdrop-filter: blur(8px); border-radius: 12px; border: 1px solid rgba(0,0,0,0.06); }
|
|
266
|
+
.skeleton { background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%); background-size: 200% 100%; animation: skeleton 1.5s ease-in-out infinite; }
|
|
267
|
+
@keyframes skeleton { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
268
|
+
</style>
|
|
269
|
+
</head>
|
|
270
|
+
<body class="p-6">
|
|
271
|
+
<!-- Global filters -->
|
|
272
|
+
<div class="flex flex-wrap gap-4 mb-6 p-4 rounded-xl shadow-sm glass-card">
|
|
273
|
+
<div>
|
|
274
|
+
<label class="block text-xs text-gray-500 mb-1"><i data-lucide="calendar" class="inline w-3.5 h-3.5"></i> 日期范围</label>
|
|
275
|
+
<input type="date" id="start" class="border border-gray-200 rounded-lg px-3 py-2 text-sm" value="2025-01-01">
|
|
276
|
+
<input type="date" id="end" class="border border-gray-200 rounded-lg px-3 py-2 text-sm ml-1" value="2025-01-31">
|
|
277
|
+
</div>
|
|
278
|
+
<button onclick="refresh()" class="bg-blue-600 text-white px-6 py-2 rounded-lg self-end hover:bg-blue-700 transition flex items-center gap-2">
|
|
279
|
+
<i data-lucide="refresh-cw" class="w-4 h-4"></i> 查询数据
|
|
280
|
+
</button>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
|
284
|
+
<!-- Tier 1: KPI + Sparkline -->
|
|
285
|
+
<div class="card">
|
|
286
|
+
<h3 class="text-gray-500 text-sm flex items-center gap-1"><i data-lucide="trending-up" class="w-4 h-4"></i> 核心营收 (GMV)</h3>
|
|
287
|
+
<div id="kpi-value-1" class="text-3xl font-bold my-2 text-slate-800">¥0</div>
|
|
288
|
+
<div id="sparkline-1" style="height: 60px;"></div>
|
|
289
|
+
</div>
|
|
290
|
+
<!-- More KPI cards... -->
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<!-- Tier 2: Trend -->
|
|
294
|
+
<div class="card mb-6">
|
|
295
|
+
<h3 class="font-bold mb-4 flex items-center gap-2"><i data-lucide="bar-chart-2" class="w-5 h-5"></i> 月度地区销售趋势</h3>
|
|
296
|
+
<div id="trend-chart" style="height: 400px;"></div>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<!-- Tier 3: Table -->
|
|
300
|
+
<div class="card">
|
|
301
|
+
<h3 class="font-bold mb-4 flex items-center gap-2"><i data-lucide="table" class="w-5 h-5"></i> 店铺表现明细 (Top 50)</h3>
|
|
302
|
+
<div class="overflow-x-auto">
|
|
303
|
+
<table id="detail-table" class="w-full text-left">
|
|
304
|
+
<thead>
|
|
305
|
+
<tr class="border-b text-gray-400 text-sm"><th class="pb-2">店铺名称</th><th class="pb-2">销售额</th><th class="pb-2">订单数</th></tr>
|
|
306
|
+
</thead>
|
|
307
|
+
<tbody class="text-slate-700"></tbody>
|
|
308
|
+
</table>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<script>
|
|
313
|
+
const sdk = new DataQuerySDK({ baseURL: 'http://localhost:4001', serverKey: 'prod', datasourceId: '1' });
|
|
314
|
+
const adaptToECharts = (res) => [res.data.columns.map(c => c.name), ...res.data.rows];
|
|
315
|
+
|
|
316
|
+
async function refresh() {
|
|
317
|
+
lucide.createIcons();
|
|
318
|
+
const start = document.getElementById('start').value;
|
|
319
|
+
const end = document.getElementById('end').value;
|
|
320
|
+
const timeFilter = { dimension: 'date', operator: 'BETWEEN', values: [start, end] };
|
|
321
|
+
try {
|
|
322
|
+
const kpiRes = await sdk.query({ metrics: ['sales_amt'], groupBy: ['date__day'], filters: [timeFilter] });
|
|
323
|
+
renderKPI(kpiRes);
|
|
324
|
+
const trendRes = await sdk.query({ metrics: ['sales_amt'], groupBy: ['date__month', 'region'], filters: [timeFilter] });
|
|
325
|
+
renderTrend(trendRes);
|
|
326
|
+
const tableRes = await sdk.query({ metrics: ['sales_amt', 'order_cnt'], groupBy: ['shop_name'], filters: [timeFilter], orderBy: [{ field: 'sales_amt', direction: 'DESC' }], limit: 50 });
|
|
327
|
+
renderTable(tableRes);
|
|
328
|
+
} catch (err) { console.error("数据请求失败", err); }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function renderTrend(res) {
|
|
332
|
+
const chart = echarts.init(document.getElementById('trend-chart'));
|
|
333
|
+
chart.setOption({
|
|
334
|
+
dataset: { source: adaptToECharts(res) },
|
|
335
|
+
tooltip: { trigger: 'axis' },
|
|
336
|
+
xAxis: { type: 'category' },
|
|
337
|
+
yAxis: { type: 'value' },
|
|
338
|
+
series: [{ type: 'line', stack: 'Total', areaStyle: {}, smooth: true }]
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function renderTable(res) {
|
|
343
|
+
const tbody = document.querySelector('#detail-table tbody');
|
|
344
|
+
tbody.innerHTML = res.data.rows.map(row =>
|
|
345
|
+
`<tr class="border-b"><td class="py-3">${row[0]}</td><td>¥${row[1]}</td><td>${row[2]}</td></tr>`
|
|
346
|
+
).join('');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
window.onload = () => { lucide.createIcons(); refresh(); };
|
|
350
|
+
</script>
|
|
351
|
+
</body>
|
|
352
|
+
</html>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
**Summary:** Follow Sections 1–5 for SDK, data, layout, and interaction. Apply Section 6 for Lucide, Inter, Shadcn-style, glass-card, loading states, and responsive behavior. Use Section 7 as a minimal reference; full implementations should include retry logic, chart click linkage, and all three tiers with the enhanced visuals above.
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: data-query-sdk
|
|
3
|
+
description: Use when using Data Query SDK v3.0 to query metrics data, build filters, or construct data queries for dashboards and reports
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Data Query SDK v3.0
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Data Query SDK 用于从指标服务器查询数据,支持多维度分组、过滤、排序等操作。
|
|
11
|
+
|
|
12
|
+
**核心功能:**
|
|
13
|
+
- 指标查询(metrics)
|
|
14
|
+
- 维度分组(groupBy)
|
|
15
|
+
- 过滤条件(filters)
|
|
16
|
+
- 排序规则(orderBy)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 初始化
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
const sdk = new DataQuerySDK({
|
|
24
|
+
baseURL: 'http://localhost:4001',
|
|
25
|
+
serverKey: 'prod', // 指标服务器 key
|
|
26
|
+
datasourceId: '1' // 数据源 ID
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Query 结构
|
|
33
|
+
|
|
34
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
35
|
+
|------|------|------|------|
|
|
36
|
+
| `serverKey` | `string` | ✅ | 指标服务器 key |
|
|
37
|
+
| `datasourceId` | `string` | ✅ | 数据源 ID |
|
|
38
|
+
| `metrics` | `string[]` | ✅ | 指标名列表,来自 meta 中的 metric name |
|
|
39
|
+
| `groupBy` | `string[]` | ❌ | 分组维度,可为维度名或粒度表达式 |
|
|
40
|
+
| `filters` | `object[]` | ❌ | 过滤条件数组 |
|
|
41
|
+
| `orderBy` | `object[]` | ❌ | 排序规则数组 |
|
|
42
|
+
| `limit` | `integer` | ❌ | 返回行数上限 [1, 20000] |
|
|
43
|
+
| `debug` | `boolean` | ❌ | 为 true 时返回 debug 信息 |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 时间粒度表达式
|
|
48
|
+
|
|
49
|
+
**仅用于 `groupBy`**:
|
|
50
|
+
|
|
51
|
+
| 表达式 | 说明 |
|
|
52
|
+
|--------|------|
|
|
53
|
+
| `{dimension}__day` | 按天分组 |
|
|
54
|
+
| `{dimension}__week` | 按周分组 |
|
|
55
|
+
| `{dimension}__month` | 按月分组 |
|
|
56
|
+
| `{dimension}__quarter` | 按季度分组 |
|
|
57
|
+
| `{dimension}__year` | 按年分组 |
|
|
58
|
+
|
|
59
|
+
**⚠️ 重要区别:**
|
|
60
|
+
- `groupBy`:可以使用粒度表达式(如 `date__month`)
|
|
61
|
+
- `filters.dimension`:必须使用**原始维度字段**(如 `date`)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 过滤条件
|
|
66
|
+
|
|
67
|
+
### Filter 结构
|
|
68
|
+
|
|
69
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
70
|
+
|------|------|------|------|
|
|
71
|
+
| `dimension` | `string` | ✅ | 维度名,须与 meta 中定义一致 |
|
|
72
|
+
| `operator` | `string` | ✅ | 操作符,见下方列表 |
|
|
73
|
+
| `values` | `string[]` | 依操作符 | 用于比较的值 |
|
|
74
|
+
|
|
75
|
+
### 支持的操作符
|
|
76
|
+
|
|
77
|
+
| 操作符 | 说明 | 适用场景 |
|
|
78
|
+
|--------|------|----------|
|
|
79
|
+
| `EQ` | 等于(=) | 精确匹配单个值 |
|
|
80
|
+
| `NEQ` | 不等于(!=) | 排除单个值 |
|
|
81
|
+
| `IN` | 包含在列表中 | 匹配多个值中的任意一个 |
|
|
82
|
+
| `NOT_IN` | 不包含在列表中 | 排除多个值 |
|
|
83
|
+
| `BETWEEN` | 范围(闭区间) | 时间范围、数值范围,values 为 `[min, max]` |
|
|
84
|
+
| `GT` | 大于(>) | 数值比较 |
|
|
85
|
+
| `GTE` | 大于等于(>=) | 数值比较 |
|
|
86
|
+
| `LT` | 小于(<) | 数值比较 |
|
|
87
|
+
| `LTE` | 小于等于(<=) | 数值比较 |
|
|
88
|
+
| `LIKE` | 模糊匹配 | 字符串匹配,支持 `%` 通配符 |
|
|
89
|
+
| `ILIKE` | 模糊匹配(不区分大小写) | 字符串匹配 |
|
|
90
|
+
| `IS_NULL` | 为空 | 检查空值,values 可省略 |
|
|
91
|
+
| `IS_NOT_NULL` | 不为空 | 检查非空值,values 可省略 |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 排序规则
|
|
96
|
+
|
|
97
|
+
### OrderBy 结构
|
|
98
|
+
|
|
99
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
100
|
+
|------|------|------|------|
|
|
101
|
+
| `field` | `string` | ✅ | 排序字段,一般为响应中的列名 |
|
|
102
|
+
| `direction` | `string` | ✅ | `"ASC"` 或 `"DESC"` |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 返回数据格式
|
|
107
|
+
|
|
108
|
+
### 响应体结构(通用)
|
|
109
|
+
|
|
110
|
+
**外壳**:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"code": 200,
|
|
115
|
+
"message": "success",
|
|
116
|
+
"data": { ... }
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `data` 结构(指标查询结果)
|
|
121
|
+
|
|
122
|
+
| 字段 | 类型 | 说明 |
|
|
123
|
+
|------|------|------|
|
|
124
|
+
| `semanticModel` | `string` | 命中的语义模型名称,与 meta 中一致 |
|
|
125
|
+
| `columns` | `array` | 列元信息,每项 `{ "name": "<列名>", "type": "<数据类型>" }`。列顺序与 `rows` 中每行元素一一对应 |
|
|
126
|
+
| `rows` | `array` | 行数组,每行为**与 columns 同序**的值数组(非对象) |
|
|
127
|
+
| `rowsObject` | `array` | 行数组(对象格式),每行是一个对象,键为列名,值为对应数据 |
|
|
128
|
+
| `debug` | `object` \| `null` | 仅当请求中 `debug: true` 时存在,可含 `sql`、`params` 等执行信息 |
|
|
129
|
+
|
|
130
|
+
**说明**:`columns[].name` 可能为维度粒度表达式(如 `date__month`)或指标名;具体列名与 meta 中维度和指标定义一致。
|
|
131
|
+
|
|
132
|
+
### 完整示例
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"code": 200,
|
|
137
|
+
"message": "success",
|
|
138
|
+
"data": {
|
|
139
|
+
"semanticModel": "sales_model",
|
|
140
|
+
"columns": [
|
|
141
|
+
{ "name": "date__month", "type": "date" },
|
|
142
|
+
{ "name": "sales_amt", "type": "numeric" }
|
|
143
|
+
],
|
|
144
|
+
"rows": [
|
|
145
|
+
["2025-01-01", 150000],
|
|
146
|
+
["2025-02-01", 180000]
|
|
147
|
+
],
|
|
148
|
+
"rowsObject": [
|
|
149
|
+
{ "date__month": "2025-01-01", "sales_amt": 150000 },
|
|
150
|
+
{ "date__month": "2025-02-01", "sales_amt": 180000 }
|
|
151
|
+
],
|
|
152
|
+
"debug": null
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 访问数据
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const result = await sdk.query({
|
|
161
|
+
metrics: ['sales_amt'],
|
|
162
|
+
groupBy: ['date__month']
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// 访问返回数据(数组格式)
|
|
166
|
+
console.log(result.data.columns); // [{ name: 'date__month', type: 'date' }, ...]
|
|
167
|
+
console.log(result.data.rows); // [['2025-01-01', 150000], ...]
|
|
168
|
+
console.log(result.data.rows[0][1]); // 150000 (第一行的 sales_amt)
|
|
169
|
+
|
|
170
|
+
// 访问返回数据(对象格式)
|
|
171
|
+
console.log(result.data.rowsObject); // [{ date__month: '2025-01-01', sales_amt: 150000 }, ...]
|
|
172
|
+
console.log(result.data.rowsObject[0].sales_amt); // 150000
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 查询示例
|
|
178
|
+
|
|
179
|
+
### 基础查询
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
const result = await sdk.query({
|
|
183
|
+
metrics: ['sales_amt'],
|
|
184
|
+
groupBy: ['date__month']
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// 访问返回数据(数组格式)
|
|
188
|
+
console.log(result.data.rows); // [['2025-01-01', 150000], ...]
|
|
189
|
+
|
|
190
|
+
// 访问返回数据(对象格式)
|
|
191
|
+
console.log(result.data.rowsObject); // [{ date__month: '2025-01-01', sales_amt: 150000 }, ...]
|
|
192
|
+
console.log(result.data.rows[0][1]); // 150000 (第一行的 sales_amt)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 带过滤条件
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
const result = await sdk.query({
|
|
199
|
+
metrics: ['sales_amt'],
|
|
200
|
+
groupBy: ['date__month'],
|
|
201
|
+
filters: [{
|
|
202
|
+
dimension: 'date',
|
|
203
|
+
operator: 'BETWEEN',
|
|
204
|
+
values: ['2025-01-01', '2025-12-31']
|
|
205
|
+
}]
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 多维度分组
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
const result = await sdk.query({
|
|
213
|
+
metrics: ['sales_amt', 'order_cnt'],
|
|
214
|
+
groupBy: ['region', 'date__month'],
|
|
215
|
+
filters: [{
|
|
216
|
+
dimension: 'region',
|
|
217
|
+
operator: 'IN',
|
|
218
|
+
values: ['north', 'south']
|
|
219
|
+
}]
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 带排序和限制
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
const result = await sdk.query({
|
|
227
|
+
metrics: ['sales_amt'],
|
|
228
|
+
groupBy: ['shop'],
|
|
229
|
+
orderBy: [{
|
|
230
|
+
field: 'sales_amt',
|
|
231
|
+
direction: 'DESC'
|
|
232
|
+
}],
|
|
233
|
+
limit: 10
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 最佳实践
|
|
240
|
+
|
|
241
|
+
### 日期过滤必须使用 BETWEEN
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
// ❌ 错误:不要用 EQ 查询年份
|
|
245
|
+
{
|
|
246
|
+
dimension: 'date__year',
|
|
247
|
+
operator: 'EQ',
|
|
248
|
+
values: ['2025']
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ✅ 正确:使用 BETWEEN 查询具体日期范围
|
|
252
|
+
{
|
|
253
|
+
dimension: 'date',
|
|
254
|
+
operator: 'BETWEEN',
|
|
255
|
+
values: ['2025-01-01', '2025-12-31']
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 字段映射一致性
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
// ✅ 正确:filters 使用原始维度字段
|
|
263
|
+
query: {
|
|
264
|
+
groupBy: ['date__month'],
|
|
265
|
+
filters: [{
|
|
266
|
+
dimension: 'date', // 原始字段
|
|
267
|
+
operator: 'BETWEEN',
|
|
268
|
+
values: ['2025-01-01', '2025-12-31']
|
|
269
|
+
}]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ❌ 错误:filters 中使用粒度表达式
|
|
273
|
+
query: {
|
|
274
|
+
filters: [{
|
|
275
|
+
dimension: 'date__year', // 错误!
|
|
276
|
+
operator: 'EQ',
|
|
277
|
+
values: ['2025']
|
|
278
|
+
}]
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 数据探查原则
|
|
283
|
+
|
|
284
|
+
1. **先查 meta**:了解可用的指标和维度
|
|
285
|
+
2. **验证指标**:对每个候选指标执行实际查询
|
|
286
|
+
3. **带过滤测试**:数据探查时必须带上过滤条件
|
|
287
|
+
4. **确认数据格式**:验证维度值的真实格式
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 常见错误
|
|
292
|
+
|
|
293
|
+
| 错误 | 原因 | 解决方案 |
|
|
294
|
+
|------|------|----------|
|
|
295
|
+
| 指标不存在 | 指标名拼写错误或大小写不匹配 | 检查 meta 中的指标定义 |
|
|
296
|
+
| 维度不存在 | 维度名错误或该维度不支持 | 查看 meta 中的维度列表 |
|
|
297
|
+
| 日期过滤无效 | 使用了 EQ 而不是 BETWEEN | 日期范围必须使用 BETWEEN |
|
|
298
|
+
| 粒度表达式错误 | 在 filters 中使用了粒度表达式 | filters 必须使用原始维度字段 |
|
|
299
|
+
| 返回空数据 | 过滤条件过于严格或无匹配数据 | 放宽过滤条件或检查数据范围 |
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 版本信息
|
|
304
|
+
|
|
305
|
+
- **版本**: 3.0.0
|
|
306
|
+
- **更新日期**: 2025-01-15
|
|
307
|
+
- **SDK 文件**: `http://localhost:4001/sdk/data-query-sdk.js`
|