befly 3.36.0 → 3.37.0
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/apis/dashboard/serviceStatus.js +1 -33
- package/apis/tongJi/_tongJi.js +48 -0
- package/apis/tongJi/errorReport.js +1 -21
- package/apis/tongJi/errorStats.js +65 -51
- package/apis/tongJi/infoReport.js +47 -148
- package/apis/tongJi/infoStats.js +151 -186
- package/apis/tongJi/onlineReport.js +24 -59
- package/apis/tongJi/onlineStats.js +34 -191
- package/configs/beflyConfig.json +2 -2
- package/package.json +1 -1
- package/sql/befly.sql +0 -32
- package/tables/errorReport.json +2 -1
- package/apis/tongJi/cacheHealth.js +0 -192
- package/apis/tongJi/fallbackReset.js +0 -61
- package/tables/infoReport.json +0 -123
|
@@ -1,177 +1,55 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getOnlineStatsActiveProductKey } from "./_tongJi.js";
|
|
2
2
|
|
|
3
|
-
import { getTongJiMonthStartDate, getTongJiNumber, getTongJiRecentDateList, getTongJiWeekStartDate } from "./_tongJi.js";
|
|
4
|
-
|
|
5
|
-
const ONLINE_STATS_DAY_LIMIT = 30;
|
|
6
3
|
const ONLINE_STATS_ACTIVE_KEY = "online:active";
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
function getPeriodKeys(periodType, periodValue) {
|
|
10
|
-
return {
|
|
11
|
-
pv: `online:${periodType}:${periodValue}:pv`,
|
|
12
|
-
members: `online:${periodType}:${periodValue}:members`,
|
|
13
|
-
reportTime: `online:${periodType}:${periodValue}:reportTime`
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// 构造单产品统计的 Redis 键。
|
|
18
|
-
function getOnlineStatsProductPeriodKeys(periodType, periodValue, productName) {
|
|
19
|
-
const productKey = encodeURIComponent(productName);
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
pv: `online:${periodType}:${periodValue}:product:${productKey}:pv`,
|
|
23
|
-
members: `online:${periodType}:${periodValue}:product:${productKey}:members`,
|
|
24
|
-
reportTime: `online:${periodType}:${periodValue}:product:${productKey}:reportTime`
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// 构造单产品实时在线集合的 Redis 键。
|
|
29
|
-
function getOnlineStatsActiveProductKey(productName) {
|
|
30
|
-
const productKey = encodeURIComponent(productName);
|
|
31
|
-
|
|
32
|
-
return `online:active:product:${productKey}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 读取 Redis 字符串并转成统计数字。
|
|
36
|
-
async function getRedisNumber(befly, key) {
|
|
37
|
-
return getTongJiNumber(await befly.redis.getString(key));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 读取某个周期的全量统计数据。
|
|
41
|
-
async function getOnlineStatsPeriodData(befly, periodType, periodValue) {
|
|
42
|
-
const keys = getPeriodKeys(periodType, periodValue);
|
|
43
|
-
return {
|
|
44
|
-
reportTime: await getRedisNumber(befly, keys.reportTime),
|
|
45
|
-
pv: await getRedisNumber(befly, keys.pv),
|
|
46
|
-
uv: getTongJiNumber(await befly.redis.scard(keys.members))
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 读取某个周期的单产品统计数据。
|
|
51
|
-
async function getOnlineStatsProductPeriodData(befly, periodType, periodValue, productName) {
|
|
52
|
-
const keys = getOnlineStatsProductPeriodKeys(periodType, periodValue, productName);
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
reportTime: await getRedisNumber(befly, keys.reportTime),
|
|
56
|
-
pv: await getRedisNumber(befly, keys.pv),
|
|
57
|
-
uv: getTongJiNumber(await befly.redis.scard(keys.members))
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// 清理过期成员后返回全站实时在线人数。
|
|
62
|
-
async function getOnlineStatsTotalOnlineCount(befly) {
|
|
63
|
-
const now = Date.now();
|
|
64
|
-
|
|
5
|
+
async function getOnlineStatsTotalOnlineCount(befly, now) {
|
|
65
6
|
await befly.redis.zremrangebyscore(ONLINE_STATS_ACTIVE_KEY, "-inf", now);
|
|
66
7
|
return await befly.redis.zcard(ONLINE_STATS_ACTIVE_KEY);
|
|
67
8
|
}
|
|
68
9
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const now = Date.now();
|
|
72
|
-
const productKey = getOnlineStatsActiveProductKey(productName);
|
|
10
|
+
async function getOnlineStatsProductOnlineCount(befly, productCode, now) {
|
|
11
|
+
const productKey = getOnlineStatsActiveProductKey(productCode);
|
|
73
12
|
|
|
74
13
|
await befly.redis.zremrangebyscore(productKey, "-inf", now);
|
|
75
14
|
return await befly.redis.zcard(productKey);
|
|
76
15
|
}
|
|
77
16
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const days = [];
|
|
81
|
-
|
|
82
|
-
for (const item of recentDateList) {
|
|
83
|
-
const dayData = await getOnlineStatsPeriodData(befly, "day", item);
|
|
84
|
-
|
|
85
|
-
if (dayData.reportTime <= 0) {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
17
|
+
async function buildOnlineStatsProducts(befly, projectLists, now) {
|
|
18
|
+
const products = [];
|
|
88
19
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
20
|
+
for (const item of projectLists) {
|
|
21
|
+
products.push({
|
|
22
|
+
key: item.productCode,
|
|
23
|
+
productName: item.productName,
|
|
24
|
+
productCode: item.productCode,
|
|
25
|
+
productVersion: item.productVersion || "",
|
|
26
|
+
onlineCount: await getOnlineStatsProductOnlineCount(befly, item.productCode, now)
|
|
94
27
|
});
|
|
95
28
|
}
|
|
96
29
|
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// 构造单产品统计的按天趋势列表。
|
|
101
|
-
async function buildOnlineStatsProductDays(befly, recentDateList, productName) {
|
|
102
|
-
const days = [];
|
|
103
|
-
|
|
104
|
-
for (const item of recentDateList) {
|
|
105
|
-
const dayData = await getOnlineStatsProductPeriodData(befly, "day", item, productName);
|
|
106
|
-
|
|
107
|
-
if (dayData.reportTime <= 0) {
|
|
108
|
-
continue;
|
|
30
|
+
return products.toSorted((a, b) => {
|
|
31
|
+
if (b.onlineCount !== a.onlineCount) {
|
|
32
|
+
return b.onlineCount - a.onlineCount;
|
|
109
33
|
}
|
|
110
34
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
reportTime: dayData.reportTime,
|
|
114
|
-
pv: dayData.pv,
|
|
115
|
-
uv: dayData.uv
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return days;
|
|
35
|
+
return String(a.productName).localeCompare(String(b.productName), "zh-CN");
|
|
36
|
+
});
|
|
120
37
|
}
|
|
121
38
|
|
|
122
|
-
|
|
123
|
-
async function buildOnlineStatsProducts(befly, recentDateList, reportDate, weekStartDate, monthStartDate, projectLists) {
|
|
39
|
+
function buildEmptyOnlineStatsProducts(projectLists) {
|
|
124
40
|
const products = [];
|
|
125
41
|
|
|
126
42
|
for (const item of projectLists) {
|
|
127
|
-
const productDays = await buildOnlineStatsProductDays(befly, recentDateList, item.productName);
|
|
128
|
-
let totalPv = 0;
|
|
129
|
-
|
|
130
|
-
for (const productDay of productDays) {
|
|
131
|
-
totalPv += productDay.pv;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const productToday = await getOnlineStatsProductPeriodData(befly, "day", reportDate, item.productName);
|
|
135
|
-
const productWeek = await getOnlineStatsProductPeriodData(befly, "week", weekStartDate, item.productName);
|
|
136
|
-
const productMonth = await getOnlineStatsProductPeriodData(befly, "month", monthStartDate, item.productName);
|
|
137
|
-
const productOnlineCount = await getOnlineStatsProductOnlineCount(befly, item.productName);
|
|
138
|
-
|
|
139
43
|
products.push({
|
|
140
44
|
key: item.productCode,
|
|
141
45
|
productName: item.productName,
|
|
142
46
|
productCode: item.productCode,
|
|
143
47
|
productVersion: item.productVersion || "",
|
|
144
|
-
onlineCount:
|
|
145
|
-
today: {
|
|
146
|
-
pv: productToday.pv,
|
|
147
|
-
uv: productToday.uv
|
|
148
|
-
},
|
|
149
|
-
week: {
|
|
150
|
-
pv: productWeek.pv,
|
|
151
|
-
uv: productWeek.uv
|
|
152
|
-
},
|
|
153
|
-
month: {
|
|
154
|
-
pv: productMonth.pv,
|
|
155
|
-
uv: productMonth.uv
|
|
156
|
-
},
|
|
157
|
-
days: productDays,
|
|
158
|
-
totalPv: totalPv
|
|
48
|
+
onlineCount: 0
|
|
159
49
|
});
|
|
160
50
|
}
|
|
161
51
|
|
|
162
|
-
|
|
163
|
-
if (b.totalPv !== a.totalPv) {
|
|
164
|
-
return b.totalPv - a.totalPv;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return String(a.productName).localeCompare(String(b.productName), "zh-CN");
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
for (const item of sortedProducts) {
|
|
171
|
-
delete item.totalPv;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return sortedProducts;
|
|
52
|
+
return products;
|
|
175
53
|
}
|
|
176
54
|
|
|
177
55
|
export default {
|
|
@@ -180,64 +58,29 @@ export default {
|
|
|
180
58
|
body: "none",
|
|
181
59
|
auth: true,
|
|
182
60
|
fields: {
|
|
183
|
-
|
|
61
|
+
productCode: { name: "产品代号", input: "string", min: 0, max: 100 }
|
|
184
62
|
},
|
|
185
63
|
required: [],
|
|
186
|
-
// 汇总当前查询范围的在线统计,并在未筛选产品时附带配置项目统计。
|
|
187
64
|
handler: async (befly, ctx) => {
|
|
188
65
|
const now = Date.now();
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
let monthCurrent;
|
|
200
|
-
let onlineCount;
|
|
201
|
-
|
|
202
|
-
if (hasProductFilter) {
|
|
203
|
-
currentDay = await getOnlineStatsProductPeriodData(befly, "day", reportDate, productName);
|
|
204
|
-
weekCurrent = await getOnlineStatsProductPeriodData(befly, "week", weekStartDate, productName);
|
|
205
|
-
monthCurrent = await getOnlineStatsProductPeriodData(befly, "month", monthStartDate, productName);
|
|
206
|
-
onlineCount = await getOnlineStatsProductOnlineCount(befly, productName);
|
|
207
|
-
} else {
|
|
208
|
-
currentDay = await getOnlineStatsPeriodData(befly, "day", reportDate);
|
|
209
|
-
weekCurrent = await getOnlineStatsPeriodData(befly, "week", weekStartDate);
|
|
210
|
-
monthCurrent = await getOnlineStatsPeriodData(befly, "month", monthStartDate);
|
|
211
|
-
onlineCount = await getOnlineStatsTotalOnlineCount(befly);
|
|
66
|
+
const productCode = ctx?.body?.productCode ?? "";
|
|
67
|
+
const hasProductFilter = productCode.length > 0;
|
|
68
|
+
const projectLists = hasProductFilter ? [] : befly.config?.projectLists || [];
|
|
69
|
+
|
|
70
|
+
if (!befly.redis) {
|
|
71
|
+
return befly.tool.Yes("获取成功", {
|
|
72
|
+
queryTime: now,
|
|
73
|
+
onlineCount: 0,
|
|
74
|
+
products: buildEmptyOnlineStatsProducts(projectLists)
|
|
75
|
+
});
|
|
212
76
|
}
|
|
213
77
|
|
|
214
|
-
const
|
|
215
|
-
const products = await buildOnlineStatsProducts(befly,
|
|
78
|
+
const onlineCount = hasProductFilter ? await getOnlineStatsProductOnlineCount(befly, productCode, now) : await getOnlineStatsTotalOnlineCount(befly, now);
|
|
79
|
+
const products = await buildOnlineStatsProducts(befly, projectLists, now);
|
|
216
80
|
|
|
217
81
|
return befly.tool.Yes("获取成功", {
|
|
218
82
|
queryTime: now,
|
|
219
83
|
onlineCount: onlineCount,
|
|
220
|
-
today: {
|
|
221
|
-
reportDate: reportDate,
|
|
222
|
-
reportTime: currentDay.reportTime,
|
|
223
|
-
pv: currentDay.pv,
|
|
224
|
-
uv: currentDay.uv
|
|
225
|
-
},
|
|
226
|
-
week: {
|
|
227
|
-
startDate: weekStartDate,
|
|
228
|
-
endDate: reportDate,
|
|
229
|
-
reportTime: weekCurrent.reportTime,
|
|
230
|
-
pv: weekCurrent.pv,
|
|
231
|
-
uv: weekCurrent.uv
|
|
232
|
-
},
|
|
233
|
-
month: {
|
|
234
|
-
startDate: monthStartDate,
|
|
235
|
-
endDate: reportDate,
|
|
236
|
-
reportTime: monthCurrent.reportTime,
|
|
237
|
-
pv: monthCurrent.pv,
|
|
238
|
-
uv: monthCurrent.uv
|
|
239
|
-
},
|
|
240
|
-
days: days,
|
|
241
84
|
products: products
|
|
242
85
|
});
|
|
243
86
|
}
|
package/configs/beflyConfig.json
CHANGED
package/package.json
CHANGED
package/sql/befly.sql
CHANGED
|
@@ -227,35 +227,3 @@ CREATE TABLE IF NOT EXISTS `befly_error_report` (
|
|
|
227
227
|
KEY `idx_befly_error_report_time` (`report_time`),
|
|
228
228
|
KEY `idx_befly_error_report_bucket_date` (`bucket_date`)
|
|
229
229
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
230
|
-
|
|
231
|
-
CREATE TABLE IF NOT EXISTS `befly_info_report` (
|
|
232
|
-
`id` BIGINT NOT NULL,
|
|
233
|
-
`report_time` BIGINT NOT NULL DEFAULT 0,
|
|
234
|
-
`report_date` INT NOT NULL DEFAULT 0,
|
|
235
|
-
`member_key` VARCHAR(100) NOT NULL DEFAULT '',
|
|
236
|
-
`source` VARCHAR(50) NOT NULL DEFAULT '',
|
|
237
|
-
`product_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
238
|
-
`product_code` VARCHAR(100) NOT NULL DEFAULT '',
|
|
239
|
-
`product_version` VARCHAR(100) NOT NULL DEFAULT '',
|
|
240
|
-
`page_path` VARCHAR(200) NOT NULL DEFAULT '',
|
|
241
|
-
`page_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
242
|
-
`detail` TEXT NULL,
|
|
243
|
-
`user_agent` VARCHAR(500) NOT NULL DEFAULT '',
|
|
244
|
-
`browser_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
245
|
-
`browser_version` VARCHAR(100) NOT NULL DEFAULT '',
|
|
246
|
-
`os_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
247
|
-
`os_version` VARCHAR(100) NOT NULL DEFAULT '',
|
|
248
|
-
`device_type` VARCHAR(50) NOT NULL DEFAULT '',
|
|
249
|
-
`device_vendor` VARCHAR(100) NOT NULL DEFAULT '',
|
|
250
|
-
`device_model` VARCHAR(100) NOT NULL DEFAULT '',
|
|
251
|
-
`engine_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
252
|
-
`cpu_architecture` VARCHAR(100) NOT NULL DEFAULT '',
|
|
253
|
-
`state` TINYINT NOT NULL DEFAULT 1,
|
|
254
|
-
`created_at` BIGINT NOT NULL DEFAULT 0,
|
|
255
|
-
`updated_at` BIGINT NOT NULL DEFAULT 0,
|
|
256
|
-
`deleted_at` BIGINT NULL DEFAULT NULL,
|
|
257
|
-
PRIMARY KEY (`id`),
|
|
258
|
-
UNIQUE KEY `uk_befly_info_report_date_member` (`report_date`, `member_key`),
|
|
259
|
-
KEY `idx_befly_info_report_time` (`report_time`),
|
|
260
|
-
KEY `idx_befly_info_report_date` (`report_date`)
|
|
261
|
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
package/tables/errorReport.json
CHANGED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { getDateYmdNumber } from "#befly/utils/datetime.js";
|
|
2
|
-
|
|
3
|
-
import { getTongJiMonthStartDate, getTongJiNumber, getTongJiWeekStartDate } from "./_tongJi.js";
|
|
4
|
-
|
|
5
|
-
const ONLINE_ACTIVE_KEY = "online:active";
|
|
6
|
-
const INFO_STATS_FALLBACK_COUNT_KEY = "stats:fallback:infoStats:count";
|
|
7
|
-
const ERROR_STATS_FALLBACK_COUNT_KEY = "stats:fallback:errorStats:count";
|
|
8
|
-
|
|
9
|
-
function getInfoStatsFallbackDailyCountKey(reportDate) {
|
|
10
|
-
return `stats:fallback:infoStats:count:day:${reportDate}`;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function getErrorStatsFallbackDailyCountKey(reportDate) {
|
|
14
|
-
return `stats:fallback:errorStats:count:day:${reportDate}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getOnlineReportTimeKey(periodType, periodValue) {
|
|
18
|
-
return `online:${periodType}:${periodValue}:reportTime`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function getInfoReportTimeKey(periodType, periodValue) {
|
|
22
|
-
return `info:${periodType}:${periodValue}:reportTime`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getErrorCountKey(periodType, periodValue) {
|
|
26
|
-
return `error:${periodType}:${periodValue}:count`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function getRedisStringNumber(redis, key) {
|
|
30
|
-
return getTongJiNumber(await redis.getString(key));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function getRedisSetCount(redis, key) {
|
|
34
|
-
if (typeof redis.scard !== "function") {
|
|
35
|
-
return 0;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return getTongJiNumber(await redis.scard(key));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function getRedisSortedSetCount(redis, key) {
|
|
42
|
-
if (typeof redis.zcard !== "function") {
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return getTongJiNumber(await redis.zcard(key));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function getRedisConnectivity(redis) {
|
|
50
|
-
if (!redis || typeof redis.ping !== "function") {
|
|
51
|
-
return {
|
|
52
|
-
status: "unavailable",
|
|
53
|
-
responseTime: -1
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
const start = Date.now();
|
|
59
|
-
await redis.ping();
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
status: "running",
|
|
63
|
-
responseTime: Date.now() - start
|
|
64
|
-
};
|
|
65
|
-
} catch {
|
|
66
|
-
return {
|
|
67
|
-
status: "stopped",
|
|
68
|
-
responseTime: -1
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export default {
|
|
74
|
-
name: "获取统计缓存健康状态",
|
|
75
|
-
method: "POST",
|
|
76
|
-
body: "none",
|
|
77
|
-
auth: true,
|
|
78
|
-
fields: {},
|
|
79
|
-
required: [],
|
|
80
|
-
handler: async (befly) => {
|
|
81
|
-
const now = Date.now();
|
|
82
|
-
const reportDate = getDateYmdNumber(now);
|
|
83
|
-
const weekStartDate = getTongJiWeekStartDate(now);
|
|
84
|
-
const monthStartDate = getTongJiMonthStartDate(now);
|
|
85
|
-
const redisConnectivity = await getRedisConnectivity(befly.redis);
|
|
86
|
-
|
|
87
|
-
if (!befly.redis) {
|
|
88
|
-
return befly.tool.Yes("获取成功", {
|
|
89
|
-
queryTime: now,
|
|
90
|
-
redis: redisConnectivity,
|
|
91
|
-
online: null,
|
|
92
|
-
info: null,
|
|
93
|
-
error: null,
|
|
94
|
-
roleCache: null,
|
|
95
|
-
hints: ["Redis 不可用,统计接口将回退到 MySQL 聚合"]
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const onlineTodayReportTime = await getRedisStringNumber(befly.redis, getOnlineReportTimeKey("day", reportDate));
|
|
100
|
-
const onlineWeekReportTime = await getRedisStringNumber(befly.redis, getOnlineReportTimeKey("week", weekStartDate));
|
|
101
|
-
const onlineMonthReportTime = await getRedisStringNumber(befly.redis, getOnlineReportTimeKey("month", monthStartDate));
|
|
102
|
-
|
|
103
|
-
const infoTodayReportTime = await getRedisStringNumber(befly.redis, getInfoReportTimeKey("day", reportDate));
|
|
104
|
-
const infoWeekReportTime = await getRedisStringNumber(befly.redis, getInfoReportTimeKey("week", weekStartDate));
|
|
105
|
-
const infoMonthReportTime = await getRedisStringNumber(befly.redis, getInfoReportTimeKey("month", monthStartDate));
|
|
106
|
-
|
|
107
|
-
const errorDayCount = await getRedisStringNumber(befly.redis, getErrorCountKey("day", reportDate));
|
|
108
|
-
const errorWeekCount = await getRedisStringNumber(befly.redis, getErrorCountKey("week", weekStartDate));
|
|
109
|
-
const errorMonthCount = await getRedisStringNumber(befly.redis, getErrorCountKey("month", monthStartDate));
|
|
110
|
-
|
|
111
|
-
const roleApisVersion = String((await befly.redis.getString("role:apis:activeVersion")) || "");
|
|
112
|
-
const roleMenusVersion = String((await befly.redis.getString("role:menus:activeVersion")) || "");
|
|
113
|
-
|
|
114
|
-
const online = {
|
|
115
|
-
activeMembers: await getRedisSortedSetCount(befly.redis, ONLINE_ACTIVE_KEY),
|
|
116
|
-
reportTime: {
|
|
117
|
-
today: onlineTodayReportTime,
|
|
118
|
-
week: onlineWeekReportTime,
|
|
119
|
-
month: onlineMonthReportTime
|
|
120
|
-
},
|
|
121
|
-
ready: onlineTodayReportTime > 0 || onlineWeekReportTime > 0 || onlineMonthReportTime > 0
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const info = {
|
|
125
|
-
reportTime: {
|
|
126
|
-
today: infoTodayReportTime,
|
|
127
|
-
week: infoWeekReportTime,
|
|
128
|
-
month: infoMonthReportTime
|
|
129
|
-
},
|
|
130
|
-
daySourcesCount: await getRedisSetCount(befly.redis, `info:day:${reportDate}:sources:names`),
|
|
131
|
-
ready: infoTodayReportTime > 0 || infoWeekReportTime > 0 || infoMonthReportTime > 0
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const error = {
|
|
135
|
-
count: {
|
|
136
|
-
today: errorDayCount,
|
|
137
|
-
week: errorWeekCount,
|
|
138
|
-
month: errorMonthCount
|
|
139
|
-
},
|
|
140
|
-
ready: errorDayCount > 0 || errorWeekCount > 0 || errorMonthCount > 0
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const roleCache = {
|
|
144
|
-
apis: {
|
|
145
|
-
activeVersion: roleApisVersion,
|
|
146
|
-
ready: roleApisVersion.length > 0
|
|
147
|
-
},
|
|
148
|
-
menus: {
|
|
149
|
-
activeVersion: roleMenusVersion,
|
|
150
|
-
ready: roleMenusVersion.length > 0
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const fallback = {
|
|
155
|
-
infoStats: await getRedisStringNumber(befly.redis, INFO_STATS_FALLBACK_COUNT_KEY),
|
|
156
|
-
errorStats: await getRedisStringNumber(befly.redis, ERROR_STATS_FALLBACK_COUNT_KEY),
|
|
157
|
-
today: {
|
|
158
|
-
infoStats: await getRedisStringNumber(befly.redis, getInfoStatsFallbackDailyCountKey(reportDate)),
|
|
159
|
-
errorStats: await getRedisStringNumber(befly.redis, getErrorStatsFallbackDailyCountKey(reportDate))
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const hints = [];
|
|
164
|
-
|
|
165
|
-
if (!online.ready) {
|
|
166
|
-
hints.push("online 统计预聚合尚未建立,onlineStats 将返回空或低命中结果");
|
|
167
|
-
}
|
|
168
|
-
if (!info.ready) {
|
|
169
|
-
hints.push("info 统计预聚合尚未建立,infoStats 将回退到 MySQL 聚合");
|
|
170
|
-
}
|
|
171
|
-
if (!error.ready) {
|
|
172
|
-
hints.push("error 统计预聚合尚未建立,errorStats 将回退到 MySQL 聚合");
|
|
173
|
-
}
|
|
174
|
-
if (!roleCache.apis.ready || !roleCache.menus.ready) {
|
|
175
|
-
hints.push("角色权限版本缓存未就绪,建议执行一次 cacheRefresh");
|
|
176
|
-
}
|
|
177
|
-
if (fallback.infoStats > 0 || fallback.errorStats > 0) {
|
|
178
|
-
hints.push("检测到统计查询回退到 MySQL,可关注预聚合写入是否稳定,必要时可执行 tongJi/fallbackReset 重置观测计数");
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return befly.tool.Yes("获取成功", {
|
|
182
|
-
queryTime: now,
|
|
183
|
-
redis: redisConnectivity,
|
|
184
|
-
online: online,
|
|
185
|
-
info: info,
|
|
186
|
-
error: error,
|
|
187
|
-
roleCache: roleCache,
|
|
188
|
-
fallback: fallback,
|
|
189
|
-
hints: hints
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { getDateYmdNumber } from "#befly/utils/datetime.js";
|
|
2
|
-
|
|
3
|
-
import { getTongJiNumber } from "./_tongJi.js";
|
|
4
|
-
|
|
5
|
-
const INFO_STATS_FALLBACK_COUNT_KEY = "stats:fallback:infoStats:count";
|
|
6
|
-
const ERROR_STATS_FALLBACK_COUNT_KEY = "stats:fallback:errorStats:count";
|
|
7
|
-
|
|
8
|
-
function getInfoStatsFallbackDailyCountKey(reportDate) {
|
|
9
|
-
return `stats:fallback:infoStats:count:day:${reportDate}`;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function getErrorStatsFallbackDailyCountKey(reportDate) {
|
|
13
|
-
return `stats:fallback:errorStats:count:day:${reportDate}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default {
|
|
17
|
-
name: "重置统计回退计数",
|
|
18
|
-
method: "POST",
|
|
19
|
-
body: "none",
|
|
20
|
-
auth: true,
|
|
21
|
-
fields: {},
|
|
22
|
-
required: [],
|
|
23
|
-
handler: async (befly) => {
|
|
24
|
-
if (!befly.redis) {
|
|
25
|
-
return befly.tool.No("Redis 不可用,无法重置", {
|
|
26
|
-
reset: false
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const reportDate = getDateYmdNumber(Date.now());
|
|
31
|
-
const infoDailyKey = getInfoStatsFallbackDailyCountKey(reportDate);
|
|
32
|
-
const errorDailyKey = getErrorStatsFallbackDailyCountKey(reportDate);
|
|
33
|
-
const resetKeys = [INFO_STATS_FALLBACK_COUNT_KEY, ERROR_STATS_FALLBACK_COUNT_KEY, infoDailyKey, errorDailyKey];
|
|
34
|
-
const before = {
|
|
35
|
-
infoStats: getTongJiNumber(await befly.redis.getString(INFO_STATS_FALLBACK_COUNT_KEY)),
|
|
36
|
-
errorStats: getTongJiNumber(await befly.redis.getString(ERROR_STATS_FALLBACK_COUNT_KEY)),
|
|
37
|
-
today: {
|
|
38
|
-
infoStats: getTongJiNumber(await befly.redis.getString(infoDailyKey)),
|
|
39
|
-
errorStats: getTongJiNumber(await befly.redis.getString(errorDailyKey))
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const removedCount = await befly.redis.delBatch(resetKeys);
|
|
44
|
-
|
|
45
|
-
return befly.tool.Yes("重置成功", {
|
|
46
|
-
reset: true,
|
|
47
|
-
reportDate: reportDate,
|
|
48
|
-
removedCount: getTongJiNumber(removedCount),
|
|
49
|
-
keys: resetKeys,
|
|
50
|
-
before: before,
|
|
51
|
-
after: {
|
|
52
|
-
infoStats: 0,
|
|
53
|
-
errorStats: 0,
|
|
54
|
-
today: {
|
|
55
|
-
infoStats: 0,
|
|
56
|
-
errorStats: 0
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
};
|