befly 3.24.19 → 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.
Files changed (68) hide show
  1. package/apis/_apis.js +20 -0
  2. package/apis/admin/delete.js +3 -1
  3. package/apis/admin/detail.js +1 -3
  4. package/apis/admin/select.js +9 -7
  5. package/apis/admin/update.js +2 -2
  6. package/apis/api/all.js +6 -11
  7. package/apis/api/select.js +18 -19
  8. package/apis/dict/_dict.js +24 -0
  9. package/apis/dict/all.js +6 -4
  10. package/apis/dict/detail.js +9 -5
  11. package/apis/dict/insert.js +4 -11
  12. package/apis/dict/items.js +5 -6
  13. package/apis/dict/select.js +13 -9
  14. package/apis/dict/update.js +9 -13
  15. package/apis/dictType/select.js +4 -4
  16. package/apis/email/config.js +9 -11
  17. package/apis/email/logList.js +14 -4
  18. package/apis/email/send.js +10 -3
  19. package/apis/email/verify.js +9 -7
  20. package/apis/loginLog/select.js +11 -4
  21. package/apis/menu/all.js +13 -19
  22. package/apis/menu/select.js +19 -14
  23. package/apis/operateLog/select.js +13 -4
  24. package/apis/role/_role.js +21 -0
  25. package/apis/role/all.js +1 -3
  26. package/apis/role/apiSave.js +8 -15
  27. package/apis/role/apis.js +4 -10
  28. package/apis/role/delete.js +28 -36
  29. package/apis/role/detail.js +4 -10
  30. package/apis/role/insert.js +12 -10
  31. package/apis/role/menuSave.js +9 -15
  32. package/apis/role/menus.js +4 -10
  33. package/apis/role/save.js +19 -23
  34. package/apis/role/select.js +12 -9
  35. package/apis/role/update.js +14 -15
  36. package/apis/source/imageList.js +3 -3
  37. package/apis/sysConfig/get.js +11 -23
  38. package/apis/sysConfig/insert.js +22 -27
  39. package/apis/sysConfig/select.js +11 -5
  40. package/apis/sysConfig/update.js +33 -38
  41. package/apis/tongJi/_tongJi.js +41 -0
  42. package/apis/tongJi/cacheHealth.js +8 -30
  43. package/apis/tongJi/errorList.js +26 -27
  44. package/apis/tongJi/errorReport.js +26 -43
  45. package/apis/tongJi/errorStats.js +17 -48
  46. package/apis/tongJi/fallbackReset.js +7 -15
  47. package/apis/tongJi/infoReport.js +20 -32
  48. package/apis/tongJi/infoStats.js +5 -17
  49. package/apis/tongJi/onlineReport.js +50 -56
  50. package/apis/tongJi/onlineStats.js +97 -111
  51. package/checks/config.js +44 -1
  52. package/configs/beflyConfig.json +10 -1
  53. package/index.js +25 -0
  54. package/lib/dbHelper.js +1 -1
  55. package/lib/dbParse.js +61 -99
  56. package/lib/dbUtil.js +101 -21
  57. package/lib/redisHelper.js +25 -0
  58. package/lib/sqlBuilder.js +6 -0
  59. package/package.json +1 -1
  60. package/plugins/email.js +3 -6
  61. package/router/api.js +0 -7
  62. package/utils/email.js +3 -0
  63. package/apis/admin/cacheRefresh.js +0 -122
  64. package/apis/dashboard/configStatus.js +0 -39
  65. package/apis/dashboard/performanceMetrics.js +0 -23
  66. package/apis/dashboard/permissionStats.js +0 -27
  67. package/apis/dashboard/systemInfo.js +0 -19
  68. package/lib/requestMetrics.js +0 -203
