Skip to content

Commit 5b11d83

Browse files
committed
feat: 支持指定桶的间隔分钟
1 parent dd63f89 commit 5b11d83

File tree

5 files changed

+91
-57
lines changed

5 files changed

+91
-57
lines changed

frontend/tick-stats-fe/src/assets/utils.js

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useGlobalStore } from '@/stores/global';
22
import moment from 'moment';
3+
import { da } from 'vuetify/locale';
34

45
export const fetchWrapper = async (url, options = {}) => {
56
if (!/^http/.test(url)) {
@@ -91,38 +92,34 @@ export const generatePieDemoData = () => {
9192
}
9293
})
9394
}
94-
95-
export const fillingTimeData = (items, count = 1496, interval_mins = 30) => {
95+
export const fillingTimeData = (items, interval_mins = 30) => {
96+
// 将 items 转换为 [timestamp_ms, value] 的数组
9697
let arr = items.map((item) => {
97-
return [item.k, item.v];
98-
})
98+
return [item.k, item.v]; // [timestamp_ms, value]
99+
});
99100

100-
let data = [];
101-
let nearest = new Date();
102-
nearest.setSeconds(0);
103-
nearest.setMilliseconds(0);
104-
let minutes = nearest.getMinutes();
105-
if (minutes < 30) {
106-
nearest.setMinutes(0);
107-
} else {
108-
nearest.setMinutes(30);
101+
if (arr.length === 0) {
102+
return arr;
109103
}
110-
nearest = nearest.getTime();
104+
arr.sort((a, b) => a[0] - b[0]);
111105

112-
let interval = interval_mins * 60 * 1000;
113-
for (let i = 0; i < count; i++) {
114-
let time = nearest - i * interval
115-
let found = arr.find((item) => {
116-
return item[0] === time;
117-
});
118-
if (found) {
119-
data.push(found);
106+
let data = [];
107+
let currentTime = arr[0][0];
108+
const intervalMs = interval_mins * 60 * 1000;
109+
const lastTime = arr[arr.length - 1][0];
110+
111+
// 遍历所有时间点,填充缺失的数据
112+
while (currentTime <= lastTime) {
113+
if (arr.length > 0 && currentTime === arr[0][0]) {
114+
data.push(arr.shift());
120115
} else {
121-
data.push([time, 0]);
116+
data.push([currentTime, 0]);
122117
}
118+
currentTime += intervalMs;
123119
}
120+
124121
return data;
125-
}
122+
};
126123

127124
export const chartsPresetConfigs = [
128125
{
@@ -157,6 +154,12 @@ export const chartsPresetConfigs = [
157154
default: false,
158155
description: "Only display the number of the latest data point."
159156
},
157+
{
158+
name: "bucket_mins",
159+
type: "number",
160+
default: 30,
161+
description: "The time interval in minutes for each data point. Default is 30 mins."
162+
}
160163
],
161164
multiple_keys: false
162165
},

frontend/tick-stats-fe/src/components/charts/BasicChart.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export default {
5555
await fetchWrapper(`/api/metric/${appid}/${chartid}`, {
5656
method: 'GET',
5757
}).then((data) => {
58+
let bucket_mins = chartData.extra_config.bucket_mins;
59+
if (!bucket_mins) {
60+
bucket_mins = 30;
61+
}
62+
bucket_mins = parseInt(bucket_mins);
63+
5864
let _display_type = 'default';
5965
if (chartData.extra_config.only_represent_number) {
6066
_display_type = 'plain_number';
@@ -66,7 +72,7 @@ export default {
6672
series: [{
6773
showSymbol: false,
6874
type: 'line',
69-
data: fillingTimeData(data)
75+
data: fillingTimeData(data, bucket_mins)
7076
}],
7177
_display_type: _display_type,
7278
}

frontend/tick-stats-fe/src/views/Stats.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@
142142
:label="ops.name" variant="outlined">
143143
</v-select>
144144
</div>
145+
<div v-else-if="ops.type === 'number'">
146+
<small>{{ ops.description }}</small>
147+
<v-text-field v-model="newChart.data.extra_config[ops.name]" :label="ops.name"
148+
variant="outlined" type="number">
149+
</v-text-field>
150+
</div>
145151
</div>
146152
<small>Public charts can be viewed by anyone</small>
147153
<v-checkbox v-model="newChart.data.public" label="Public" color="primary"></v-checkbox>

repositories/metrics.go

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,22 @@ func (r *metricsRepository) GetSimpleLine(
5959
// method: sum, count, accumulate
6060
// distinct_ip: true, false
6161

62+
// 检查 bucket_mins 是否存在
63+
_, ok := extraConfig["bucket_mins"]
64+
var bucket_mins int
65+
if !ok {
66+
bucket_mins = 30
67+
} else {
68+
bucket_mins, err = strconv.Atoi(extraConfig["bucket_mins"].(string))
69+
if err != nil || bucket_mins <= 30 {
70+
bucket_mins = 30
71+
}
72+
}
73+
74+
data_points := (to - from) / 60 / int64(bucket_mins)
75+
76+
fmt.Println("data_points: ", data_points)
77+
6278
switch extraConfig["method"] {
6379
case "count":
6480
var _v_select string
@@ -68,44 +84,47 @@ func (r *metricsRepository) GetSimpleLine(
6884
_v_select = "COUNT(ip) as v"
6985
}
7086
query := fmt.Sprintf(`
71-
SELECT time_bucket('30 minutes', time) as k, %s FROM basic_metric_data
87+
SELECT time_bucket('%d minutes', time) as k, %s FROM basic_metric_data
7288
WHERE app_id = ? AND jsonb_typeof(value->?) = 'number'
7389
GROUP BY k
74-
ORDER BY k
75-
LIMIT 1440;
76-
`, _v_select)
90+
ORDER BY k DESC
91+
LIMIT %d;
92+
`, bucket_mins, _v_select, data_points)
7793
err = r.db.Raw(query, appId, keyName).Scan(&metrics_).Error
7894
case "accumulate":
79-
query := `
80-
WITH RECURSIVE time_series AS (
81-
SELECT generate_series(
82-
date_trunc('hour', NOW()) - INTERVAL '30 day',
83-
date_trunc('hour', NOW() + INTERVAL '30 minutes'),
84-
'30 minutes'
85-
) AS time
86-
),
87-
metric_data AS (
88-
SELECT ts.time as k, COALESCE(SUM((value->>?)::int), 0) as v
89-
FROM time_series ts
90-
LEFT JOIN basic_metric_data bmd
91-
ON bmd.time >= ts.time
92-
AND bmd.time < ts.time + INTERVAL '30 minutes'
93-
AND bmd.app_id = ?
94-
GROUP BY ts.time
95-
ORDER BY ts.time
96-
)
97-
SELECT k, SUM(v) OVER (ORDER BY k) as v
98-
FROM metric_data;
99-
`
95+
query := fmt.Sprintf(`
96+
WITH RECURSIVE time_series AS (
97+
SELECT generate_series(
98+
date_trunc('hour', NOW()) - INTERVAL '30 days',
99+
date_trunc('hour', NOW() + INTERVAL '%d minutes'),
100+
'%d minutes'
101+
) AS time
102+
),
103+
metric_data AS (
104+
SELECT ts.time as k, COALESCE(SUM((value->>$1)::int), 0) as v
105+
FROM time_series ts
106+
LEFT JOIN basic_metric_data bmd
107+
ON bmd.time >= ts.time
108+
AND bmd.time < ts.time + INTERVAL '%d minutes'
109+
AND bmd.app_id = $2
110+
GROUP BY ts.time
111+
ORDER BY ts.time
112+
)
113+
SELECT k, SUM(v) OVER (ORDER BY k) as v
114+
FROM metric_data;
115+
ORDER BY k DESC
116+
LIMIT %d;
117+
`, bucket_mins, bucket_mins, bucket_mins, data_points)
100118
err = r.db.Raw(query, keyName, appId).Scan(&metrics_).Error
101119
default:
102120
// sum
103-
query := `
104-
SELECT time_bucket('30 minutes', time) as k,
121+
query := fmt.Sprintf(`
122+
SELECT time_bucket('%d minutes', time, NOW()) as k,
105123
SUM((value->>?)::numeric) as v FROM basic_metric_data
106124
WHERE app_id = ? AND jsonb_typeof(value->?) = 'number'
107125
GROUP BY k
108-
ORDER BY k LIMIT 1440;`
126+
ORDER BY k DESC
127+
LIMIT %d;`, bucket_mins, data_points)
109128
err = r.db.Raw(query, keyName, appId, keyName).Scan(&metrics_).Error
110129
}
111130

services/metrics.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ func (s *metricsService) GetByAppID(c *gin.Context, appId string, chartId string
6969
if from == 0 && to == 0 {
7070
// 1 days interval
7171
to = utils.GetCurrentTimestamp()
72-
from = to - 60*60*24
72+
from = to - 60*60*24*30
7373
} else {
7474
// cannot query more than 7 days
75-
if from < utils.GetCurrentTimestamp()-60*60*24*7 {
75+
if from < utils.GetCurrentTimestamp()-60*60*24*30 {
7676
return nil, utils.ErrInvalidTimeRange
7777
}
78-
if to-from > 60*60*24*7 {
78+
if to-from > 60*60*24*30 {
7979
return nil, utils.ErrTimeRangeTooLong
8080
}
8181
}
@@ -87,7 +87,7 @@ func (s *metricsService) GetByAppID(c *gin.Context, appId string, chartId string
8787
metrics, err = s.metricsRepository.GetSimplePie(appId, chart.KeyName, chart.ExtraConfig, from, to)
8888
case models.Table:
8989
metrics, err = s.metricsRepository.GetTable(appId, chart.KeyName, chart.ExtraConfig, from, to)
90-
90+
9191
default:
9292
return nil, utils.ErrInvalidChartType
9393
}

0 commit comments

Comments
 (0)