befly 3.19.5 → 3.19.6

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.
@@ -89,7 +89,10 @@ export default {
89
89
  const lists = [];
90
90
  for (const item of result.data?.lists || []) {
91
91
  const row = Object.assign({}, item);
92
+ row.reportTime = Number(item.reportTime || 0);
92
93
  row.firstReportTime = Number(item.firstReportTime || 0);
94
+ row.bucketTime = Number(item.bucketTime || 0);
95
+ row.bucketDate = Number(item.bucketDate || 0);
93
96
  row.hitCount = Number(item.hitCount || 0) || 1;
94
97
  lists.push(row);
95
98
  }
@@ -1,6 +1,7 @@
1
1
  import { UAParser } from "ua-parser-js";
2
2
  import { getDateYmdNumber, getTimeBucketStart } from "../../utils/datetime.js";
3
- import { VISIT_STATS_BUCKET_MS } from "../../utils/visitStats.js";
3
+
4
+ const VISIT_STATS_BUCKET_MS = 30 * 60 * 1000;
4
5
 
5
6
  function getErrorReportUaData(ctx) {
6
7
  let userAgent = "";
@@ -1,5 +1,7 @@
1
1
  import { getDateYmdNumber } from "../../utils/datetime.js";
2
2
 
3
+ const ERROR_STATS_DAY_LIMIT = 30;
4
+
3
5
  function toNumber(value) {
4
6
  const num = Number(value);
5
7
  if (!Number.isFinite(num)) {
@@ -9,6 +11,39 @@ function toNumber(value) {
9
11
  return num;
10
12
  }
11
13
 
14
+ function getErrorStatsWeekStartDate(timestamp = Date.now()) {
15
+ const date = Reflect.construct(Date, [timestamp]);
16
+ const day = date.getDay();
17
+ const offset = day === 0 ? -6 : 1 - day;
18
+
19
+ return getDateYmdNumber(timestamp + offset * 24 * 60 * 60 * 1000);
20
+ }
21
+
22
+ function getErrorStatsMonthStartDate(timestamp = Date.now()) {
23
+ const date = Reflect.construct(Date, [timestamp]);
24
+
25
+ return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
26
+ }
27
+
28
+ function getErrorStatsRecentDateList(now = Date.now(), limit = ERROR_STATS_DAY_LIMIT) {
29
+ const list = [];
30
+
31
+ for (let i = limit - 1; i >= 0; i--) {
32
+ list.push(getDateYmdNumber(now - i * 24 * 60 * 60 * 1000));
33
+ }
34
+
35
+ return list;
36
+ }
37
+
38
+ async function getErrorStatsSummary(befly, startDate, endDate) {
39
+ const result = await befly.mysql.execute("SELECT SUM(hit_count) as count FROM befly_error_report WHERE state = 1 AND bucket_date BETWEEN ? AND ?", [startDate, endDate]);
40
+ const detail = result.data?.[0] || {};
41
+
42
+ return {
43
+ count: toNumber(detail.count)
44
+ };
45
+ }
46
+
12
47
  export default {
13
48
  name: "获取错误统计",
14
49
  method: "POST",
@@ -24,14 +59,34 @@ export default {
24
59
 
25
60
  if (!tableReady) {
26
61
  return befly.tool.Yes("获取成功", {
62
+ today: {
63
+ bucketDate: bucketDate,
64
+ count: 0
65
+ },
66
+ week: {
67
+ startDate: getErrorStatsWeekStartDate(now),
68
+ endDate: bucketDate,
69
+ count: 0
70
+ },
71
+ month: {
72
+ startDate: getErrorStatsMonthStartDate(now),
73
+ endDate: bucketDate,
74
+ count: 0
75
+ },
27
76
  trend: [],
28
77
  days: [],
29
78
  topTypes: []
30
79
  });
31
80
  }
32
81
 
82
+ const weekStartDate = getErrorStatsWeekStartDate(now);
83
+ const monthStartDate = getErrorStatsMonthStartDate(now);
84
+ const recentDateList = getErrorStatsRecentDateList(now);
85
+ const todaySummary = await getErrorStatsSummary(befly, bucketDate, bucketDate);
86
+ const weekSummary = await getErrorStatsSummary(befly, weekStartDate, bucketDate);
87
+ const monthSummary = await getErrorStatsSummary(befly, monthStartDate, bucketDate);
33
88
  const trendRes = await befly.mysql.execute("SELECT bucket_time as bucketTime, bucket_date as bucketDate, SUM(hit_count) as count FROM befly_error_report WHERE state = 1 AND bucket_date = ? GROUP BY bucket_time, bucket_date ORDER BY bucket_time ASC", [bucketDate]);
34
- const daysRes = await befly.mysql.execute("SELECT bucket_date as bucketDate, SUM(hit_count) as count FROM befly_error_report WHERE state = 1 GROUP BY bucket_date ORDER BY bucket_date DESC LIMIT 7", []);
89
+ const daysRes = await befly.mysql.execute("SELECT bucket_date as bucketDate, SUM(hit_count) as count FROM befly_error_report WHERE state = 1 AND bucket_date >= ? GROUP BY bucket_date ORDER BY bucket_date ASC", [recentDateList[0]]);
35
90
  const topTypesRes = await befly.mysql.execute("SELECT error_type as errorType, SUM(hit_count) as count FROM befly_error_report WHERE state = 1 AND bucket_date = ? GROUP BY error_type ORDER BY count DESC LIMIT 5", [bucketDate]);
36
91
 
37
92
  const trend = [];
@@ -43,15 +98,29 @@ export default {
43
98
  });
44
99
  }
45
100
 
46
- const days = [];
101
+ const daysMap = new Map();
102
+
103
+ for (const item of recentDateList) {
104
+ daysMap.set(item, {
105
+ bucketDate: item,
106
+ count: 0
107
+ });
108
+ }
109
+
47
110
  for (const item of daysRes.data || []) {
48
- days.push({
49
- bucketDate: toNumber(item.bucketDate),
111
+ const itemBucketDate = toNumber(item.bucketDate);
112
+
113
+ if (!daysMap.has(itemBucketDate)) {
114
+ continue;
115
+ }
116
+
117
+ daysMap.set(itemBucketDate, {
118
+ bucketDate: itemBucketDate,
50
119
  count: toNumber(item.count)
51
120
  });
52
121
  }
53
122
 
54
- days.sort((a, b) => a.bucketDate - b.bucketDate);
123
+ const days = Array.from(daysMap.values());
55
124
 
56
125
  const topTypes = [];
57
126
  for (const item of topTypesRes.data || []) {
@@ -62,6 +131,20 @@ export default {
62
131
  }
63
132
 
64
133
  return befly.tool.Yes("获取成功", {
134
+ today: {
135
+ bucketDate: bucketDate,
136
+ count: todaySummary.count
137
+ },
138
+ week: {
139
+ startDate: weekStartDate,
140
+ endDate: bucketDate,
141
+ count: weekSummary.count
142
+ },
143
+ month: {
144
+ startDate: monthStartDate,
145
+ endDate: bucketDate,
146
+ count: monthSummary.count
147
+ },
65
148
  trend: trend,
66
149
  days: days,
67
150
  topTypes: topTypes
@@ -0,0 +1,104 @@
1
+ import { UAParser } from "ua-parser-js";
2
+
3
+ import { isValidPositiveInt } from "../../utils/is.js";
4
+ import { getDateYmdNumber } from "../../utils/datetime.js";
5
+
6
+ function getInfoStatsMember(ctx) {
7
+ if (isValidPositiveInt(ctx.userId)) {
8
+ return `user:${ctx.userId}`;
9
+ }
10
+
11
+ return `ip:${ctx.ip || "unknown"}`;
12
+ }
13
+
14
+ export default {
15
+ name: "上报访问信息统计",
16
+ method: "POST",
17
+ body: "none",
18
+ auth: false,
19
+ fields: {
20
+ pagePath: { name: "页面路径", input: "string", min: 0, max: 200 },
21
+ pageName: { name: "页面名称", input: "string", min: 0, max: 100 },
22
+ source: { name: "来源", input: "string", min: 0, max: 50 },
23
+ productName: { name: "产品名称", input: "string", min: 0, max: 100 },
24
+ productCode: { name: "产品代号", input: "string", min: 0, max: 100 },
25
+ productVersion: { name: "产品版本", input: "string", min: 0, max: 100 },
26
+ detail: { name: "扩展详情", input: "string", min: 0, max: 5000 }
27
+ },
28
+ required: [],
29
+ handler: async (befly, ctx) => {
30
+ const now = Date.now();
31
+ const reportDate = getDateYmdNumber(now);
32
+ const member = getInfoStatsMember(ctx);
33
+ const tableReady = (await befly.mysql.tableExists("beflyInfoReport")).data === true;
34
+ const body = ctx.body || {};
35
+ let userAgent = "";
36
+
37
+ if (!tableReady) {
38
+ return befly.tool.Yes("上报成功", {
39
+ reportTime: now,
40
+ reportDate: reportDate,
41
+ counted: false,
42
+ stored: false
43
+ });
44
+ }
45
+
46
+ if (typeof ctx.req?.headers?.get === "function") {
47
+ userAgent = ctx.req.headers.get("user-agent") || "";
48
+ } else if (typeof ctx.headers?.get === "function") {
49
+ userAgent = ctx.headers.get("user-agent") || "";
50
+ }
51
+
52
+ const uaResult = UAParser(userAgent);
53
+
54
+ const detail = await befly.mysql.getOne({
55
+ table: "beflyInfoReport",
56
+ where: {
57
+ reportDate: reportDate,
58
+ memberKey: member
59
+ }
60
+ });
61
+
62
+ if (detail.data?.id) {
63
+ return befly.tool.Yes("上报成功", {
64
+ reportTime: now,
65
+ reportDate: reportDate,
66
+ counted: false,
67
+ stored: false
68
+ });
69
+ }
70
+
71
+ await befly.mysql.insData({
72
+ table: "beflyInfoReport",
73
+ data: {
74
+ reportTime: now,
75
+ reportDate: reportDate,
76
+ memberKey: member,
77
+ source: body.source || "",
78
+ productName: body.productName || "",
79
+ productCode: body.productCode || "",
80
+ productVersion: body.productVersion || "",
81
+ pagePath: body.pagePath || "",
82
+ pageName: body.pageName || "",
83
+ detail: body.detail || "",
84
+ userAgent: userAgent,
85
+ deviceType: uaResult.device.type || "desktop",
86
+ browserName: uaResult.browser.name || "",
87
+ browserVersion: uaResult.browser.version || "",
88
+ osName: uaResult.os.name || "",
89
+ osVersion: uaResult.os.version || "",
90
+ deviceVendor: uaResult.device.vendor || "",
91
+ deviceModel: uaResult.device.model || "",
92
+ engineName: uaResult.engine.name || "",
93
+ cpuArchitecture: uaResult.cpu.architecture || ""
94
+ }
95
+ });
96
+
97
+ return befly.tool.Yes("上报成功", {
98
+ reportTime: now,
99
+ reportDate: reportDate,
100
+ counted: true,
101
+ stored: true
102
+ });
103
+ }
104
+ };
@@ -0,0 +1,147 @@
1
+ import { addDays, getDateYmdNumber } from "../../utils/datetime.js";
2
+
3
+ const INFO_STATS_FIELDS = [
4
+ { dbField: "source", key: "sources" },
5
+ { dbField: "product_name", key: "productNames" },
6
+ { dbField: "product_code", key: "productCodes" },
7
+ { dbField: "product_version", key: "productVersions" },
8
+ { dbField: "page_path", key: "pagePaths" },
9
+ { dbField: "page_name", key: "pageNames" },
10
+ { dbField: "device_type", key: "deviceTypes" },
11
+ { dbField: "browser_name", key: "browsers" },
12
+ { dbField: "browser_version", key: "browserVersions" },
13
+ { dbField: "os_name", key: "osList" },
14
+ { dbField: "os_version", key: "osVersions" },
15
+ { dbField: "device_vendor", key: "deviceVendors" },
16
+ { dbField: "device_model", key: "deviceModels" },
17
+ { dbField: "engine_name", key: "engines" },
18
+ { dbField: "cpu_architecture", key: "cpuArchitectures" }
19
+ ];
20
+
21
+ function getInfoStatsWeekStartDate(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(addDays(timestamp, offset));
27
+ }
28
+
29
+ function getInfoStatsMonthStartDate(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 formatInfoStatsList(rows = []) {
36
+ const list = [];
37
+
38
+ for (const item of rows || []) {
39
+ const name = String(item.name || "").trim();
40
+ const count = Number(item.count || 0);
41
+
42
+ if (!name || count <= 0) {
43
+ continue;
44
+ }
45
+
46
+ list.push({
47
+ name: name,
48
+ count: count
49
+ });
50
+ }
51
+
52
+ return list;
53
+ }
54
+
55
+ async function getInfoStatsFieldList(befly, startDate, endDate, dbField) {
56
+ const result = await befly.mysql.execute(`SELECT ${dbField} as name, COUNT(*) as count FROM befly_info_report WHERE state = 1 AND report_date BETWEEN ? AND ? AND ${dbField} <> '' GROUP BY ${dbField} ORDER BY count DESC, name ASC LIMIT 10`, [startDate, endDate]);
57
+
58
+ return formatInfoStatsList(result.data || []);
59
+ }
60
+
61
+ async function getInfoStatsRange(befly, startDate, endDate) {
62
+ const data = {};
63
+
64
+ for (const item of INFO_STATS_FIELDS) {
65
+ data[item.key] = await getInfoStatsFieldList(befly, startDate, endDate, item.dbField);
66
+ }
67
+
68
+ return data;
69
+ }
70
+
71
+ export default {
72
+ name: "获取访问信息统计",
73
+ method: "POST",
74
+ body: "none",
75
+ auth: true,
76
+ fields: {},
77
+ required: [],
78
+ handler: async (befly) => {
79
+ const now = Date.now();
80
+ const reportDate = getDateYmdNumber(now);
81
+ const weekStartDate = getInfoStatsWeekStartDate(now);
82
+ const monthStartDate = getInfoStatsMonthStartDate(now);
83
+
84
+ const today = await getInfoStatsRange(befly, reportDate, reportDate);
85
+ const week = await getInfoStatsRange(befly, weekStartDate, reportDate);
86
+ const month = await getInfoStatsRange(befly, monthStartDate, reportDate);
87
+
88
+ return befly.tool.Yes("获取成功", {
89
+ today: {
90
+ reportDate: reportDate,
91
+ sources: today.sources,
92
+ productNames: today.productNames,
93
+ productCodes: today.productCodes,
94
+ productVersions: today.productVersions,
95
+ pagePaths: today.pagePaths,
96
+ pageNames: today.pageNames,
97
+ deviceTypes: today.deviceTypes,
98
+ browsers: today.browsers,
99
+ browserVersions: today.browserVersions,
100
+ osList: today.osList,
101
+ osVersions: today.osVersions,
102
+ deviceVendors: today.deviceVendors,
103
+ deviceModels: today.deviceModels,
104
+ engines: today.engines,
105
+ cpuArchitectures: today.cpuArchitectures
106
+ },
107
+ week: {
108
+ startDate: weekStartDate,
109
+ endDate: reportDate,
110
+ sources: week.sources,
111
+ productNames: week.productNames,
112
+ productCodes: week.productCodes,
113
+ productVersions: week.productVersions,
114
+ pagePaths: week.pagePaths,
115
+ pageNames: week.pageNames,
116
+ deviceTypes: week.deviceTypes,
117
+ browsers: week.browsers,
118
+ browserVersions: week.browserVersions,
119
+ osList: week.osList,
120
+ osVersions: week.osVersions,
121
+ deviceVendors: week.deviceVendors,
122
+ deviceModels: week.deviceModels,
123
+ engines: week.engines,
124
+ cpuArchitectures: week.cpuArchitectures
125
+ },
126
+ month: {
127
+ startDate: monthStartDate,
128
+ endDate: reportDate,
129
+ sources: month.sources,
130
+ productNames: month.productNames,
131
+ productCodes: month.productCodes,
132
+ productVersions: month.productVersions,
133
+ pagePaths: month.pagePaths,
134
+ pageNames: month.pageNames,
135
+ deviceTypes: month.deviceTypes,
136
+ browsers: month.browsers,
137
+ browserVersions: month.browserVersions,
138
+ osList: month.osList,
139
+ osVersions: month.osVersions,
140
+ deviceVendors: month.deviceVendors,
141
+ deviceModels: month.deviceModels,
142
+ engines: month.engines,
143
+ cpuArchitectures: month.cpuArchitectures
144
+ }
145
+ });
146
+ }
147
+ };
@@ -0,0 +1,110 @@
1
+ import { addDays, getDateYmdNumber } from "../../utils/datetime.js";
2
+ import { isValidPositiveInt } from "../../utils/is.js";
3
+
4
+ const ONLINE_STATS_ONLINE_TTL_SECONDS = 10 * 60;
5
+ const ONLINE_STATS_REDIS_TTL_SECONDS = 7 * 24 * 60 * 60;
6
+ const ONLINE_STATS_TEMP_TTL_SECONDS = 45 * 24 * 60 * 60;
7
+
8
+ function getOnlineStatsMember(ctx) {
9
+ if (isValidPositiveInt(ctx.userId)) {
10
+ return `user:${ctx.userId}`;
11
+ }
12
+
13
+ return `ip:${ctx.ip || "unknown"}`;
14
+ }
15
+
16
+ function getOnlineStatsWeekStartDate(timestamp = Date.now()) {
17
+ const date = Reflect.construct(Date, [timestamp]);
18
+ const day = date.getDay();
19
+ const offset = day === 0 ? -6 : 1 - day;
20
+
21
+ return getDateYmdNumber(addDays(timestamp, offset));
22
+ }
23
+
24
+ function getOnlineStatsMonthStartDate(timestamp = Date.now()) {
25
+ const date = Reflect.construct(Date, [timestamp]);
26
+
27
+ return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
28
+ }
29
+
30
+ function getPeriodKeys(periodType, periodValue) {
31
+ return {
32
+ pv: `online:${periodType}:${periodValue}:pv`,
33
+ members: `online:${periodType}:${periodValue}:members`,
34
+ reportTime: `online:${periodType}:${periodValue}:reportTime`
35
+ };
36
+ }
37
+
38
+ async function touchRedisKeys(befly, keys, ttl) {
39
+ for (const key of keys) {
40
+ await befly.redis.expire(key, ttl);
41
+ }
42
+ }
43
+
44
+ async function getOnlineStatsOnlineCount(befly) {
45
+ const members = await befly.redis.smembers("online:visitors");
46
+ if (members.length === 0) {
47
+ return 0;
48
+ }
49
+
50
+ const existsList = await befly.redis.existsBatch(members.map((member) => `online:visitor:${member}`));
51
+ const expiredMembers = [];
52
+ let onlineCount = 0;
53
+
54
+ for (let i = 0; i < members.length; i++) {
55
+ if (existsList[i]) {
56
+ onlineCount += 1;
57
+ continue;
58
+ }
59
+
60
+ expiredMembers.push(members[i]);
61
+ }
62
+
63
+ if (expiredMembers.length > 0) {
64
+ await befly.redis.srem("online:visitors", expiredMembers);
65
+ }
66
+
67
+ return onlineCount;
68
+ }
69
+
70
+ async function updateOnlineStatsPeriod(befly, periodType, periodValue, member) {
71
+ const keys = getPeriodKeys(periodType, periodValue);
72
+
73
+ await befly.redis.incr(keys.pv);
74
+ await befly.redis.sadd(keys.members, [member]);
75
+ await befly.redis.setString(keys.reportTime, String(Date.now()), ONLINE_STATS_TEMP_TTL_SECONDS);
76
+ await touchRedisKeys(befly, Object.values(keys), ONLINE_STATS_TEMP_TTL_SECONDS);
77
+ }
78
+
79
+ export default {
80
+ name: "上报在线统计",
81
+ method: "POST",
82
+ body: "none",
83
+ auth: false,
84
+ fields: {},
85
+ required: [],
86
+ handler: async (befly, ctx) => {
87
+ const now = Date.now();
88
+ const reportDate = getDateYmdNumber(now);
89
+ const weekStartDate = getOnlineStatsWeekStartDate(now);
90
+ const monthStartDate = getOnlineStatsMonthStartDate(now);
91
+ const member = getOnlineStatsMember(ctx);
92
+
93
+ await befly.redis.setString(`online:visitor:${member}`, "1", ONLINE_STATS_ONLINE_TTL_SECONDS);
94
+ await befly.redis.sadd("online:visitors", [member]);
95
+ await befly.redis.expire("online:visitors", ONLINE_STATS_REDIS_TTL_SECONDS);
96
+
97
+ const onlineCount = await getOnlineStatsOnlineCount(befly);
98
+
99
+ await updateOnlineStatsPeriod(befly, "day", reportDate, member);
100
+ await updateOnlineStatsPeriod(befly, "week", weekStartDate, member);
101
+ await updateOnlineStatsPeriod(befly, "month", monthStartDate, member);
102
+
103
+ return befly.tool.Yes("上报成功", {
104
+ reportTime: now,
105
+ reportDate: reportDate,
106
+ onlineCount: onlineCount,
107
+ stored: true
108
+ });
109
+ }
110
+ };
@@ -0,0 +1,147 @@
1
+ import { addDays, getDateYmdNumber } from "../../utils/datetime.js";
2
+
3
+ const ONLINE_STATS_DAY_LIMIT = 30;
4
+
5
+ function toOnlineStatsNumber(value) {
6
+ const num = Number(value);
7
+
8
+ if (!Number.isFinite(num)) {
9
+ return 0;
10
+ }
11
+
12
+ return num;
13
+ }
14
+
15
+ function getOnlineStatsWeekStartDate(timestamp = Date.now()) {
16
+ const date = Reflect.construct(Date, [timestamp]);
17
+ const day = date.getDay();
18
+ const offset = day === 0 ? -6 : 1 - day;
19
+
20
+ return getDateYmdNumber(addDays(timestamp, offset));
21
+ }
22
+
23
+ function getOnlineStatsMonthStartDate(timestamp = Date.now()) {
24
+ const date = Reflect.construct(Date, [timestamp]);
25
+
26
+ return getDateYmdNumber(Reflect.construct(Date, [date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0]).getTime());
27
+ }
28
+
29
+ function getOnlineStatsRecentDateList(now = Date.now(), limit = ONLINE_STATS_DAY_LIMIT) {
30
+ const list = [];
31
+
32
+ for (let i = limit - 1; i >= 0; i--) {
33
+ list.push(getDateYmdNumber(addDays(now, -i)));
34
+ }
35
+
36
+ return list;
37
+ }
38
+
39
+ function getPeriodKeys(periodType, periodValue) {
40
+ return {
41
+ pv: `online:${periodType}:${periodValue}:pv`,
42
+ members: `online:${periodType}:${periodValue}:members`,
43
+ reportTime: `online:${periodType}:${periodValue}:reportTime`
44
+ };
45
+ }
46
+
47
+ async function getRedisNumber(befly, key) {
48
+ return toOnlineStatsNumber(await befly.redis.getString(key));
49
+ }
50
+
51
+ async function getOnlineStatsPeriodData(befly, periodType, periodValue) {
52
+ const keys = getPeriodKeys(periodType, periodValue);
53
+ return {
54
+ reportTime: await getRedisNumber(befly, keys.reportTime),
55
+ pv: await getRedisNumber(befly, keys.pv),
56
+ uv: toOnlineStatsNumber(await befly.redis.scard(keys.members))
57
+ };
58
+ }
59
+
60
+ async function getOnlineStatsOnlineCount(befly) {
61
+ const members = await befly.redis.smembers("online:visitors");
62
+ if (members.length === 0) {
63
+ return 0;
64
+ }
65
+
66
+ const existsList = await befly.redis.existsBatch(members.map((member) => `online:visitor:${member}`));
67
+ const expiredMembers = [];
68
+ let onlineCount = 0;
69
+
70
+ for (let i = 0; i < members.length; i++) {
71
+ if (existsList[i]) {
72
+ onlineCount += 1;
73
+ continue;
74
+ }
75
+
76
+ expiredMembers.push(members[i]);
77
+ }
78
+
79
+ if (expiredMembers.length > 0) {
80
+ await befly.redis.srem("online:visitors", expiredMembers);
81
+ }
82
+
83
+ return onlineCount;
84
+ }
85
+
86
+ export default {
87
+ name: "获取在线统计",
88
+ method: "POST",
89
+ body: "none",
90
+ auth: true,
91
+ fields: {},
92
+ required: [],
93
+ handler: async (befly) => {
94
+ const now = Date.now();
95
+ const reportDate = getDateYmdNumber(now);
96
+ const weekStartDate = getOnlineStatsWeekStartDate(now);
97
+ const monthStartDate = getOnlineStatsMonthStartDate(now);
98
+ const recentDateList = getOnlineStatsRecentDateList(now, ONLINE_STATS_DAY_LIMIT);
99
+ const days = [];
100
+
101
+ for (const item of recentDateList) {
102
+ const dayData = await getOnlineStatsPeriodData(befly, "day", item);
103
+
104
+ if (dayData.reportTime <= 0) {
105
+ continue;
106
+ }
107
+
108
+ days.push({
109
+ reportDate: item,
110
+ reportTime: dayData.reportTime,
111
+ pv: dayData.pv,
112
+ uv: dayData.uv
113
+ });
114
+ }
115
+
116
+ const currentDay = await getOnlineStatsPeriodData(befly, "day", reportDate);
117
+ const weekCurrent = await getOnlineStatsPeriodData(befly, "week", weekStartDate);
118
+ const monthCurrent = await getOnlineStatsPeriodData(befly, "month", monthStartDate);
119
+ const onlineCount = await getOnlineStatsOnlineCount(befly);
120
+
121
+ return befly.tool.Yes("获取成功", {
122
+ queryTime: now,
123
+ onlineCount: onlineCount,
124
+ today: {
125
+ reportDate: reportDate,
126
+ reportTime: currentDay.reportTime,
127
+ pv: currentDay.pv,
128
+ uv: currentDay.uv
129
+ },
130
+ week: {
131
+ startDate: weekStartDate,
132
+ endDate: reportDate,
133
+ reportTime: weekCurrent.reportTime,
134
+ pv: weekCurrent.pv,
135
+ uv: weekCurrent.uv
136
+ },
137
+ month: {
138
+ startDate: monthStartDate,
139
+ endDate: reportDate,
140
+ reportTime: monthCurrent.reportTime,
141
+ pv: monthCurrent.pv,
142
+ uv: monthCurrent.uv
143
+ },
144
+ days: days
145
+ });
146
+ }
147
+ };