@@ -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
- try {
21
- const existing = await befly.mysql.getOne({
22
- table: "beflySysConfig",
23
- where: { code: ctx.body.code }
24
- });
20
+ const existing = await befly.mysql.getOne({
21
+ table: "beflySysConfig",
22
+ where: { code: ctx.body.code }
23
+ });
25
24
 
26
- if (existing.data?.id) {
27
- return befly.tool.No("配置代码已存在");
28
- }
25
+ if (existing.data?.id) {
26
+ return befly.tool.No("配置代码已存在");
27
+ }
29
28
 
30
- const configId = await befly.mysql.insData({
31
- table: "beflySysConfig",
32
- data: {
33
- name: ctx.body.name,
34
- code: ctx.body.code,
35
- value: ctx.body.value,
36
- valueType: ctx.body.valueType || "string",
37
- group: ctx.body.group || "",
38
- sort: ctx.body.sort || 0,
39
- isSystem: ctx.body.isSystem || 0,
40
- description: ctx.body.description || ""
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
- return befly.tool.Yes("操作成功", { id: configId.data });
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
  };
@@ -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
- page: { name: "页码", input: "integer", min: 1, max: 9999 },
8
- limit: { name: "每页数量", input: "integer", min: 1, max: 100 },
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
- fields: ["id", "name", "code", "value", "valueType", "group", "sort", "isSystem", "description", "state", "createdAt", "updatedAt"],
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"]
@@ -6,7 +6,7 @@ export default {
6
6
  body: "none",
7
7
  auth: true,
8
8
  fields: {
9
- id: { name: "ID", input: "integer", min: 1, max: null },
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: { name: "状态", input: "integer", min: 0, max: 2 }
17
+ state: sysConfigTable.state
18
18
  },
19
19
  required: ["id"],
20
20
  handler: async (befly, ctx) => {
21
- try {
22
- const config = await befly.mysql.getOne({
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
+ }
@@ -1,4 +1,6 @@
1
- import { addDays, getDateYmdNumber } from "#root/utils/datetime.js";
1
+ import { getDateYmdNumber } from "#root/utils/datetime.js";
2
+
3
+ import { getTongJiMonthStartDate, getTongJiNumber, getTongJiWeekStartDate } from "./_tongJi.js";
2
4
 
3
5
  const ONLINE_ACTIVE_KEY = "online:active";
4
6
  const INFO_STATS_FALLBACK_COUNT_KEY = "stats:fallback:infoStats:count";
@@ -12,30 +14,6 @@ function getErrorStatsFallbackDailyCountKey(reportDate) {
12
14
  return `stats:fallback:errorStats:count:day:${reportDate}`;
13
15
  }
14
16
 
15
- function toNumber(value) {
16
- const num = Number(value);
17
-
18
- if (!Number.isFinite(num)) {
19
- return 0;
20
- }
21
-
22
- return num;
23
- }
24
-
25
- function getWeekStartDate(timestamp = Date.now()) {
26
- const date = Reflect.construct(Date, [timestamp]);
27
- const day = date.getDay();
28
- const offset = day === 0 ? -6 : 1 - day;
29
-
30
- return getDateYmdNumber(addDays(timestamp, offset));
31
- }
32
-
33
- function getMonthStartDate(timestamp = Date.now()) {
34
- const date = Reflect.construct(Date, [timestamp]);
35
-
36
- return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
37
- }
38
-
39
17
  function getOnlineReportTimeKey(periodType, periodValue) {
40
18
  return `online:${periodType}:${periodValue}:reportTime`;
41
19
  }
@@ -49,7 +27,7 @@ function getErrorCountKey(periodType, periodValue) {
49
27
  }
50
28
 
51
29
  async function getRedisStringNumber(redis, key) {
52
- return toNumber(await redis.getString(key));
30
+ return getTongJiNumber(await redis.getString(key));
53
31
  }
54
32
 
55
33
  async function getRedisSetCount(redis, key) {
@@ -57,7 +35,7 @@ async function getRedisSetCount(redis, key) {
57
35
  return 0;
58
36
  }
59
37
 
60
- return toNumber(await redis.scard(key));
38
+ return getTongJiNumber(await redis.scard(key));
61
39
  }
62
40
 
63
41
  async function getRedisSortedSetCount(redis, key) {
@@ -65,7 +43,7 @@ async function getRedisSortedSetCount(redis, key) {
65
43
  return 0;
66
44
  }
67
45
 
68
- return toNumber(await redis.zcard(key));
46
+ return getTongJiNumber(await redis.zcard(key));
69
47
  }
70
48
 
71
49
  async function getRedisConnectivity(redis) {
@@ -102,8 +80,8 @@ export default {
102
80
  handler: async (befly) => {
103
81
  const now = Date.now();
104
82
  const reportDate = getDateYmdNumber(now);
105
- const weekStartDate = getWeekStartDate(now);
106
- const monthStartDate = getMonthStartDate(now);
83
+ const weekStartDate = getTongJiWeekStartDate(now);
84
+ const monthStartDate = getTongJiMonthStartDate(now);
107
85
  const redisConnectivity = await getRedisConnectivity(befly.redis);
108
86
 
109
87
  if (!befly.redis) {
@@ -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
- page: { name: "页码", input: "integer", min: 1, max: 9999 },
8
- limit: { name: "每页数量", input: "integer", min: 1, max: 100 },
9
- keyword: { name: "关键词", input: "string", min: 0, max: 100 },
10
- errorType: { name: "错误类型", input: "string", min: 0, max: 50 },
11
- source: { name: "来源", input: "string", min: 0, max: 50 },
12
- productName: { name: "产品名称", input: "string", min: 0, max: 100 },
13
- productCode: { name: "产品代号", input: "string", min: 0, max: 100 },
14
- productVersion: { name: "产品版本", input: "string", min: 0, max: 100 },
15
- deviceType: { name: "设备类型", input: "string", min: 0, max: 50 },
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 = Number(ctx.body.page || 1);
23
- const limit = Number(ctx.body.limit || 20);
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["$or"] = [
47
- { pagePath$like: keyword },
48
- { pageName$like: keyword },
49
- { message$like: keyword },
50
- { errorType$like: keyword },
51
- { productName$like: keyword },
52
- { productCode$like: keyword },
53
- { productVersion$like: keyword },
54
- { browserName$like: keyword },
55
- { osName$like: keyword },
56
- { deviceType$like: keyword },
57
- { deviceVendor$like: keyword },
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) {
@@ -1,6 +1,9 @@
1
1
  import { UAParser } from "ua-parser-js";
2
2
 
3
- import { addDays, getDateYmdNumber, getTimeBucketStart } from "#root/utils/datetime.js";
3
+ import errorReportTable from "#root/tables/errorReport.json";
4
+ import { getDateYmdNumber, getTimeBucketStart } from "#root/utils/datetime.js";
5
+
6
+ import { expireTongJiRedisKeys, getTongJiMonthStartDate, getTongJiWeekStartDate } from "./_tongJi.js";
4
7
 
5
8
  const VISIT_STATS_BUCKET_MS = 30 * 60 * 1000;
6
9
  const ERROR_STATS_REDIS_TTL_SECONDS = 45 * 24 * 60 * 60;
@@ -30,20 +33,6 @@ function getErrorReportUaData(ctx) {
30
33
  };
31
34
  }
32
35
 
33
- function getErrorStatsWeekStartDate(timestamp = Date.now()) {
34
- const date = Reflect.construct(Date, [timestamp]);
35
- const day = date.getDay();
36
- const offset = day === 0 ? -6 : 1 - day;
37
-
38
- return getDateYmdNumber(addDays(timestamp, offset));
39
- }
40
-
41
- function getErrorStatsMonthStartDate(timestamp = Date.now()) {
42
- const date = Reflect.construct(Date, [timestamp]);
43
-
44
- return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
45
- }
46
-
47
36
  function getErrorStatsPeriodCountKey(periodType, periodValue) {
48
37
  return `error:${periodType}:${periodValue}:count`;
49
38
  }
@@ -64,19 +53,13 @@ function getErrorStatsDayTypeCountKey(bucketDate, errorType) {
64
53
  return `error:day:${bucketDate}:type:${encodeURIComponent(String(errorType || "unknown"))}:count`;
65
54
  }
66
55
 
67
- async function setErrorStatsRedisTtl(befly, keys) {
68
- for (const key of keys) {
69
- await befly.redis.expire(key, ERROR_STATS_REDIS_TTL_SECONDS);
70
- }
71
- }
72
-
73
56
  async function updateErrorStatsRedis(befly, now, bucketDate, bucketTime, errorType) {
74
57
  if (!befly.redis) {
75
58
  return;
76
59
  }
77
60
 
78
- const weekStartDate = getErrorStatsWeekStartDate(now);
79
- const monthStartDate = getErrorStatsMonthStartDate(now);
61
+ const weekStartDate = getTongJiWeekStartDate(now);
62
+ const monthStartDate = getTongJiMonthStartDate(now);
80
63
  const dayCountKey = getErrorStatsPeriodCountKey("day", bucketDate);
81
64
  const weekCountKey = getErrorStatsPeriodCountKey("week", weekStartDate);
82
65
  const monthCountKey = getErrorStatsPeriodCountKey("month", monthStartDate);
@@ -95,7 +78,7 @@ async function updateErrorStatsRedis(befly, now, bucketDate, bucketTime, errorTy
95
78
  await befly.redis.sadd(dayTypesKey, [String(errorType || "unknown")]);
96
79
  await befly.redis.incr(dayTypeCountKey);
97
80
 
98
- await setErrorStatsRedisTtl(befly, [dayCountKey, weekCountKey, monthCountKey, dayBucketsKey, dayBucketCountKey, dayTypesKey, dayTypeCountKey]);
81
+ await expireTongJiRedisKeys(befly, [dayCountKey, weekCountKey, monthCountKey, dayBucketsKey, dayBucketCountKey, dayTypesKey, dayTypeCountKey], ERROR_STATS_REDIS_TTL_SECONDS);
99
82
  }
100
83
 
101
84
  export default {
@@ -104,15 +87,15 @@ export default {
104
87
  body: "none",
105
88
  auth: false,
106
89
  fields: {
107
- pagePath: { name: "页面路径", input: "string", min: 0, max: 200 },
108
- pageName: { name: "页面名称", input: "string", min: 0, max: 100 },
109
- source: { name: "来源", input: "string", min: 0, max: 50 },
110
- productName: { name: "产品名称", input: "string", min: 0, max: 100 },
111
- productCode: { name: "产品代号", input: "string", min: 0, max: 100 },
112
- productVersion: { name: "产品版本", input: "string", min: 0, max: 100 },
113
- errorType: { name: "错误类型", input: "string", min: 0, max: 50 },
114
- message: { name: "错误信息", input: "string", min: 0, max: 500 },
115
- detail: { name: "错误详情", input: "string", min: 0, max: 5000 }
90
+ pagePath: errorReportTable.pagePath,
91
+ pageName: errorReportTable.pageName,
92
+ source: errorReportTable.source,
93
+ productName: errorReportTable.productName,
94
+ productCode: errorReportTable.productCode,
95
+ productVersion: errorReportTable.productVersion,
96
+ errorType: errorReportTable.errorType,
97
+ message: errorReportTable.message,
98
+ detail: errorReportTable.detail
116
99
  },
117
100
  required: [],
118
101
  handler: async (befly, ctx) => {
@@ -129,15 +112,15 @@ export default {
129
112
  bucketTime: bucketTime,
130
113
  bucketDate: bucketDate,
131
114
  hitCount: 1,
132
- source: ctx.body?.source,
133
- productName: ctx.body?.productName || "",
134
- productCode: ctx.body?.productCode || "",
135
- productVersion: ctx.body?.productVersion || "",
136
- pagePath: ctx.body?.pagePath || "",
137
- pageName: ctx.body?.pageName || "",
138
- errorType: ctx.body?.errorType || "",
139
- message: ctx.body?.message || "",
140
- detail: ctx.body?.detail || "",
115
+ source: ctx.body.source || "",
116
+ productName: ctx.body.productName || "",
117
+ productCode: ctx.body.productCode || "",
118
+ productVersion: ctx.body.productVersion || "",
119
+ pagePath: ctx.body.pagePath || "",
120
+ pageName: ctx.body.pageName || "",
121
+ errorType: ctx.body.errorType || "",
122
+ message: ctx.body.message || "",
123
+ detail: ctx.body.detail || "",
141
124
  userAgent: uaData.userAgent,
142
125
  browserName: uaData.browserName,
143
126
  browserVersion: uaData.browserVersion,
@@ -151,7 +134,7 @@ export default {
151
134
  }
152
135
  });
153
136
 
154
- await updateErrorStatsRedis(befly, now, bucketDate, bucketTime, ctx.body?.errorType || "unknown");
137
+ await updateErrorStatsRedis(befly, now, bucketDate, bucketTime, ctx.body.errorType || "unknown");
155
138
 
156
139
  return befly.tool.Yes("上报成功", {
157
140
  reportTime: now,
@@ -1,5 +1,7 @@
1
1
  import { getDateYmdNumber } from "#root/utils/datetime.js";
2
2
 
3
+ import { getTongJiMonthStartDate, getTongJiNumber, getTongJiRecentDateList, getTongJiWeekStartDate } from "./_tongJi.js";
4
+
3
5
  const ERROR_STATS_DAY_LIMIT = 30;
4
6
  const ERROR_STATS_FALLBACK_COUNT_KEY = "stats:fallback:errorStats:count";
5
7
  const ERROR_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX = "stats:fallback:errorStats:count:day:";
@@ -9,39 +11,6 @@ function getErrorStatsFallbackDailyCountKey(bucketDate) {
9
11
  return `${ERROR_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX}${bucketDate}`;
10
12
  }
11
13
 
12
- function toNumber(value) {
13
- const num = Number(value);
14
- if (!Number.isFinite(num)) {
15
- return 0;
16
- }
17
-
18
- return num;
19
- }
20
-
21
- function getErrorStatsWeekStartDate(timestamp = Date.now()) {
22
- const date = Reflect.construct(Date, [timestamp]);
23
- const day = date.getDay();
24
- const offset = day === 0 ? -6 : 1 - day;
25
-
26
- return getDateYmdNumber(timestamp + offset * 24 * 60 * 60 * 1000);
27
- }
28
-
29
- function getErrorStatsMonthStartDate(timestamp = Date.now()) {
30
- const date = Reflect.construct(Date, [timestamp]);
31
-
32
- return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
33
- }
34
-
35
- function getErrorStatsRecentDateList(now = Date.now(), limit = ERROR_STATS_DAY_LIMIT) {
36
- const list = [];
37
-
38
- for (let i = limit - 1; i >= 0; i--) {
39
- list.push(getDateYmdNumber(now - i * 24 * 60 * 60 * 1000));
40
- }
41
-
42
- return list;
43
- }
44
-
45
14
  function getErrorStatsPeriodCountKey(periodType, periodValue) {
46
15
  return `error:${periodType}:${periodValue}:count`;
47
16
  }
@@ -64,7 +33,7 @@ function getErrorStatsDayTypeCountKey(bucketDate, errorType) {
64
33
 
65
34
  async function getErrorStatsRedisSummary(befly, periodType, periodValue) {
66
35
  return {
67
- count: toNumber(await befly.redis.getString(getErrorStatsPeriodCountKey(periodType, periodValue)))
36
+ count: getTongJiNumber(await befly.redis.getString(getErrorStatsPeriodCountKey(periodType, periodValue)))
68
37
  };
69
38
  }
70
39
 
@@ -73,12 +42,12 @@ async function getErrorStatsRedisTrend(befly, bucketDate) {
73
42
  const trend = [];
74
43
 
75
44
  for (const bucketTime of bucketSet) {
76
- const itemBucketTime = toNumber(bucketTime);
45
+ const itemBucketTime = getTongJiNumber(bucketTime);
77
46
 
78
47
  trend.push({
79
48
  bucketTime: itemBucketTime,
80
49
  bucketDate: bucketDate,
81
- count: toNumber(await befly.redis.getString(getErrorStatsDayBucketCountKey(bucketDate, itemBucketTime)))
50
+ count: getTongJiNumber(await befly.redis.getString(getErrorStatsDayBucketCountKey(bucketDate, itemBucketTime)))
82
51
  });
83
52
  }
84
53
 
@@ -92,7 +61,7 @@ async function getErrorStatsRedisDays(befly, recentDateList) {
92
61
  for (const item of recentDateList) {
93
62
  days.push({
94
63
  bucketDate: item,
95
- count: toNumber(await befly.redis.getString(getErrorStatsPeriodCountKey("day", item)))
64
+ count: getTongJiNumber(await befly.redis.getString(getErrorStatsPeriodCountKey("day", item)))
96
65
  });
97
66
  }
98
67
 
@@ -105,7 +74,7 @@ async function getErrorStatsRedisTopTypes(befly, bucketDate) {
105
74
 
106
75
  for (const errorType of typeSet) {
107
76
  const name = String(errorType || "unknown");
108
- const count = toNumber(await befly.redis.getString(getErrorStatsDayTypeCountKey(bucketDate, name)));
77
+ const count = getTongJiNumber(await befly.redis.getString(getErrorStatsDayTypeCountKey(bucketDate, name)));
109
78
 
110
79
  if (count <= 0) {
111
80
  continue;
@@ -185,7 +154,7 @@ async function getErrorStatsSummary(befly, startDate, endDate) {
185
154
  const detail = result.data?.[0] || {};
186
155
 
187
156
  return {
188
- count: toNumber(detail.count)
157
+ count: getTongJiNumber(detail.count)
189
158
  };
190
159
  }
191
160
 
@@ -201,9 +170,9 @@ export default {
201
170
  const tableReady = tableExistsResult.data === true;
202
171
  const now = Date.now();
203
172
  const bucketDate = getDateYmdNumber(now);
204
- const weekStartDate = getErrorStatsWeekStartDate(now);
205
- const monthStartDate = getErrorStatsMonthStartDate(now);
206
- const recentDateList = getErrorStatsRecentDateList(now);
173
+ const weekStartDate = getTongJiWeekStartDate(now);
174
+ const monthStartDate = getTongJiMonthStartDate(now);
175
+ const recentDateList = getTongJiRecentDateList(now, ERROR_STATS_DAY_LIMIT);
207
176
 
208
177
  if (!tableReady) {
209
178
  return befly.tool.Yes("获取成功", {
@@ -247,9 +216,9 @@ export default {
247
216
  const trend = [];
248
217
  for (const item of trendRes.data || []) {
249
218
  trend.push({
250
- bucketTime: toNumber(item.bucketTime),
251
- bucketDate: toNumber(item.bucketDate),
252
- count: toNumber(item.count)
219
+ bucketTime: getTongJiNumber(item.bucketTime),
220
+ bucketDate: getTongJiNumber(item.bucketDate),
221
+ count: getTongJiNumber(item.count)
253
222
  });
254
223
  }
255
224
 
@@ -263,7 +232,7 @@ export default {
263
232
  }
264
233
 
265
234
  for (const item of daysRes.data || []) {
266
- const itemBucketDate = toNumber(item.bucketDate);
235
+ const itemBucketDate = getTongJiNumber(item.bucketDate);
267
236
 
268
237
  if (!daysMap.has(itemBucketDate)) {
269
238
  continue;
@@ -271,7 +240,7 @@ export default {
271
240
 
272
241
  daysMap.set(itemBucketDate, {
273
242
  bucketDate: itemBucketDate,
274
- count: toNumber(item.count)
243
+ count: getTongJiNumber(item.count)
275
244
  });
276
245
  }
277
246
 
@@ -281,7 +250,7 @@ export default {
281
250
  for (const item of topTypesRes.data || []) {
282
251
  topTypes.push({
283
252
  errorType: String(item.errorType || "unknown"),
284
- count: toNumber(item.count)
253
+ count: getTongJiNumber(item.count)
285
254
  });
286
255
  }
287
256