befly 3.19.5 → 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.
- package/apis/tongJi/errorList.js +3 -0
- package/apis/tongJi/errorReport.js +2 -1
- package/apis/tongJi/errorStats.js +88 -5
- package/apis/tongJi/infoReport.js +104 -0
- package/apis/tongJi/infoStats.js +147 -0
- package/apis/tongJi/onlineReport.js +154 -0
- package/apis/tongJi/onlineStats.js +255 -0
- package/configs/beflyMenus.json +4 -9
- package/package.json +3 -3
- package/sql/befly.sql +43 -30
- package/apis/tongJi/visitReport.js +0 -241
- package/apis/tongJi/visitStats.js +0 -380
- package/utils/visitStats.js +0 -121
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
import { addDays, getDateYmdNumber, getTimeBucketStart } from "../../utils/datetime.js";
|
|
2
|
-
import { VISIT_STATS_BUCKET_MS, VISIT_STATS_REDIS_TTL_SECONDS, VISIT_STATS_UA_FIELDS, getVisitStatsBucketKey, getVisitStatsBucketProductPvKey, getVisitStatsBucketProductUvKey, getVisitStatsDayKey, getVisitStatsDayProductKeysKey, getVisitStatsDayProductPvKey, getVisitStatsDayProductUvKey, getVisitStatsDayUaCountKey, getVisitStatsDayUaValuesKey, getVisitStatsProductMetaKey, normalizeVisitStatsUaValue, parseVisitStatsProductKey, toVisitStatsNumber } from "../../utils/visitStats.js";
|
|
3
|
-
|
|
4
|
-
const VISIT_STATS_DAY_LIMIT = 30;
|
|
5
|
-
|
|
6
|
-
async function ensureVisitStatsTableReady(befly) {
|
|
7
|
-
if (befly.visitStatsTableReady !== undefined) {
|
|
8
|
-
return befly.visitStatsTableReady;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const tableExistsResult = await befly.mysql.tableExists("beflyVisitStats");
|
|
12
|
-
befly.visitStatsTableReady = tableExistsResult.data === true;
|
|
13
|
-
return befly.visitStatsTableReady;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function flushVisitStatsBucket(befly, bucketTime) {
|
|
17
|
-
if (bucketTime <= 0) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const currentBucketTime = getTimeBucketStart(Date.now(), VISIT_STATS_BUCKET_MS);
|
|
22
|
-
if (bucketTime >= currentBucketTime) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const tableReady = await ensureVisitStatsTableReady(befly);
|
|
27
|
-
if (!tableReady) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const flushedKey = `visitStats:flushed:${bucketTime}`;
|
|
32
|
-
const flushed = await befly.redis.exists(flushedKey);
|
|
33
|
-
if (flushed) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const bucketDate = getDateYmdNumber(bucketTime);
|
|
38
|
-
const bucketKey = getVisitStatsBucketKey(bucketTime);
|
|
39
|
-
const pv = toVisitStatsNumber(await befly.redis.getString(`${bucketKey}:pv`));
|
|
40
|
-
const uv = await befly.redis.scard(`${bucketKey}:uv`);
|
|
41
|
-
const errorCount = toVisitStatsNumber(await befly.redis.getString(`${bucketKey}:errorCount`));
|
|
42
|
-
const durationSum = toVisitStatsNumber(await befly.redis.getString(`${bucketKey}:durationSum`));
|
|
43
|
-
const durationCount = toVisitStatsNumber(await befly.redis.getString(`${bucketKey}:durationCount`));
|
|
44
|
-
const avgDuration = durationCount > 0 ? Math.round(durationSum / durationCount) : 0;
|
|
45
|
-
|
|
46
|
-
if (pv <= 0 && uv <= 0 && errorCount <= 0 && durationCount <= 0) {
|
|
47
|
-
await befly.redis.setString(flushedKey, "1", VISIT_STATS_REDIS_TTL_SECONDS);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const data = {
|
|
52
|
-
bucketTime: bucketTime,
|
|
53
|
-
bucketDate: bucketDate,
|
|
54
|
-
pv: pv,
|
|
55
|
-
uv: uv,
|
|
56
|
-
errorCount: errorCount,
|
|
57
|
-
durationSum: durationSum,
|
|
58
|
-
durationCount: durationCount,
|
|
59
|
-
avgDuration: avgDuration
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const detail = await befly.mysql.getOne({
|
|
63
|
-
table: "beflyVisitStats",
|
|
64
|
-
where: { bucketTime: bucketTime }
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (detail.data?.id) {
|
|
68
|
-
await befly.mysql.updData({
|
|
69
|
-
table: "beflyVisitStats",
|
|
70
|
-
data: data,
|
|
71
|
-
where: { bucketTime: bucketTime }
|
|
72
|
-
});
|
|
73
|
-
} else {
|
|
74
|
-
await befly.mysql.insData({
|
|
75
|
-
table: "beflyVisitStats",
|
|
76
|
-
data: data
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
await befly.redis.setString(flushedKey, "1", VISIT_STATS_REDIS_TTL_SECONDS);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function flushVisitStatsPreviousBucket(befly, now = Date.now()) {
|
|
84
|
-
const currentBucketTime = getTimeBucketStart(now, VISIT_STATS_BUCKET_MS);
|
|
85
|
-
await flushVisitStatsBucket(befly, currentBucketTime - VISIT_STATS_BUCKET_MS);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function getVisitStatsOnlineCount(befly) {
|
|
89
|
-
const members = await befly.redis.smembers("visitStats:online:visitors");
|
|
90
|
-
if (members.length === 0) {
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const existsList = await befly.redis.existsBatch(members.map((member) => `visitStats:online:visitor:${member}`));
|
|
95
|
-
const expiredMembers = [];
|
|
96
|
-
let onlineCount = 0;
|
|
97
|
-
|
|
98
|
-
for (let i = 0; i < members.length; i++) {
|
|
99
|
-
if (existsList[i]) {
|
|
100
|
-
onlineCount += 1;
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
expiredMembers.push(members[i]);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (expiredMembers.length > 0) {
|
|
107
|
-
await befly.redis.srem("visitStats:online:visitors", expiredMembers);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return onlineCount;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function getVisitStatsUaFieldList(befly, bucketDate, field, defaultValue) {
|
|
114
|
-
const valuesKey = getVisitStatsDayUaValuesKey(bucketDate, field);
|
|
115
|
-
const values = await befly.redis.smembers(valuesKey);
|
|
116
|
-
const list = [];
|
|
117
|
-
|
|
118
|
-
for (const rawValue of values) {
|
|
119
|
-
const value = normalizeVisitStatsUaValue(rawValue, defaultValue);
|
|
120
|
-
const count = toVisitStatsNumber(await befly.redis.getString(getVisitStatsDayUaCountKey(bucketDate, field, value)));
|
|
121
|
-
if (count <= 0) {
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
list.push({
|
|
126
|
-
name: value,
|
|
127
|
-
count: count
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
list.sort((a, b) => {
|
|
132
|
-
if (b.count !== a.count) {
|
|
133
|
-
return b.count - a.count;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return String(a.name).localeCompare(String(b.name));
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
return list.slice(0, 10);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async function getVisitStatsUaStats(befly, bucketDate) {
|
|
143
|
-
const data = {};
|
|
144
|
-
|
|
145
|
-
for (const item of VISIT_STATS_UA_FIELDS) {
|
|
146
|
-
const field = item.field;
|
|
147
|
-
const defaultValue = item.defaultValue;
|
|
148
|
-
data[field] = await getVisitStatsUaFieldList(befly, bucketDate, field, defaultValue);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
deviceTypes: data.deviceType || [],
|
|
153
|
-
browsers: data.browserName || [],
|
|
154
|
-
browserVersions: data.browserVersion || [],
|
|
155
|
-
osList: data.osName || [],
|
|
156
|
-
osVersions: data.osVersion || [],
|
|
157
|
-
deviceVendors: data.deviceVendor || [],
|
|
158
|
-
deviceModels: data.deviceModel || [],
|
|
159
|
-
engines: data.engineName || [],
|
|
160
|
-
cpuArchitectures: data.cpuArchitecture || []
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function getVisitStatsRecentDayList(now, limit = VISIT_STATS_DAY_LIMIT) {
|
|
165
|
-
const list = [];
|
|
166
|
-
|
|
167
|
-
for (let i = limit - 1; i >= 0; i--) {
|
|
168
|
-
list.push(getDateYmdNumber(addDays(now, -i)));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return list;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function getVisitStatsBucketList(bucketDate, currentBucketTime) {
|
|
175
|
-
const list = [];
|
|
176
|
-
let startBucketTime = currentBucketTime;
|
|
177
|
-
|
|
178
|
-
while (getDateYmdNumber(startBucketTime - VISIT_STATS_BUCKET_MS) === bucketDate) {
|
|
179
|
-
startBucketTime -= VISIT_STATS_BUCKET_MS;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
for (let bucketTime = startBucketTime; bucketTime <= currentBucketTime; bucketTime += VISIT_STATS_BUCKET_MS) {
|
|
183
|
-
list.push(bucketTime);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return list;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async function getVisitStatsProductMeta(befly, productKey) {
|
|
190
|
-
const metaText = await befly.redis.getString(getVisitStatsProductMetaKey(productKey));
|
|
191
|
-
|
|
192
|
-
if (!metaText) {
|
|
193
|
-
return parseVisitStatsProductKey(productKey);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
const meta = JSON.parse(metaText);
|
|
198
|
-
return {
|
|
199
|
-
...parseVisitStatsProductKey(productKey),
|
|
200
|
-
...meta
|
|
201
|
-
};
|
|
202
|
-
} catch (_error) {
|
|
203
|
-
return parseVisitStatsProductKey(productKey);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
async function getVisitStatsProducts(befly, now, bucketDate, currentBucketTime) {
|
|
208
|
-
const recentDayList = getVisitStatsRecentDayList(now);
|
|
209
|
-
const productKeySet = new Set();
|
|
210
|
-
|
|
211
|
-
for (const itemBucketDate of recentDayList) {
|
|
212
|
-
const productKeys = await befly.redis.smembers(getVisitStatsDayProductKeysKey(itemBucketDate));
|
|
213
|
-
|
|
214
|
-
for (const productKey of productKeys) {
|
|
215
|
-
if (productKey) {
|
|
216
|
-
productKeySet.add(productKey);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const bucketList = getVisitStatsBucketList(bucketDate, currentBucketTime);
|
|
222
|
-
const products = [];
|
|
223
|
-
|
|
224
|
-
for (const productKey of productKeySet) {
|
|
225
|
-
const meta = await getVisitStatsProductMeta(befly, productKey);
|
|
226
|
-
const trend = [];
|
|
227
|
-
const days = [];
|
|
228
|
-
let totalPv = 0;
|
|
229
|
-
let totalUv = 0;
|
|
230
|
-
|
|
231
|
-
for (const itemBucketTime of bucketList) {
|
|
232
|
-
trend.push({
|
|
233
|
-
bucketTime: itemBucketTime,
|
|
234
|
-
bucketDate: bucketDate,
|
|
235
|
-
pv: toVisitStatsNumber(await befly.redis.getString(getVisitStatsBucketProductPvKey(itemBucketTime, productKey))),
|
|
236
|
-
uv: await befly.redis.scard(getVisitStatsBucketProductUvKey(itemBucketTime, productKey))
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
for (const itemBucketDate of recentDayList) {
|
|
241
|
-
const pv = toVisitStatsNumber(await befly.redis.getString(getVisitStatsDayProductPvKey(itemBucketDate, productKey)));
|
|
242
|
-
const uv = await befly.redis.scard(getVisitStatsDayProductUvKey(itemBucketDate, productKey));
|
|
243
|
-
|
|
244
|
-
totalPv += pv;
|
|
245
|
-
totalUv += uv;
|
|
246
|
-
days.push({
|
|
247
|
-
bucketDate: itemBucketDate,
|
|
248
|
-
pv: pv,
|
|
249
|
-
uv: uv
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
products.push({
|
|
254
|
-
key: productKey,
|
|
255
|
-
productName: String(meta.productName || "Unknown"),
|
|
256
|
-
productCode: String(meta.productCode || "unknown"),
|
|
257
|
-
productVersion: String(meta.productVersion || "Unknown"),
|
|
258
|
-
today: days[days.length - 1] || {
|
|
259
|
-
bucketDate: bucketDate,
|
|
260
|
-
pv: 0,
|
|
261
|
-
uv: 0
|
|
262
|
-
},
|
|
263
|
-
trend: trend,
|
|
264
|
-
days: days,
|
|
265
|
-
totalPv: totalPv,
|
|
266
|
-
totalUv: totalUv
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
products.sort((a, b) => {
|
|
271
|
-
if (b.today.pv !== a.today.pv) {
|
|
272
|
-
return b.today.pv - a.today.pv;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (b.totalPv !== a.totalPv) {
|
|
276
|
-
return b.totalPv - a.totalPv;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return `${a.productCode}|${a.productVersion}`.localeCompare(`${b.productCode}|${b.productVersion}`);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
return products;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export default {
|
|
286
|
-
name: "获取访问统计",
|
|
287
|
-
method: "POST",
|
|
288
|
-
body: "none",
|
|
289
|
-
auth: true,
|
|
290
|
-
fields: {},
|
|
291
|
-
required: [],
|
|
292
|
-
handler: async (befly) => {
|
|
293
|
-
const now = Date.now();
|
|
294
|
-
await flushVisitStatsPreviousBucket(befly, now);
|
|
295
|
-
const tableReady = await ensureVisitStatsTableReady(befly);
|
|
296
|
-
|
|
297
|
-
const bucketTime = getTimeBucketStart(now, VISIT_STATS_BUCKET_MS);
|
|
298
|
-
const bucketDate = getDateYmdNumber(now);
|
|
299
|
-
const bucketKey = getVisitStatsBucketKey(bucketTime);
|
|
300
|
-
const dayKey = getVisitStatsDayKey(bucketDate);
|
|
301
|
-
|
|
302
|
-
const todayPv = toVisitStatsNumber(await befly.redis.getString(`${dayKey}:pv`));
|
|
303
|
-
const todayUv = await befly.redis.scard(`${dayKey}:uv`);
|
|
304
|
-
const trend = [];
|
|
305
|
-
const currentPv = toVisitStatsNumber(await befly.redis.getString(`${bucketKey}:pv`));
|
|
306
|
-
const currentUv = await befly.redis.scard(`${bucketKey}:uv`);
|
|
307
|
-
|
|
308
|
-
if (tableReady) {
|
|
309
|
-
const trendResult = await befly.mysql.getAll({
|
|
310
|
-
table: "beflyVisitStats",
|
|
311
|
-
where: { bucketDate: bucketDate },
|
|
312
|
-
orderBy: ["bucketTime#ASC"]
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
if (Array.isArray(trendResult.data?.lists)) {
|
|
316
|
-
trend.push(...trendResult.data.lists);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (currentPv > 0 || currentUv > 0) {
|
|
321
|
-
trend.push({
|
|
322
|
-
bucketTime: bucketTime,
|
|
323
|
-
bucketDate: bucketDate,
|
|
324
|
-
pv: currentPv,
|
|
325
|
-
uv: currentUv,
|
|
326
|
-
errorCount: 0,
|
|
327
|
-
durationSum: 0,
|
|
328
|
-
durationCount: 0,
|
|
329
|
-
avgDuration: 0
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const daysMap = new Map();
|
|
334
|
-
|
|
335
|
-
if (tableReady) {
|
|
336
|
-
const daysSqlRes = await befly.mysql.execute(`SELECT bucket_date as bucketDate, SUM(pv) as pv, SUM(uv) as uv FROM befly_visit_stats WHERE state = 1 GROUP BY bucket_date ORDER BY bucket_date DESC LIMIT ${VISIT_STATS_DAY_LIMIT}`, []);
|
|
337
|
-
|
|
338
|
-
for (const item of daysSqlRes.data || []) {
|
|
339
|
-
const itemBucketDate = toVisitStatsNumber(item.bucketDate);
|
|
340
|
-
daysMap.set(itemBucketDate, {
|
|
341
|
-
bucketDate: itemBucketDate,
|
|
342
|
-
pv: toVisitStatsNumber(item.pv),
|
|
343
|
-
uv: toVisitStatsNumber(item.uv)
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
daysMap.set(bucketDate, {
|
|
349
|
-
bucketDate: bucketDate,
|
|
350
|
-
pv: todayPv,
|
|
351
|
-
uv: todayUv
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
const days = [];
|
|
355
|
-
for (const item of daysMap.values()) {
|
|
356
|
-
days.push(item);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
days.sort((a, b) => a.bucketDate - b.bucketDate);
|
|
360
|
-
if (days.length > VISIT_STATS_DAY_LIMIT) {
|
|
361
|
-
days.splice(0, days.length - VISIT_STATS_DAY_LIMIT);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const onlineCount = await getVisitStatsOnlineCount(befly);
|
|
365
|
-
const uaStats = await getVisitStatsUaStats(befly, bucketDate);
|
|
366
|
-
const products = await getVisitStatsProducts(befly, now, bucketDate, bucketTime);
|
|
367
|
-
return befly.tool.Yes("获取成功", {
|
|
368
|
-
onlineCount: onlineCount,
|
|
369
|
-
today: {
|
|
370
|
-
bucketDate: bucketDate,
|
|
371
|
-
pv: todayPv,
|
|
372
|
-
uv: todayUv
|
|
373
|
-
},
|
|
374
|
-
trend: trend,
|
|
375
|
-
days: days,
|
|
376
|
-
uaStats: uaStats,
|
|
377
|
-
products: products
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
};
|
package/utils/visitStats.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
export const VISIT_STATS_REDIS_TTL_SECONDS = 7 * 24 * 60 * 60;
|
|
2
|
-
export const VISIT_STATS_ONLINE_TTL_SECONDS = 10 * 60;
|
|
3
|
-
export const VISIT_STATS_BUCKET_MS = 30 * 60 * 1000;
|
|
4
|
-
export const VISIT_STATS_UA_FIELDS = [
|
|
5
|
-
{ field: "deviceType", defaultValue: "desktop" },
|
|
6
|
-
{ field: "browserName", defaultValue: "Unknown" },
|
|
7
|
-
{ field: "browserVersion", defaultValue: "Unknown" },
|
|
8
|
-
{ field: "osName", defaultValue: "Unknown" },
|
|
9
|
-
{ field: "osVersion", defaultValue: "Unknown" },
|
|
10
|
-
{ field: "deviceVendor", defaultValue: "Unknown" },
|
|
11
|
-
{ field: "deviceModel", defaultValue: "Unknown" },
|
|
12
|
-
{ field: "engineName", defaultValue: "Unknown" },
|
|
13
|
-
{ field: "cpuArchitecture", defaultValue: "Unknown" }
|
|
14
|
-
];
|
|
15
|
-
export const VISIT_STATS_PRODUCT_FIELDS = [
|
|
16
|
-
{ field: "productName", defaultValue: "Unknown" },
|
|
17
|
-
{ field: "productCode", defaultValue: "unknown" },
|
|
18
|
-
{ field: "productVersion", defaultValue: "Unknown" }
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
export function getVisitStatsBucketKey(bucketTime) {
|
|
22
|
-
return `visitStats:bucket:${bucketTime}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function getVisitStatsDayKey(bucketDate) {
|
|
26
|
-
return `visitStats:day:${bucketDate}`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function normalizeVisitStatsUaValue(value, defaultValue = "Unknown") {
|
|
30
|
-
const text = String(value || "").trim();
|
|
31
|
-
if (!text) {
|
|
32
|
-
return defaultValue;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return text;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function normalizeVisitStatsProductValue(value, defaultValue = "Unknown") {
|
|
39
|
-
const text = String(value || "")
|
|
40
|
-
.trim()
|
|
41
|
-
.slice(0, 100);
|
|
42
|
-
|
|
43
|
-
if (!text) {
|
|
44
|
-
return defaultValue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return text;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function getVisitStatsProductMeta(productData = {}) {
|
|
51
|
-
return {
|
|
52
|
-
productName: normalizeVisitStatsProductValue(productData.productName, "Unknown"),
|
|
53
|
-
productCode: normalizeVisitStatsProductValue(productData.productCode, "unknown"),
|
|
54
|
-
productVersion: normalizeVisitStatsProductValue(productData.productVersion, "Unknown")
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function getVisitStatsProductKey(productData = {}) {
|
|
59
|
-
const meta = getVisitStatsProductMeta(productData);
|
|
60
|
-
return [meta.productCode, meta.productVersion, meta.productName].map((item) => encodeURIComponent(item)).join("|");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function parseVisitStatsProductKey(productKey) {
|
|
64
|
-
const parts = String(productKey || "")
|
|
65
|
-
.split("|")
|
|
66
|
-
.map((item) => decodeURIComponent(item || ""));
|
|
67
|
-
|
|
68
|
-
return getVisitStatsProductMeta({
|
|
69
|
-
productCode: parts[0],
|
|
70
|
-
productVersion: parts[1],
|
|
71
|
-
productName: parts[2]
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function getVisitStatsDayUaValuesKey(bucketDate, field) {
|
|
76
|
-
return `visitStats:day:${bucketDate}:ua:${field}:values`;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function getVisitStatsDayUaCountKey(bucketDate, field, value) {
|
|
80
|
-
return `visitStats:day:${bucketDate}:ua:${field}:count:${encodeURIComponent(String(value || ""))}`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function getVisitStatsDayProductValuesKey(bucketDate, field) {
|
|
84
|
-
return `visitStats:day:${bucketDate}:product:${field}:values`;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function getVisitStatsDayProductCountKey(bucketDate, field, value) {
|
|
88
|
-
return `visitStats:day:${bucketDate}:product:${field}:count:${encodeURIComponent(String(value || ""))}`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function getVisitStatsDayProductKeysKey(bucketDate) {
|
|
92
|
-
return `visitStats:day:${bucketDate}:product:keys`;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function getVisitStatsProductMetaKey(productKey) {
|
|
96
|
-
return `visitStats:product:meta:${productKey}`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function getVisitStatsBucketProductPvKey(bucketTime, productKey) {
|
|
100
|
-
return `visitStats:bucket:${bucketTime}:product:${productKey}:pv`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function getVisitStatsBucketProductUvKey(bucketTime, productKey) {
|
|
104
|
-
return `visitStats:bucket:${bucketTime}:product:${productKey}:uv`;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function getVisitStatsDayProductPvKey(bucketDate, productKey) {
|
|
108
|
-
return `visitStats:day:${bucketDate}:product:${productKey}:pv`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function getVisitStatsDayProductUvKey(bucketDate, productKey) {
|
|
112
|
-
return `visitStats:day:${bucketDate}:product:${productKey}:uv`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function toVisitStatsNumber(value) {
|
|
116
|
-
const num = Number(value);
|
|
117
|
-
if (!Number.isFinite(num)) {
|
|
118
|
-
return 0;
|
|
119
|
-
}
|
|
120
|
-
return num;
|
|
121
|
-
}
|