befly 3.36.0 → 3.38.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.
@@ -1,5 +1,3 @@
1
- import cacheHealthApi from "#befly/apis/tongJi/cacheHealth.js";
2
-
3
1
  function isEmailConfigured(emailConfig) {
4
2
  if (!emailConfig || typeof emailConfig !== "object") {
5
3
  return false;
@@ -48,25 +46,6 @@ function buildStaticServices(befly) {
48
46
  return [buildServiceItem("文件系统", "running", "-"), buildServiceItem("邮件服务", isEmailConfigured(befly.config?.email) ? "running" : "unconfigured", "-"), buildServiceItem("OSS存储", "unconfigured", "-")];
49
47
  }
50
48
 
51
- function buildCacheHealthSummary(cacheHealthData) {
52
- if (!cacheHealthData) {
53
- return null;
54
- }
55
-
56
- return {
57
- redisStatus: cacheHealthData.redis?.status || "unknown",
58
- onlineReady: Boolean(cacheHealthData.online?.ready),
59
- infoReady: Boolean(cacheHealthData.info?.ready),
60
- errorReady: Boolean(cacheHealthData.error?.ready),
61
- roleApisReady: Boolean(cacheHealthData.roleCache?.apis?.ready),
62
- roleMenusReady: Boolean(cacheHealthData.roleCache?.menus?.ready),
63
- infoFallbackCount: Number(cacheHealthData.fallback?.infoStats || 0),
64
- errorFallbackCount: Number(cacheHealthData.fallback?.errorStats || 0),
65
- infoFallbackTodayCount: Number(cacheHealthData.fallback?.today?.infoStats || 0),
66
- errorFallbackTodayCount: Number(cacheHealthData.fallback?.today?.errorStats || 0)
67
- };
68
- }
69
-
70
49
  export default {
71
50
  name: "获取服务状态",
72
51
  method: "POST",
@@ -77,17 +56,6 @@ export default {
77
56
  handler: async (befly) => {
78
57
  const services = [await probeMysqlService(befly), await probeRedisService(befly), ...buildStaticServices(befly)];
79
58
 
80
- let cacheHealthSummary = null;
81
-
82
- try {
83
- const cacheHealthResult = await cacheHealthApi.handler(befly);
84
- const cacheHealthData = cacheHealthResult?.data || null;
85
-
86
- cacheHealthSummary = buildCacheHealthSummary(cacheHealthData);
87
- } catch (error) {
88
- befly.logger.error("缓存健康状态聚合失败", error);
89
- }
90
-
91
- return befly.tool.Yes("获取成功", { services: services, cacheHealth: cacheHealthSummary });
59
+ return befly.tool.Yes("获取成功", { services: services });
92
60
  }
93
61
  };
@@ -39,3 +39,51 @@ export async function expireTongJiRedisKeys(befly, keys, ttl) {
39
39
  await befly.redis.expire(key, ttl);
40
40
  }
41
41
  }
42
+
43
+ export function getVisitStatsDayMembersKey(reportDate) {
44
+ return `visit:day:${reportDate}:members`;
45
+ }
46
+
47
+ export function getVisitStatsDayProductMembersKey(reportDate, productCode) {
48
+ return `visit:day:${reportDate}:product:${encodeURIComponent(productCode)}:members`;
49
+ }
50
+
51
+ export function getVisitStatsMonthMembersKey(monthStartDate) {
52
+ return `visit:month:${monthStartDate}:members`;
53
+ }
54
+
55
+ export function getVisitStatsMonthProductMembersKey(monthStartDate, productCode) {
56
+ return `visit:month:${monthStartDate}:product:${encodeURIComponent(productCode)}:members`;
57
+ }
58
+
59
+ export function getVisitStatsRecentMembersKey() {
60
+ return "visit:recent30:members";
61
+ }
62
+
63
+ export function getVisitStatsRecentProductMembersKey(productCode) {
64
+ return `visit:recent30:product:${encodeURIComponent(productCode)}:members`;
65
+ }
66
+
67
+ export function getOnlineStatsActiveProductKey(productCode) {
68
+ return `online:active:product:${encodeURIComponent(productCode)}`;
69
+ }
70
+
71
+ export function getErrorStatsPeriodCountKey(periodType, periodValue) {
72
+ return `error:${periodType}:${periodValue}:count`;
73
+ }
74
+
75
+ export function getErrorStatsDayBucketsKey(bucketDate) {
76
+ return `error:day:${bucketDate}:buckets`;
77
+ }
78
+
79
+ export function getErrorStatsDayBucketCountKey(bucketDate, bucketTime) {
80
+ return `error:day:${bucketDate}:bucket:${bucketTime}:count`;
81
+ }
82
+
83
+ export function getErrorStatsDayTypesKey(bucketDate) {
84
+ return `error:day:${bucketDate}:types`;
85
+ }
86
+
87
+ export function getErrorStatsDayTypeCountKey(bucketDate, errorType) {
88
+ return `error:day:${bucketDate}:type:${encodeURIComponent(String(errorType || "unknown"))}:count`;
89
+ }
@@ -3,7 +3,7 @@ import { UAParser } from "ua-parser-js";
3
3
  import errorReportTable from "#befly/tables/errorReport.json";
4
4
  import { getDateYmdNumber, getTimeBucketStart } from "#befly/utils/datetime.js";
5
5
 
6
- import { expireTongJiRedisKeys, getTongJiMonthStartDate, getTongJiWeekStartDate } from "./_tongJi.js";
6
+ import { expireTongJiRedisKeys, getErrorStatsDayBucketCountKey, getErrorStatsDayBucketsKey, getErrorStatsDayTypeCountKey, getErrorStatsDayTypesKey, getErrorStatsPeriodCountKey, getTongJiMonthStartDate, getTongJiWeekStartDate } from "./_tongJi.js";
7
7
 
8
8
  const VISIT_STATS_BUCKET_MS = 30 * 60 * 1000;
9
9
  const ERROR_STATS_REDIS_TTL_SECONDS = 45 * 24 * 60 * 60;
@@ -33,26 +33,6 @@ function getErrorReportUaData(ctx) {
33
33
  };
34
34
  }
35
35
 
36
- function getErrorStatsPeriodCountKey(periodType, periodValue) {
37
- return `error:${periodType}:${periodValue}:count`;
38
- }
39
-
40
- function getErrorStatsDayBucketsKey(bucketDate) {
41
- return `error:day:${bucketDate}:buckets`;
42
- }
43
-
44
- function getErrorStatsDayBucketCountKey(bucketDate, bucketTime) {
45
- return `error:day:${bucketDate}:bucket:${bucketTime}:count`;
46
- }
47
-
48
- function getErrorStatsDayTypesKey(bucketDate) {
49
- return `error:day:${bucketDate}:types`;
50
- }
51
-
52
- function getErrorStatsDayTypeCountKey(bucketDate, errorType) {
53
- return `error:day:${bucketDate}:type:${encodeURIComponent(String(errorType || "unknown"))}:count`;
54
- }
55
-
56
36
  async function updateErrorStatsRedis(befly, now, bucketDate, bucketTime, errorType) {
57
37
  if (!befly.redis) {
58
38
  return;
@@ -1,35 +1,8 @@
1
1
  import { getDateYmdNumber } from "#befly/utils/datetime.js";
2
2
 
3
- import { getTongJiMonthStartDate, getTongJiNumber, getTongJiRecentDateList, getTongJiWeekStartDate } from "./_tongJi.js";
3
+ import { getErrorStatsDayBucketCountKey, getErrorStatsDayBucketsKey, getErrorStatsDayTypeCountKey, getErrorStatsDayTypesKey, getErrorStatsPeriodCountKey, getTongJiMonthStartDate, getTongJiNumber, getTongJiRecentDateList, getTongJiWeekStartDate } from "./_tongJi.js";
4
4
 
5
5
  const ERROR_STATS_DAY_LIMIT = 30;
6
- const ERROR_STATS_FALLBACK_COUNT_KEY = "stats:fallback:errorStats:count";
7
- const ERROR_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX = "stats:fallback:errorStats:count:day:";
8
- const ERROR_STATS_FALLBACK_TTL_SECONDS = 7 * 24 * 60 * 60;
9
-
10
- function getErrorStatsFallbackDailyCountKey(bucketDate) {
11
- return `${ERROR_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX}${bucketDate}`;
12
- }
13
-
14
- function getErrorStatsPeriodCountKey(periodType, periodValue) {
15
- return `error:${periodType}:${periodValue}:count`;
16
- }
17
-
18
- function getErrorStatsDayBucketsKey(bucketDate) {
19
- return `error:day:${bucketDate}:buckets`;
20
- }
21
-
22
- function getErrorStatsDayBucketCountKey(bucketDate, bucketTime) {
23
- return `error:day:${bucketDate}:bucket:${bucketTime}:count`;
24
- }
25
-
26
- function getErrorStatsDayTypesKey(bucketDate) {
27
- return `error:day:${bucketDate}:types`;
28
- }
29
-
30
- function getErrorStatsDayTypeCountKey(bucketDate, errorType) {
31
- return `error:day:${bucketDate}:type:${encodeURIComponent(String(errorType || "unknown"))}:count`;
32
- }
33
6
 
34
7
  async function getErrorStatsRedisSummary(befly, periodType, periodValue) {
35
8
  return {
@@ -99,13 +72,22 @@ async function getErrorStatsRedisTopTypes(befly, bucketDate) {
99
72
 
100
73
  async function getErrorStatsFromRedis(befly, bucketDate, weekStartDate, monthStartDate, recentDateList) {
101
74
  const todaySummary = await getErrorStatsRedisSummary(befly, "day", bucketDate);
75
+ const yesterdayDate = recentDateList[recentDateList.length - 2] || bucketDate;
76
+ const dayBeforeYesterdayDate = recentDateList[recentDateList.length - 3] || yesterdayDate;
77
+ const yesterdaySummary = await getErrorStatsRedisSummary(befly, "day", yesterdayDate);
78
+ const dayBeforeYesterdaySummary = await getErrorStatsRedisSummary(befly, "day", dayBeforeYesterdayDate);
102
79
  const weekSummary = await getErrorStatsRedisSummary(befly, "week", weekStartDate);
103
80
  const monthSummary = await getErrorStatsRedisSummary(befly, "month", monthStartDate);
104
81
  const trend = await getErrorStatsRedisTrend(befly, bucketDate);
105
82
  const days = await getErrorStatsRedisDays(befly, recentDateList);
106
83
  const topTypes = await getErrorStatsRedisTopTypes(befly, bucketDate);
84
+ let recent30Count = 0;
85
+
86
+ for (const item of days) {
87
+ recent30Count += item.count;
88
+ }
107
89
 
108
- if (todaySummary.count <= 0 && weekSummary.count <= 0 && monthSummary.count <= 0 && trend.length === 0 && topTypes.length === 0) {
90
+ if (todaySummary.count <= 0 && yesterdaySummary.count <= 0 && dayBeforeYesterdaySummary.count <= 0 && weekSummary.count <= 0 && monthSummary.count <= 0 && recent30Count <= 0 && trend.length === 0 && topTypes.length === 0) {
109
91
  return null;
110
92
  }
111
93
 
@@ -114,6 +96,14 @@ async function getErrorStatsFromRedis(befly, bucketDate, weekStartDate, monthSta
114
96
  bucketDate: bucketDate,
115
97
  count: todaySummary.count
116
98
  },
99
+ yesterday: {
100
+ bucketDate: yesterdayDate,
101
+ count: yesterdaySummary.count
102
+ },
103
+ dayBeforeYesterday: {
104
+ bucketDate: dayBeforeYesterdayDate,
105
+ count: dayBeforeYesterdaySummary.count
106
+ },
117
107
  week: {
118
108
  startDate: weekStartDate,
119
109
  endDate: bucketDate,
@@ -124,31 +114,17 @@ async function getErrorStatsFromRedis(befly, bucketDate, weekStartDate, monthSta
124
114
  endDate: bucketDate,
125
115
  count: monthSummary.count
126
116
  },
117
+ recent30: {
118
+ startDate: recentDateList[0],
119
+ endDate: bucketDate,
120
+ count: recent30Count
121
+ },
127
122
  trend: trend,
128
123
  days: days,
129
124
  topTypes: topTypes
130
125
  };
131
126
  }
132
127
 
133
- async function incrErrorStatsFallbackCount(befly, bucketDate) {
134
- if (!befly.redis) {
135
- return;
136
- }
137
-
138
- const dailyKey = getErrorStatsFallbackDailyCountKey(bucketDate);
139
-
140
- if (typeof befly.redis.incrWithExpire === "function") {
141
- await befly.redis.incrWithExpire(ERROR_STATS_FALLBACK_COUNT_KEY, ERROR_STATS_FALLBACK_TTL_SECONDS);
142
- await befly.redis.incrWithExpire(dailyKey, ERROR_STATS_FALLBACK_TTL_SECONDS);
143
- return;
144
- }
145
-
146
- await befly.redis.incr(ERROR_STATS_FALLBACK_COUNT_KEY);
147
- await befly.redis.expire(ERROR_STATS_FALLBACK_COUNT_KEY, ERROR_STATS_FALLBACK_TTL_SECONDS);
148
- await befly.redis.incr(dailyKey);
149
- await befly.redis.expire(dailyKey, ERROR_STATS_FALLBACK_TTL_SECONDS);
150
- }
151
-
152
128
  async function getErrorStatsSummary(befly, startDate, endDate) {
153
129
  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]);
154
130
  const detail = result.data?.[0] || {};
@@ -173,13 +149,32 @@ export default {
173
149
  const weekStartDate = getTongJiWeekStartDate(now);
174
150
  const monthStartDate = getTongJiMonthStartDate(now);
175
151
  const recentDateList = getTongJiRecentDateList(now, ERROR_STATS_DAY_LIMIT);
152
+ const yesterdayDate = recentDateList[recentDateList.length - 2] || bucketDate;
153
+ const dayBeforeYesterdayDate = recentDateList[recentDateList.length - 3] || yesterdayDate;
176
154
 
177
155
  if (!tableReady) {
156
+ const days = [];
157
+
158
+ for (const item of recentDateList) {
159
+ days.push({
160
+ bucketDate: item,
161
+ count: 0
162
+ });
163
+ }
164
+
178
165
  return befly.tool.Yes("获取成功", {
179
166
  today: {
180
167
  bucketDate: bucketDate,
181
168
  count: 0
182
169
  },
170
+ yesterday: {
171
+ bucketDate: yesterdayDate,
172
+ count: 0
173
+ },
174
+ dayBeforeYesterday: {
175
+ bucketDate: dayBeforeYesterdayDate,
176
+ count: 0
177
+ },
183
178
  week: {
184
179
  startDate: weekStartDate,
185
180
  endDate: bucketDate,
@@ -190,8 +185,13 @@ export default {
190
185
  endDate: bucketDate,
191
186
  count: 0
192
187
  },
188
+ recent30: {
189
+ startDate: recentDateList[0],
190
+ endDate: bucketDate,
191
+ count: 0
192
+ },
193
193
  trend: [],
194
- days: [],
194
+ days: days,
195
195
  topTypes: []
196
196
  });
197
197
  }
@@ -202,13 +202,14 @@ export default {
202
202
  if (redisResult) {
203
203
  return befly.tool.Yes("获取成功", redisResult);
204
204
  }
205
-
206
- await incrErrorStatsFallbackCount(befly, bucketDate);
207
205
  }
208
206
 
209
207
  const todaySummary = await getErrorStatsSummary(befly, bucketDate, bucketDate);
208
+ const yesterdaySummary = await getErrorStatsSummary(befly, yesterdayDate, yesterdayDate);
209
+ const dayBeforeYesterdaySummary = await getErrorStatsSummary(befly, dayBeforeYesterdayDate, dayBeforeYesterdayDate);
210
210
  const weekSummary = await getErrorStatsSummary(befly, weekStartDate, bucketDate);
211
211
  const monthSummary = await getErrorStatsSummary(befly, monthStartDate, bucketDate);
212
+ const recent30Summary = await getErrorStatsSummary(befly, recentDateList[0], bucketDate);
212
213
  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]);
213
214
  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]]);
214
215
  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]);
@@ -259,6 +260,14 @@ export default {
259
260
  bucketDate: bucketDate,
260
261
  count: todaySummary.count
261
262
  },
263
+ yesterday: {
264
+ bucketDate: yesterdayDate,
265
+ count: yesterdaySummary.count
266
+ },
267
+ dayBeforeYesterday: {
268
+ bucketDate: dayBeforeYesterdayDate,
269
+ count: dayBeforeYesterdaySummary.count
270
+ },
262
271
  week: {
263
272
  startDate: weekStartDate,
264
273
  endDate: bucketDate,
@@ -269,6 +278,11 @@ export default {
269
278
  endDate: bucketDate,
270
279
  count: monthSummary.count
271
280
  },
281
+ recent30: {
282
+ startDate: recentDateList[0],
283
+ endDate: bucketDate,
284
+ count: recent30Summary.count
285
+ },
272
286
  trend: trend,
273
287
  days: days,
274
288
  topTypes: topTypes
@@ -1,108 +1,76 @@
1
- import { UAParser } from "ua-parser-js";
2
-
3
- import infoReportTable from "#befly/tables/infoReport.json";
4
- import { getDateYmdNumber } from "#befly/utils/datetime.js";
1
+ import { addDays, getDateYmdNumber, getDayStartTime } from "#befly/utils/datetime.js";
5
2
  import { isValidPositiveInt } from "#befly/utils/is.js";
6
3
 
7
- import { getTongJiMonthStartDate, getTongJiWeekStartDate } from "./_tongJi.js";
4
+ import { expireTongJiRedisKeys, getTongJiMonthStartDate, getVisitStatsDayMembersKey, getVisitStatsDayProductMembersKey, getVisitStatsMonthMembersKey, getVisitStatsMonthProductMembersKey, getVisitStatsRecentMembersKey, getVisitStatsRecentProductMembersKey } from "./_tongJi.js";
8
5
 
9
- const INFO_STATS_REDIS_TTL_SECONDS = 45 * 24 * 60 * 60;
10
- const INFO_STATS_FIELDS = [
11
- { key: "sources", valueKey: "source" },
12
- { key: "productNames", valueKey: "productName" },
13
- { key: "productCodes", valueKey: "productCode" },
14
- { key: "productVersions", valueKey: "productVersion" },
15
- { key: "pagePaths", valueKey: "pagePath" },
16
- { key: "pageNames", valueKey: "pageName" },
17
- { key: "deviceTypes", valueKey: "deviceType" },
18
- { key: "browsers", valueKey: "browserName" },
19
- { key: "browserVersions", valueKey: "browserVersion" },
20
- { key: "osList", valueKey: "osName" },
21
- { key: "osVersions", valueKey: "osVersion" },
22
- { key: "deviceVendors", valueKey: "deviceVendor" },
23
- { key: "deviceModels", valueKey: "deviceModel" },
24
- { key: "engines", valueKey: "engineName" },
25
- { key: "cpuArchitectures", valueKey: "cpuArchitecture" }
26
- ];
6
+ const VISIT_STATS_REDIS_TTL_SECONDS = 45 * 24 * 60 * 60;
27
7
 
28
- function getInfoStatsMember(ctx) {
29
- if (isValidPositiveInt(ctx.userId)) {
8
+ function getVisitStatsMember(ctx) {
9
+ if (isValidPositiveInt(ctx?.userId)) {
30
10
  return `user:${ctx.userId}`;
31
11
  }
32
12
 
33
- return `ip:${ctx.ip || "unknown"}`;
13
+ return `ip:${ctx?.ip || "unknown"}`;
34
14
  }
35
15
 
36
- function getInfoStatsNamesKey(periodType, periodValue, fieldKey) {
37
- return `info:${periodType}:${periodValue}:${fieldKey}:names`;
38
- }
16
+ async function addRecentVisitMember(befly, key, member, now) {
17
+ const cutoffTime = getDayStartTime(addDays(now, -29));
39
18
 
40
- function getInfoStatsCountKey(periodType, periodValue, fieldKey, value) {
41
- return `info:${periodType}:${periodValue}:${fieldKey}:count:${encodeURIComponent(String(value || ""))}`;
42
- }
43
-
44
- function getInfoStatsReportTimeKey(periodType, periodValue) {
45
- return `info:${periodType}:${periodValue}:reportTime`;
46
- }
47
-
48
- async function updateInfoStatsPeriodRedis(befly, periodType, periodValue, statData, now) {
49
- const reportTimeKey = getInfoStatsReportTimeKey(periodType, periodValue);
50
- await befly.redis.setString(reportTimeKey, String(now), INFO_STATS_REDIS_TTL_SECONDS);
51
- await befly.redis.expire(reportTimeKey, INFO_STATS_REDIS_TTL_SECONDS);
52
-
53
- for (const item of INFO_STATS_FIELDS) {
54
- const rawValue = String(statData[item.valueKey] || "").trim();
55
-
56
- if (!rawValue) {
57
- continue;
19
+ await befly.redis.zadd(key, [
20
+ {
21
+ score: now,
22
+ member: member
58
23
  }
59
-
60
- const namesKey = getInfoStatsNamesKey(periodType, periodValue, item.key);
61
- const countKey = getInfoStatsCountKey(periodType, periodValue, item.key, rawValue);
62
-
63
- await befly.redis.sadd(namesKey, [rawValue]);
64
- await befly.redis.incr(countKey);
65
- await befly.redis.expire(namesKey, INFO_STATS_REDIS_TTL_SECONDS);
66
- await befly.redis.expire(countKey, INFO_STATS_REDIS_TTL_SECONDS);
67
- }
24
+ ]);
25
+ await befly.redis.zremrangebyscore(key, "-inf", cutoffTime - 1);
26
+ await befly.redis.expire(key, VISIT_STATS_REDIS_TTL_SECONDS);
68
27
  }
69
28
 
70
- async function updateInfoStatsRedis(befly, now, reportDate, statData) {
71
- if (!befly.redis) {
72
- return;
73
- }
74
-
75
- const weekStartDate = getTongJiWeekStartDate(now);
29
+ async function updateVisitStatsRedis(befly, now, reportDate, member, productCode) {
76
30
  const monthStartDate = getTongJiMonthStartDate(now);
31
+ const dayKey = getVisitStatsDayMembersKey(reportDate);
32
+ const monthKey = getVisitStatsMonthMembersKey(monthStartDate);
33
+ const recentKey = getVisitStatsRecentMembersKey();
34
+ const dayAdded = await befly.redis.sadd(dayKey, [member]);
35
+
36
+ await befly.redis.sadd(monthKey, [member]);
37
+ await addRecentVisitMember(befly, recentKey, member, now);
38
+ await expireTongJiRedisKeys(befly, [dayKey, monthKey], VISIT_STATS_REDIS_TTL_SECONDS);
39
+
40
+ if (productCode) {
41
+ const dayProductKey = getVisitStatsDayProductMembersKey(reportDate, productCode);
42
+ const monthProductKey = getVisitStatsMonthProductMembersKey(monthStartDate, productCode);
43
+ const recentProductKey = getVisitStatsRecentProductMembersKey(productCode);
44
+
45
+ await befly.redis.sadd(dayProductKey, [member]);
46
+ await befly.redis.sadd(monthProductKey, [member]);
47
+ await addRecentVisitMember(befly, recentProductKey, member, now);
48
+ await expireTongJiRedisKeys(befly, [dayProductKey, monthProductKey], VISIT_STATS_REDIS_TTL_SECONDS);
49
+ }
77
50
 
78
- await updateInfoStatsPeriodRedis(befly, "day", reportDate, statData, now);
79
- await updateInfoStatsPeriodRedis(befly, "week", weekStartDate, statData, now);
80
- await updateInfoStatsPeriodRedis(befly, "month", monthStartDate, statData, now);
51
+ return dayAdded > 0;
81
52
  }
82
53
 
83
54
  export default {
84
- name: "上报访问信息统计",
55
+ name: "上报独立访客统计",
85
56
  method: "POST",
86
57
  body: "none",
87
58
  auth: false,
88
59
  fields: {
89
- pagePath: infoReportTable.pagePath,
90
- pageName: infoReportTable.pageName,
91
- source: infoReportTable.source,
92
- productName: infoReportTable.productName,
93
- productCode: infoReportTable.productCode,
94
- productVersion: infoReportTable.productVersion,
95
- detail: infoReportTable.detail
60
+ pagePath: { name: "页面路径", input: "string", min: 0, max: 200 },
61
+ pageName: { name: "页面名称", input: "string", min: 0, max: 100 },
62
+ source: { name: "来源", input: "string", min: 0, max: 50 },
63
+ productName: { name: "产品名称", input: "string", min: 0, max: 100 },
64
+ productCode: { name: "产品代号", input: "string", min: 0, max: 100 },
65
+ productVersion: { name: "产品版本", input: "string", min: 0, max: 100 },
66
+ detail: { name: "扩展详情", input: "string", min: 0, max: 1000 }
96
67
  },
97
68
  required: [],
98
69
  handler: async (befly, ctx) => {
99
70
  const now = Date.now();
100
71
  const reportDate = getDateYmdNumber(now);
101
- const member = getInfoStatsMember(ctx);
102
- const tableReady = (await befly.mysql.tableExists("beflyInfoReport")).data === true;
103
- let userAgent = "";
104
72
 
105
- if (!tableReady) {
73
+ if (!befly.redis) {
106
74
  return befly.tool.Yes("上报成功", {
107
75
  reportTime: now,
108
76
  reportDate: reportDate,
@@ -111,81 +79,12 @@ export default {
111
79
  });
112
80
  }
113
81
 
114
- if (typeof ctx.req?.headers?.get === "function") {
115
- userAgent = ctx.req.headers.get("user-agent") || "";
116
- } else if (typeof ctx.headers?.get === "function") {
117
- userAgent = ctx.headers.get("user-agent") || "";
118
- }
119
-
120
- const uaResult = UAParser(userAgent);
121
-
122
- const detail = await befly.mysql.getOne({
123
- table: "beflyInfoReport",
124
- where: {
125
- reportDate: reportDate,
126
- memberKey: member
127
- }
128
- });
129
-
130
- if (detail.data?.id) {
131
- return befly.tool.Yes("上报成功", {
132
- reportTime: now,
133
- reportDate: reportDate,
134
- counted: false,
135
- stored: false
136
- });
137
- }
138
-
139
- const statData = {
140
- source: ctx.body.source || "",
141
- productName: ctx.body.productName || "",
142
- productCode: ctx.body.productCode || "",
143
- productVersion: ctx.body.productVersion || "",
144
- pagePath: ctx.body.pagePath || "",
145
- pageName: ctx.body.pageName || "",
146
- deviceType: uaResult.device.type || "desktop",
147
- browserName: uaResult.browser.name || "",
148
- browserVersion: uaResult.browser.version || "",
149
- osName: uaResult.os.name || "",
150
- osVersion: uaResult.os.version || "",
151
- deviceVendor: uaResult.device.vendor || "",
152
- deviceModel: uaResult.device.model || "",
153
- engineName: uaResult.engine.name || "",
154
- cpuArchitecture: uaResult.cpu.architecture || ""
155
- };
156
-
157
- await befly.mysql.insData({
158
- table: "beflyInfoReport",
159
- data: {
160
- reportTime: now,
161
- reportDate: reportDate,
162
- memberKey: member,
163
- source: statData.source,
164
- productName: statData.productName,
165
- productCode: statData.productCode,
166
- productVersion: statData.productVersion,
167
- pagePath: statData.pagePath,
168
- pageName: statData.pageName,
169
- detail: ctx.body.detail || "",
170
- userAgent: userAgent,
171
- deviceType: statData.deviceType,
172
- browserName: statData.browserName,
173
- browserVersion: statData.browserVersion,
174
- osName: statData.osName,
175
- osVersion: statData.osVersion,
176
- deviceVendor: statData.deviceVendor,
177
- deviceModel: statData.deviceModel,
178
- engineName: statData.engineName,
179
- cpuArchitecture: statData.cpuArchitecture
180
- }
181
- });
182
-
183
- await updateInfoStatsRedis(befly, now, reportDate, statData);
82
+ const counted = await updateVisitStatsRedis(befly, now, reportDate, getVisitStatsMember(ctx), ctx.body?.productCode || "");
184
83
 
185
84
  return befly.tool.Yes("上报成功", {
186
85
  reportTime: now,
187
86
  reportDate: reportDate,
188
- counted: true,
87
+ counted: counted,
189
88
  stored: true
190
89
  });
191
90
  }