befly 3.19.6 → 3.19.7

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.
@@ -35,6 +35,24 @@ function getPeriodKeys(periodType, periodValue) {
35
35
  };
36
36
  }
37
37
 
38
+ function getOnlineStatsProductKey(productName) {
39
+ return encodeURIComponent(String(productName || ""));
40
+ }
41
+
42
+ function getOnlineStatsProductPeriodKeys(periodType, periodValue, productName) {
43
+ const productKey = getOnlineStatsProductKey(productName);
44
+
45
+ return {
46
+ pv: `online:${periodType}:${periodValue}:product:${productKey}:pv`,
47
+ members: `online:${periodType}:${periodValue}:product:${productKey}:members`,
48
+ reportTime: `online:${periodType}:${periodValue}:product:${productKey}:reportTime`
49
+ };
50
+ }
51
+
52
+ function getOnlineStatsProductsKey(periodType, periodValue) {
53
+ return `online:${periodType}:${periodValue}:products`;
54
+ }
55
+
38
56
  async function touchRedisKeys(befly, keys, ttl) {
39
57
  for (const key of keys) {
40
58
  await befly.redis.expire(key, ttl);
@@ -76,12 +94,34 @@ async function updateOnlineStatsPeriod(befly, periodType, periodValue, member) {
76
94
  await touchRedisKeys(befly, Object.values(keys), ONLINE_STATS_TEMP_TTL_SECONDS);
77
95
  }
78
96
 
97
+ async function updateOnlineStatsProductPeriod(befly, periodType, periodValue, member, productName) {
98
+ if (!productName) {
99
+ return;
100
+ }
101
+
102
+ const keys = getOnlineStatsProductPeriodKeys(periodType, periodValue, productName);
103
+ const productsKey = getOnlineStatsProductsKey(periodType, periodValue);
104
+
105
+ await befly.redis.incr(keys.pv);
106
+ await befly.redis.sadd(keys.members, [member]);
107
+ await befly.redis.setString(keys.reportTime, String(Date.now()), ONLINE_STATS_TEMP_TTL_SECONDS);
108
+ await befly.redis.sadd(productsKey, [productName]);
109
+ await touchRedisKeys(befly, [...Object.values(keys), productsKey], ONLINE_STATS_TEMP_TTL_SECONDS);
110
+ }
111
+
79
112
  export default {
80
113
  name: "上报在线统计",
81
114
  method: "POST",
82
115
  body: "none",
83
116
  auth: false,
84
- fields: {},
117
+ fields: {
118
+ pagePath: { name: "页面路径", input: "string", min: 0, max: 200 },
119
+ pageName: { name: "页面名称", input: "string", min: 0, max: 100 },
120
+ source: { name: "来源", input: "string", min: 0, max: 50 },
121
+ productName: { name: "产品名称", input: "string", min: 0, max: 100 },
122
+ productCode: { name: "产品代号", input: "string", min: 0, max: 100 },
123
+ productVersion: { name: "产品版本", input: "string", min: 0, max: 50 }
124
+ },
85
125
  required: [],
86
126
  handler: async (befly, ctx) => {
87
127
  const now = Date.now();
@@ -89,6 +129,7 @@ export default {
89
129
  const weekStartDate = getOnlineStatsWeekStartDate(now);
90
130
  const monthStartDate = getOnlineStatsMonthStartDate(now);
91
131
  const member = getOnlineStatsMember(ctx);
132
+ const productName = ctx.body?.productName || "";
92
133
 
93
134
  await befly.redis.setString(`online:visitor:${member}`, "1", ONLINE_STATS_ONLINE_TTL_SECONDS);
94
135
  await befly.redis.sadd("online:visitors", [member]);
@@ -99,6 +140,9 @@ export default {
99
140
  await updateOnlineStatsPeriod(befly, "day", reportDate, member);
100
141
  await updateOnlineStatsPeriod(befly, "week", weekStartDate, member);
101
142
  await updateOnlineStatsPeriod(befly, "month", monthStartDate, member);
143
+ await updateOnlineStatsProductPeriod(befly, "day", reportDate, member, productName);
144
+ await updateOnlineStatsProductPeriod(befly, "week", weekStartDate, member, productName);
145
+ await updateOnlineStatsProductPeriod(befly, "month", monthStartDate, member, productName);
102
146
 
103
147
  return befly.tool.Yes("上报成功", {
104
148
  reportTime: now,
@@ -44,6 +44,24 @@ function getPeriodKeys(periodType, periodValue) {
44
44
  };
45
45
  }
46
46
 
47
+ function getOnlineStatsProductKey(productName) {
48
+ return encodeURIComponent(String(productName || ""));
49
+ }
50
+
51
+ function getOnlineStatsProductPeriodKeys(periodType, periodValue, productName) {
52
+ const productKey = getOnlineStatsProductKey(productName);
53
+
54
+ return {
55
+ pv: `online:${periodType}:${periodValue}:product:${productKey}:pv`,
56
+ members: `online:${periodType}:${periodValue}:product:${productKey}:members`,
57
+ reportTime: `online:${periodType}:${periodValue}:product:${productKey}:reportTime`
58
+ };
59
+ }
60
+
61
+ function getOnlineStatsProductsKey(periodType, periodValue) {
62
+ return `online:${periodType}:${periodValue}:products`;
63
+ }
64
+
47
65
  async function getRedisNumber(befly, key) {
48
66
  return toOnlineStatsNumber(await befly.redis.getString(key));
49
67
  }
@@ -57,6 +75,32 @@ async function getOnlineStatsPeriodData(befly, periodType, periodValue) {
57
75
  };
58
76
  }
59
77
 
78
+ async function getOnlineStatsProductPeriodData(befly, periodType, periodValue, productName) {
79
+ const keys = getOnlineStatsProductPeriodKeys(periodType, periodValue, productName);
80
+
81
+ return {
82
+ reportTime: await getRedisNumber(befly, keys.reportTime),
83
+ pv: await getRedisNumber(befly, keys.pv),
84
+ uv: toOnlineStatsNumber(await befly.redis.scard(keys.members))
85
+ };
86
+ }
87
+
88
+ async function getOnlineStatsProductNames(befly, recentDateList) {
89
+ const productSet = new Set();
90
+
91
+ for (const item of recentDateList) {
92
+ for (const productName of await befly.redis.smembers(getOnlineStatsProductsKey("day", item))) {
93
+ if (!productName) {
94
+ continue;
95
+ }
96
+
97
+ productSet.add(String(productName));
98
+ }
99
+ }
100
+
101
+ return Array.from(productSet.values());
102
+ }
103
+
60
104
  async function getOnlineStatsOnlineCount(befly) {
61
105
  const members = await befly.redis.smembers("online:visitors");
62
106
  if (members.length === 0) {
@@ -83,6 +127,16 @@ async function getOnlineStatsOnlineCount(befly) {
83
127
  return onlineCount;
84
128
  }
85
129
 
130
+ function sumProductTrendField(list, field) {
131
+ let total = 0;
132
+
133
+ for (const item of list) {
134
+ total += toOnlineStatsNumber(item?.[field]);
135
+ }
136
+
137
+ return total;
138
+ }
139
+
86
140
  export default {
87
141
  name: "获取在线统计",
88
142
  method: "POST",
@@ -97,6 +151,7 @@ export default {
97
151
  const monthStartDate = getOnlineStatsMonthStartDate(now);
98
152
  const recentDateList = getOnlineStatsRecentDateList(now, ONLINE_STATS_DAY_LIMIT);
99
153
  const days = [];
154
+ const products = [];
100
155
 
101
156
  for (const item of recentDateList) {
102
157
  const dayData = await getOnlineStatsPeriodData(befly, "day", item);
@@ -117,6 +172,58 @@ export default {
117
172
  const weekCurrent = await getOnlineStatsPeriodData(befly, "week", weekStartDate);
118
173
  const monthCurrent = await getOnlineStatsPeriodData(befly, "month", monthStartDate);
119
174
  const onlineCount = await getOnlineStatsOnlineCount(befly);
175
+ const productNames = await getOnlineStatsProductNames(befly, recentDateList);
176
+
177
+ for (const productName of productNames) {
178
+ const productDays = [];
179
+
180
+ for (const item of recentDateList) {
181
+ const dayData = await getOnlineStatsProductPeriodData(befly, "day", item, productName);
182
+
183
+ if (dayData.reportTime <= 0) {
184
+ continue;
185
+ }
186
+
187
+ productDays.push({
188
+ reportDate: item,
189
+ reportTime: dayData.reportTime,
190
+ pv: dayData.pv,
191
+ uv: dayData.uv
192
+ });
193
+ }
194
+
195
+ const productToday = await getOnlineStatsProductPeriodData(befly, "day", reportDate, productName);
196
+ const productWeek = await getOnlineStatsProductPeriodData(befly, "week", weekStartDate, productName);
197
+ const productMonth = await getOnlineStatsProductPeriodData(befly, "month", monthStartDate, productName);
198
+
199
+ products.push({
200
+ key: productName,
201
+ productName: productName,
202
+ today: {
203
+ pv: productToday.pv,
204
+ uv: productToday.uv
205
+ },
206
+ week: {
207
+ pv: productWeek.pv,
208
+ uv: productWeek.uv
209
+ },
210
+ month: {
211
+ pv: productMonth.pv,
212
+ uv: productMonth.uv
213
+ },
214
+ days: productDays,
215
+ totalPv: sumProductTrendField(productDays, "pv"),
216
+ totalUv: sumProductTrendField(productDays, "uv")
217
+ });
218
+ }
219
+
220
+ products.sort((a, b) => {
221
+ if (b.totalPv !== a.totalPv) {
222
+ return b.totalPv - a.totalPv;
223
+ }
224
+
225
+ return String(a.productName).localeCompare(String(b.productName), "zh-CN");
226
+ });
120
227
 
121
228
  return befly.tool.Yes("获取成功", {
122
229
  queryTime: now,
@@ -141,7 +248,8 @@ export default {
141
248
  pv: monthCurrent.pv,
142
249
  uv: monthCurrent.uv
143
250
  },
144
- days: days
251
+ days: days,
252
+ products: products
145
253
  });
146
254
  }
147
255
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.19.6",
4
- "gitHead": "84b6e4e0adde7f566cfc890887b024ccf10b0516",
3
+ "version": "3.19.7",
4
+ "gitHead": "c47d1e515dabe142531e0a8415979cccb2729259",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
7
7
  "keywords": [