befly 3.24.18 → 3.24.20
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/_apis.js +20 -0
- package/apis/admin/delete.js +3 -1
- package/apis/admin/detail.js +1 -3
- package/apis/admin/select.js +9 -7
- package/apis/admin/update.js +2 -2
- package/apis/api/all.js +6 -11
- package/apis/api/select.js +18 -19
- package/apis/dashboard/environmentInfo.js +6 -1
- package/apis/dashboard/serviceStatus.js +78 -60
- package/apis/dict/_dict.js +24 -0
- package/apis/dict/all.js +6 -4
- package/apis/dict/detail.js +9 -5
- package/apis/dict/insert.js +4 -11
- package/apis/dict/items.js +5 -6
- package/apis/dict/select.js +13 -9
- package/apis/dict/update.js +9 -13
- package/apis/dictType/select.js +4 -4
- package/apis/email/config.js +9 -11
- package/apis/email/logList.js +14 -4
- package/apis/email/send.js +10 -3
- package/apis/email/verify.js +9 -7
- package/apis/loginLog/select.js +11 -4
- package/apis/menu/all.js +13 -19
- package/apis/menu/select.js +19 -14
- package/apis/operateLog/select.js +13 -4
- package/apis/role/_role.js +21 -0
- package/apis/role/all.js +1 -3
- package/apis/role/apiSave.js +8 -15
- package/apis/role/apis.js +4 -10
- package/apis/role/delete.js +28 -36
- package/apis/role/detail.js +4 -10
- package/apis/role/insert.js +12 -10
- package/apis/role/menuSave.js +9 -15
- package/apis/role/menus.js +4 -10
- package/apis/role/save.js +19 -23
- package/apis/role/select.js +12 -9
- package/apis/role/update.js +14 -15
- package/apis/source/imageList.js +3 -3
- package/apis/sysConfig/get.js +11 -23
- package/apis/sysConfig/insert.js +22 -27
- package/apis/sysConfig/select.js +11 -5
- package/apis/sysConfig/update.js +33 -38
- package/apis/tongJi/_tongJi.js +41 -0
- package/apis/tongJi/cacheHealth.js +192 -0
- package/apis/tongJi/errorList.js +26 -27
- package/apis/tongJi/errorReport.js +72 -18
- package/apis/tongJi/errorStats.js +154 -30
- package/apis/tongJi/fallbackReset.js +61 -0
- package/apis/tongJi/infoReport.js +112 -24
- package/apis/tongJi/infoStats.js +164 -84
- package/apis/tongJi/onlineReport.js +58 -73
- package/apis/tongJi/onlineStats.js +140 -151
- package/checks/config.js +44 -1
- package/configs/beflyConfig.json +10 -1
- package/hooks/permission.js +6 -2
- package/hooks/rateLimit.js +245 -0
- package/index.js +25 -0
- package/lib/cacheHelper.js +105 -60
- package/lib/dbHelper.js +1 -1
- package/lib/dbParse.js +61 -99
- package/lib/dbUtil.js +101 -21
- package/lib/redisHelper.js +93 -0
- package/lib/sqlBuilder.js +6 -0
- package/package.json +2 -2
- package/plugins/email.js +4 -3
- package/utils/email.js +3 -0
- package/apis/admin/cacheRefresh.js +0 -122
- package/apis/dashboard/configStatus.js +0 -39
- package/apis/dashboard/performanceMetrics.js +0 -20
- package/apis/dashboard/permissionStats.js +0 -27
- package/apis/dashboard/systemInfo.js +0 -19
package/apis/role/select.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
|
+
import { queryFields } from "#root/apis/_apis.js";
|
|
2
|
+
import roleTable from "#root/tables/role.json";
|
|
3
|
+
|
|
1
4
|
export default {
|
|
2
5
|
name: "获取角色列表",
|
|
3
6
|
method: "POST",
|
|
4
7
|
body: "none",
|
|
5
8
|
auth: true,
|
|
6
9
|
fields: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
keyword: { name: "关键词", input: "string", min: 0, max: 50 },
|
|
10
|
-
state: { name: "状态", input: "integer", min: 0, max: 2 }
|
|
10
|
+
...queryFields,
|
|
11
|
+
state: roleTable.state
|
|
11
12
|
},
|
|
12
13
|
required: [],
|
|
13
|
-
handler: async (befly) => {
|
|
14
|
+
handler: async (befly, ctx) => {
|
|
14
15
|
const roles = await befly.mysql.getList({
|
|
15
|
-
limit: 30,
|
|
16
16
|
table: "beflyRole",
|
|
17
|
+
page: ctx.body.page,
|
|
18
|
+
limit: ctx.body.limit,
|
|
17
19
|
where: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
name$like$or: ctx.body.keyword,
|
|
21
|
+
code$like$or: ctx.body.keyword,
|
|
22
|
+
state: ctx.body.state,
|
|
23
|
+
code$not: "dev"
|
|
21
24
|
},
|
|
22
25
|
orderBy: ["sort#ASC", "id#ASC"]
|
|
23
26
|
});
|
package/apis/role/update.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import roleTable from "#root/tables/role.json";
|
|
2
|
+
|
|
3
|
+
import { getRoleById } from "./_role.js";
|
|
2
4
|
|
|
3
5
|
export default {
|
|
4
6
|
name: "更新角色",
|
|
@@ -6,20 +8,17 @@ export default {
|
|
|
6
8
|
body: "none",
|
|
7
9
|
auth: true,
|
|
8
10
|
fields: {
|
|
9
|
-
id:
|
|
10
|
-
name:
|
|
11
|
-
code:
|
|
12
|
-
description:
|
|
13
|
-
menus:
|
|
14
|
-
apis:
|
|
15
|
-
sort:
|
|
11
|
+
id: roleTable.id,
|
|
12
|
+
name: roleTable.name,
|
|
13
|
+
code: roleTable.code,
|
|
14
|
+
description: roleTable.description,
|
|
15
|
+
menus: roleTable.menus,
|
|
16
|
+
apis: roleTable.apis,
|
|
17
|
+
sort: roleTable.sort
|
|
16
18
|
},
|
|
17
19
|
required: ["id"],
|
|
18
20
|
handler: async (befly, ctx) => {
|
|
19
|
-
const role = await befly.
|
|
20
|
-
table: "beflyRole",
|
|
21
|
-
where: { id: ctx.body.id }
|
|
22
|
-
});
|
|
21
|
+
const role = await getRoleById(befly, ctx.body.id);
|
|
23
22
|
|
|
24
23
|
if (!role.data?.id) {
|
|
25
24
|
return befly.tool.No("角色不存在");
|
|
@@ -39,9 +38,9 @@ export default {
|
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
const roleCode = ctx.body.code
|
|
43
|
-
const apiPaths = ctx.body.apis
|
|
44
|
-
const menuPaths = ctx.body.menus
|
|
41
|
+
const roleCode = ctx.body.code === undefined ? role.data.code : ctx.body.code;
|
|
42
|
+
const apiPaths = ctx.body.apis === undefined ? role.data.apis || [] : ctx.body.apis;
|
|
43
|
+
const menuPaths = ctx.body.menus === undefined ? role.data.menus || [] : ctx.body.menus;
|
|
45
44
|
|
|
46
45
|
await befly.mysql.updData({
|
|
47
46
|
table: "beflyRole",
|
package/apis/source/imageList.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { queryFields } from "#root/apis/_apis.js";
|
|
2
|
+
|
|
1
3
|
export default {
|
|
2
4
|
name: "获取图库列表",
|
|
3
5
|
method: "POST",
|
|
4
6
|
body: "none",
|
|
5
7
|
auth: true,
|
|
6
8
|
fields: {
|
|
7
|
-
|
|
8
|
-
limit: { name: "每页数量", input: "integer", min: 1, max: 100 },
|
|
9
|
-
keyword: { name: "关键词", input: "string", min: 0, max: 100 }
|
|
9
|
+
...queryFields
|
|
10
10
|
},
|
|
11
11
|
required: [],
|
|
12
12
|
handler: async (befly, ctx) => {
|
package/apis/sysConfig/get.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import sysConfigTable from "#root/tables/sysConfig.json";
|
|
2
|
-
import { isString } from "#root/utils/is.js";
|
|
3
2
|
|
|
4
3
|
export default {
|
|
5
4
|
name: "根据代码获取配置值",
|
|
@@ -9,41 +8,30 @@ export default {
|
|
|
9
8
|
fields: {
|
|
10
9
|
code: sysConfigTable.code
|
|
11
10
|
},
|
|
12
|
-
required: [],
|
|
11
|
+
required: ["code"],
|
|
13
12
|
handler: async (befly, ctx) => {
|
|
14
13
|
const config = await befly.mysql.getOne({
|
|
15
14
|
table: "beflySysConfig",
|
|
16
15
|
where: { code: ctx.body.code }
|
|
17
16
|
});
|
|
18
17
|
|
|
19
|
-
if (!config.data
|
|
18
|
+
if (!config.data?.id) {
|
|
20
19
|
return befly.tool.No("配置不存在");
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const code = config.data.code;
|
|
30
|
-
if (!isString(code)) {
|
|
31
|
-
return befly.tool.No("配置数据不完整");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let value = rawValue;
|
|
35
|
-
if (valueType === "number") {
|
|
36
|
-
value = Number(rawValue);
|
|
37
|
-
} else if (valueType === "boolean") {
|
|
38
|
-
value = rawValue === "true" || rawValue === "1";
|
|
39
|
-
} else if (valueType === "json") {
|
|
22
|
+
let value = config.data.value;
|
|
23
|
+
if (config.data.valueType === "number") {
|
|
24
|
+
value = Number(config.data.value);
|
|
25
|
+
} else if (config.data.valueType === "boolean") {
|
|
26
|
+
value = config.data.value === "true" || config.data.value === "1";
|
|
27
|
+
} else if (config.data.valueType === "json") {
|
|
40
28
|
try {
|
|
41
|
-
value = JSON.parse(
|
|
29
|
+
value = JSON.parse(config.data.value);
|
|
42
30
|
} catch {
|
|
43
|
-
value =
|
|
31
|
+
value = config.data.value;
|
|
44
32
|
}
|
|
45
33
|
}
|
|
46
34
|
|
|
47
|
-
return befly.tool.Yes("操作成功", { code: code, value: value });
|
|
35
|
+
return befly.tool.Yes("操作成功", { code: config.data.code, value: value });
|
|
48
36
|
}
|
|
49
37
|
};
|
package/apis/sysConfig/insert.js
CHANGED
|
@@ -15,36 +15,31 @@ export default {
|
|
|
15
15
|
isSystem: sysConfigTable.isSystem,
|
|
16
16
|
description: sysConfigTable.description
|
|
17
17
|
},
|
|
18
|
-
required: [],
|
|
18
|
+
required: ["name", "code", "value"],
|
|
19
19
|
handler: async (befly, ctx) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
});
|
|
20
|
+
const existing = await befly.mysql.getOne({
|
|
21
|
+
table: "beflySysConfig",
|
|
22
|
+
where: { code: ctx.body.code }
|
|
23
|
+
});
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
if (existing.data?.id) {
|
|
26
|
+
return befly.tool.No("配置代码已存在");
|
|
27
|
+
}
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
const configId = await befly.mysql.insData({
|
|
30
|
+
table: "beflySysConfig",
|
|
31
|
+
data: {
|
|
32
|
+
name: ctx.body.name,
|
|
33
|
+
code: ctx.body.code,
|
|
34
|
+
value: ctx.body.value,
|
|
35
|
+
valueType: ctx.body.valueType === undefined ? "string" : ctx.body.valueType,
|
|
36
|
+
group: ctx.body.group === undefined ? "" : ctx.body.group,
|
|
37
|
+
sort: ctx.body.sort === undefined ? 0 : ctx.body.sort,
|
|
38
|
+
isSystem: ctx.body.isSystem === undefined ? 0 : ctx.body.isSystem,
|
|
39
|
+
description: ctx.body.description === undefined ? "" : ctx.body.description
|
|
40
|
+
}
|
|
41
|
+
});
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
} catch (error) {
|
|
46
|
-
befly.logger.error("添加系统配置失败", error);
|
|
47
|
-
return befly.tool.No("操作失败");
|
|
48
|
-
}
|
|
43
|
+
return befly.tool.Yes("操作成功", { id: configId.data });
|
|
49
44
|
}
|
|
50
45
|
};
|
package/apis/sysConfig/select.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
|
+
import { queryFields } from "#root/apis/_apis.js";
|
|
2
|
+
import sysConfigTable from "#root/tables/sysConfig.json";
|
|
3
|
+
|
|
1
4
|
export default {
|
|
2
5
|
name: "获取系统配置列表",
|
|
3
6
|
method: "POST",
|
|
4
7
|
body: "none",
|
|
5
8
|
auth: true,
|
|
6
9
|
fields: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
keyword: { name: "关键词", input: "string", min: 0, max: 50 },
|
|
10
|
-
state: { name: "状态", input: "integer", min: 0, max: 2 }
|
|
10
|
+
...queryFields,
|
|
11
|
+
state: sysConfigTable.state
|
|
11
12
|
},
|
|
12
13
|
required: [],
|
|
13
14
|
handler: async (befly, ctx) => {
|
|
14
15
|
const result = await befly.mysql.getList({
|
|
15
16
|
table: "beflySysConfig",
|
|
16
|
-
|
|
17
|
+
where: {
|
|
18
|
+
name$like$or: ctx.body.keyword,
|
|
19
|
+
code$like$or: ctx.body.keyword,
|
|
20
|
+
group$like$or: ctx.body.keyword,
|
|
21
|
+
state: ctx.body.state
|
|
22
|
+
},
|
|
17
23
|
page: ctx.body.page,
|
|
18
24
|
limit: ctx.body.limit,
|
|
19
25
|
orderBy: ["group#ASC", "sort#ASC", "id#ASC"]
|
package/apis/sysConfig/update.js
CHANGED
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
body: "none",
|
|
7
7
|
auth: true,
|
|
8
8
|
fields: {
|
|
9
|
-
id:
|
|
9
|
+
id: sysConfigTable.id,
|
|
10
10
|
name: sysConfigTable.name,
|
|
11
11
|
code: sysConfigTable.code,
|
|
12
12
|
value: sysConfigTable.value,
|
|
@@ -14,49 +14,44 @@ export default {
|
|
|
14
14
|
group: sysConfigTable.group,
|
|
15
15
|
sort: sysConfigTable.sort,
|
|
16
16
|
description: sysConfigTable.description,
|
|
17
|
-
state:
|
|
17
|
+
state: sysConfigTable.state
|
|
18
18
|
},
|
|
19
19
|
required: ["id"],
|
|
20
20
|
handler: async (befly, ctx) => {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const config = await befly.mysql.getOne({
|
|
22
|
+
table: "beflySysConfig",
|
|
23
|
+
where: { id: ctx.body.id }
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!config.data?.id) {
|
|
27
|
+
return befly.tool.No("配置不存在");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (config.data.isSystem === 1) {
|
|
31
|
+
await befly.mysql.updData({
|
|
23
32
|
table: "beflySysConfig",
|
|
33
|
+
data: {
|
|
34
|
+
value: ctx.body.value
|
|
35
|
+
},
|
|
36
|
+
where: { id: ctx.body.id }
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
await befly.mysql.updData({
|
|
40
|
+
table: "beflySysConfig",
|
|
41
|
+
data: {
|
|
42
|
+
name: ctx.body.name,
|
|
43
|
+
code: ctx.body.code,
|
|
44
|
+
value: ctx.body.value,
|
|
45
|
+
valueType: ctx.body.valueType,
|
|
46
|
+
group: ctx.body.group,
|
|
47
|
+
sort: ctx.body.sort,
|
|
48
|
+
description: ctx.body.description,
|
|
49
|
+
state: ctx.body.state
|
|
50
|
+
},
|
|
24
51
|
where: { id: ctx.body.id }
|
|
25
52
|
});
|
|
26
|
-
|
|
27
|
-
if (!config.data?.id) {
|
|
28
|
-
return befly.tool.No("配置不存在");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (config.data.isSystem === 1) {
|
|
32
|
-
await befly.mysql.updData({
|
|
33
|
-
table: "beflySysConfig",
|
|
34
|
-
data: {
|
|
35
|
-
value: ctx.body.value
|
|
36
|
-
},
|
|
37
|
-
where: { id: ctx.body.id }
|
|
38
|
-
});
|
|
39
|
-
} else {
|
|
40
|
-
await befly.mysql.updData({
|
|
41
|
-
table: "beflySysConfig",
|
|
42
|
-
data: {
|
|
43
|
-
name: ctx.body.name,
|
|
44
|
-
code: ctx.body.code,
|
|
45
|
-
value: ctx.body.value,
|
|
46
|
-
valueType: ctx.body.valueType,
|
|
47
|
-
group: ctx.body.group,
|
|
48
|
-
sort: ctx.body.sort,
|
|
49
|
-
description: ctx.body.description,
|
|
50
|
-
state: ctx.body.state
|
|
51
|
-
},
|
|
52
|
-
where: { id: ctx.body.id }
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return befly.tool.Yes("操作成功");
|
|
57
|
-
} catch (error) {
|
|
58
|
-
befly.logger.error("更新系统配置失败", error);
|
|
59
|
-
return befly.tool.No("操作失败");
|
|
60
53
|
}
|
|
54
|
+
|
|
55
|
+
return befly.tool.Yes("操作成功");
|
|
61
56
|
}
|
|
62
57
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { addDays, getDateYmdNumber } from "#root/utils/datetime.js";
|
|
2
|
+
|
|
3
|
+
export function getTongJiNumber(value) {
|
|
4
|
+
const num = Number(value);
|
|
5
|
+
|
|
6
|
+
if (!Number.isFinite(num)) {
|
|
7
|
+
return 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return num;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getTongJiWeekStartDate(timestamp = Date.now()) {
|
|
14
|
+
const date = Reflect.construct(Date, [timestamp]);
|
|
15
|
+
const day = date.getDay();
|
|
16
|
+
const offset = day === 0 ? -6 : 1 - day;
|
|
17
|
+
|
|
18
|
+
return getDateYmdNumber(addDays(timestamp, offset));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getTongJiMonthStartDate(timestamp = Date.now()) {
|
|
22
|
+
const date = Reflect.construct(Date, [timestamp]);
|
|
23
|
+
|
|
24
|
+
return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getTongJiRecentDateList(now = Date.now(), limit = 30) {
|
|
28
|
+
const list = [];
|
|
29
|
+
|
|
30
|
+
for (let i = limit - 1; i >= 0; i -= 1) {
|
|
31
|
+
list.push(getDateYmdNumber(addDays(now, -i)));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return list;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function expireTongJiRedisKeys(befly, keys, ttl) {
|
|
38
|
+
for (const key of keys) {
|
|
39
|
+
await befly.redis.expire(key, ttl);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { getDateYmdNumber } from "#root/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
|
+
};
|
package/apis/tongJi/errorList.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
+
import { queryFields } from "#root/apis/_apis.js";
|
|
2
|
+
import errorReportTable from "#root/tables/errorReport.json";
|
|
3
|
+
|
|
1
4
|
export default {
|
|
2
5
|
name: "获取错误报告列表",
|
|
3
6
|
method: "POST",
|
|
4
7
|
body: "none",
|
|
5
8
|
auth: true,
|
|
6
9
|
fields: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
browserName: { name: "浏览器", input: "string", min: 0, max: 100 },
|
|
17
|
-
osName: { name: "操作系统", input: "string", min: 0, max: 100 }
|
|
10
|
+
...queryFields,
|
|
11
|
+
errorType: errorReportTable.errorType,
|
|
12
|
+
source: errorReportTable.source,
|
|
13
|
+
productName: errorReportTable.productName,
|
|
14
|
+
productCode: errorReportTable.productCode,
|
|
15
|
+
productVersion: errorReportTable.productVersion,
|
|
16
|
+
deviceType: errorReportTable.deviceType,
|
|
17
|
+
browserName: errorReportTable.browserName,
|
|
18
|
+
osName: errorReportTable.osName
|
|
18
19
|
},
|
|
19
20
|
required: [],
|
|
20
21
|
handler: async (befly, ctx) => {
|
|
21
22
|
const tableExistsResult = await befly.mysql.tableExists("beflyErrorReport");
|
|
22
|
-
const page =
|
|
23
|
-
const limit =
|
|
23
|
+
const page = ctx.body.page === undefined ? 1 : ctx.body.page;
|
|
24
|
+
const limit = ctx.body.limit === undefined ? 20 : ctx.body.limit;
|
|
24
25
|
|
|
25
26
|
if (tableExistsResult.data !== true) {
|
|
26
27
|
return befly.tool.Yes("获取成功", {
|
|
@@ -43,20 +44,18 @@ export default {
|
|
|
43
44
|
const where = {};
|
|
44
45
|
|
|
45
46
|
if (keyword) {
|
|
46
|
-
where
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{ deviceModel$like: keyword }
|
|
59
|
-
];
|
|
47
|
+
where.pagePath$like$or = keyword;
|
|
48
|
+
where.pageName$like$or = keyword;
|
|
49
|
+
where.message$like$or = keyword;
|
|
50
|
+
where.errorType$like$or = keyword;
|
|
51
|
+
where.productName$like$or = keyword;
|
|
52
|
+
where.productCode$like$or = keyword;
|
|
53
|
+
where.productVersion$like$or = keyword;
|
|
54
|
+
where.browserName$like$or = keyword;
|
|
55
|
+
where.osName$like$or = keyword;
|
|
56
|
+
where.deviceType$like$or = keyword;
|
|
57
|
+
where.deviceVendor$like$or = keyword;
|
|
58
|
+
where.deviceModel$like$or = keyword;
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
if (errorType) {
|