befly 3.56.0 → 3.58.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,190 +0,0 @@
1
- import { addDays, getDateYmdNumber, getDayStartTime } from "#befly/utils/datetime.js";
2
-
3
- import { getTongJiMonthStartDate, getTongJiNumber, getTongJiRecentDateList, getVisitStatsDayMembersKey, getVisitStatsDayProductMembersKey, getVisitStatsMonthMembersKey, getVisitStatsMonthProductMembersKey, getVisitStatsRecentMembersKey, getVisitStatsRecentProductMembersKey } from "./_tongJi.js";
4
-
5
- const VISIT_STATS_DAY_LIMIT = 30;
6
-
7
- async function getRedisSetCount(befly, key) {
8
- return getTongJiNumber(await befly.redis.scard(key));
9
- }
10
-
11
- async function getRedisRecentCount(befly, key, now) {
12
- const cutoffTime = getDayStartTime(addDays(now, -29));
13
-
14
- await befly.redis.zremrangebyscore(key, "-inf", cutoffTime - 1);
15
- return getTongJiNumber(await befly.redis.zcard(key));
16
- }
17
-
18
- async function getVisitStatsDays(befly, recentDateList, productCode) {
19
- const days = [];
20
-
21
- for (const reportDate of recentDateList) {
22
- days.push({
23
- reportDate: reportDate,
24
- count: await getRedisSetCount(befly, productCode ? getVisitStatsDayProductMembersKey(reportDate, productCode) : getVisitStatsDayMembersKey(reportDate))
25
- });
26
- }
27
-
28
- return days;
29
- }
30
-
31
- async function getVisitStatsSummary(befly, now, reportDate, monthStartDate, recentDateList, productCode) {
32
- const yesterdayDate = recentDateList[recentDateList.length - 2] || reportDate;
33
- const dayBeforeYesterdayDate = recentDateList[recentDateList.length - 3] || yesterdayDate;
34
-
35
- return {
36
- today: {
37
- reportDate: reportDate,
38
- count: await getRedisSetCount(befly, productCode ? getVisitStatsDayProductMembersKey(reportDate, productCode) : getVisitStatsDayMembersKey(reportDate))
39
- },
40
- yesterday: {
41
- reportDate: yesterdayDate,
42
- count: await getRedisSetCount(befly, productCode ? getVisitStatsDayProductMembersKey(yesterdayDate, productCode) : getVisitStatsDayMembersKey(yesterdayDate))
43
- },
44
- dayBeforeYesterday: {
45
- reportDate: dayBeforeYesterdayDate,
46
- count: await getRedisSetCount(befly, productCode ? getVisitStatsDayProductMembersKey(dayBeforeYesterdayDate, productCode) : getVisitStatsDayMembersKey(dayBeforeYesterdayDate))
47
- },
48
- month: {
49
- startDate: monthStartDate,
50
- endDate: reportDate,
51
- count: await getRedisSetCount(befly, productCode ? getVisitStatsMonthProductMembersKey(monthStartDate, productCode) : getVisitStatsMonthMembersKey(monthStartDate))
52
- },
53
- recent30: {
54
- startDate: recentDateList[0],
55
- endDate: reportDate,
56
- count: await getRedisRecentCount(befly, productCode ? getVisitStatsRecentProductMembersKey(productCode) : getVisitStatsRecentMembersKey(), now)
57
- },
58
- days: await getVisitStatsDays(befly, recentDateList, productCode)
59
- };
60
- }
61
-
62
- function buildEmptyVisitStats(reportDate, monthStartDate, recentDateList, projectLists) {
63
- const yesterdayDate = recentDateList[recentDateList.length - 2] || reportDate;
64
- const dayBeforeYesterdayDate = recentDateList[recentDateList.length - 3] || yesterdayDate;
65
- const days = recentDateList.map((item) => {
66
- return {
67
- reportDate: item,
68
- count: 0
69
- };
70
- });
71
- const products = [];
72
-
73
- for (const item of projectLists) {
74
- products.push({
75
- key: item.code,
76
- productName: item.name,
77
- productCode: item.code,
78
- today: 0,
79
- yesterday: 0,
80
- dayBeforeYesterday: 0,
81
- month: 0,
82
- recent30: 0,
83
- days: days
84
- });
85
- }
86
-
87
- return {
88
- today: {
89
- reportDate: reportDate,
90
- count: 0
91
- },
92
- yesterday: {
93
- reportDate: yesterdayDate,
94
- count: 0
95
- },
96
- dayBeforeYesterday: {
97
- reportDate: dayBeforeYesterdayDate,
98
- count: 0
99
- },
100
- month: {
101
- startDate: monthStartDate,
102
- endDate: reportDate,
103
- count: 0
104
- },
105
- recent30: {
106
- startDate: recentDateList[0],
107
- endDate: reportDate,
108
- count: 0
109
- },
110
- days: days,
111
- products: products
112
- };
113
- }
114
-
115
- async function buildVisitStatsProducts(befly, now, reportDate, monthStartDate, recentDateList, projectLists) {
116
- const products = [];
117
-
118
- for (const item of projectLists) {
119
- const summary = await getVisitStatsSummary(befly, now, reportDate, monthStartDate, recentDateList, item.code);
120
-
121
- products.push({
122
- key: item.code,
123
- productName: item.name,
124
- productCode: item.code,
125
- today: summary.today.count,
126
- yesterday: summary.yesterday.count,
127
- dayBeforeYesterday: summary.dayBeforeYesterday.count,
128
- month: summary.month.count,
129
- recent30: summary.recent30.count,
130
- days: summary.days
131
- });
132
- }
133
-
134
- return products.toSorted((a, b) => {
135
- if (b.recent30 !== a.recent30) {
136
- return b.recent30 - a.recent30;
137
- }
138
-
139
- return String(a.productName).localeCompare(String(b.productName), "zh-CN");
140
- });
141
- }
142
-
143
- export default {
144
- name: "获取独立访客统计",
145
- method: "POST",
146
- body: "none",
147
- auth: true,
148
- fields: {
149
- productCode: { name: "产品代号", input: "string", min: 0, max: 100 }
150
- },
151
- required: [],
152
- handler: async (befly, ctx) => {
153
- const now = Date.now();
154
- const reportDate = getDateYmdNumber(now);
155
- const monthStartDate = getTongJiMonthStartDate(now);
156
- const recentDateList = getTongJiRecentDateList(now, VISIT_STATS_DAY_LIMIT);
157
- const productCode = ctx?.body?.productCode ?? "";
158
- const hasProductFilter = productCode.length > 0;
159
- const projectLists = hasProductFilter ? [] : befly.config?.projectLists || [];
160
-
161
- if (!befly.redis) {
162
- const emptyStats = buildEmptyVisitStats(reportDate, monthStartDate, recentDateList, projectLists);
163
-
164
- return befly.tool.Yes("获取成功", {
165
- queryTime: now,
166
- today: emptyStats.today,
167
- yesterday: emptyStats.yesterday,
168
- dayBeforeYesterday: emptyStats.dayBeforeYesterday,
169
- month: emptyStats.month,
170
- recent30: emptyStats.recent30,
171
- days: emptyStats.days,
172
- products: emptyStats.products
173
- });
174
- }
175
-
176
- const summary = await getVisitStatsSummary(befly, now, reportDate, monthStartDate, recentDateList, productCode);
177
- const products = await buildVisitStatsProducts(befly, now, reportDate, monthStartDate, recentDateList, projectLists);
178
-
179
- return befly.tool.Yes("获取成功", {
180
- queryTime: now,
181
- today: summary.today,
182
- yesterday: summary.yesterday,
183
- dayBeforeYesterday: summary.dayBeforeYesterday,
184
- month: summary.month,
185
- recent30: summary.recent30,
186
- days: summary.days,
187
- products: products
188
- });
189
- }
190
- };
@@ -1,102 +0,0 @@
1
- import { getDateYmdNumber } from "#befly/utils/datetime.js";
2
- import { isValidPositiveInt } from "#befly/utils/is.js";
3
-
4
- import { getOnlineStatsActiveProductKey } from "./_tongJi.js";
5
-
6
- const ONLINE_STATS_ONLINE_TTL_SECONDS = 10 * 60;
7
- const ONLINE_STATS_REDIS_TTL_SECONDS = 7 * 24 * 60 * 60;
8
- const ONLINE_STATS_ACTIVE_KEY = "online:active";
9
-
10
- function getOnlineStatsMember(ctx) {
11
- if (isValidPositiveInt(ctx?.userId)) {
12
- return `user:${ctx.userId}`;
13
- }
14
-
15
- const clientId = ctx?.body?.clientId ?? "";
16
-
17
- if (clientId) {
18
- return `client:${clientId}`;
19
- }
20
-
21
- return `ip:${ctx?.ip || "unknown"}`;
22
- }
23
-
24
- async function updateOnlineStatsActiveMemberProduct(befly, member, productCode, expireAt) {
25
- if (!productCode) {
26
- return;
27
- }
28
-
29
- const memberProductKey = `online:active:member:${encodeURIComponent(member)}:product`;
30
- const previousProductCode = String((await befly.redis.getString(memberProductKey)) || "").trim();
31
-
32
- if (previousProductCode && previousProductCode !== productCode) {
33
- await befly.redis.zrem(getOnlineStatsActiveProductKey(previousProductCode), [member]);
34
- }
35
-
36
- const activeProductKey = getOnlineStatsActiveProductKey(productCode);
37
-
38
- await befly.redis.setString(memberProductKey, productCode, ONLINE_STATS_ONLINE_TTL_SECONDS);
39
- await befly.redis.zadd(activeProductKey, [
40
- {
41
- score: expireAt,
42
- member: member
43
- }
44
- ]);
45
- await befly.redis.expire(activeProductKey, ONLINE_STATS_REDIS_TTL_SECONDS);
46
- }
47
-
48
- async function updateOnlineStatsActiveMember(befly, member, now, productCode) {
49
- const expireAt = now + ONLINE_STATS_ONLINE_TTL_SECONDS * 1000;
50
-
51
- await befly.redis.zadd(ONLINE_STATS_ACTIVE_KEY, [
52
- {
53
- score: expireAt,
54
- member: member
55
- }
56
- ]);
57
- await updateOnlineStatsActiveMemberProduct(befly, member, productCode, expireAt);
58
- await befly.redis.expire(ONLINE_STATS_ACTIVE_KEY, ONLINE_STATS_REDIS_TTL_SECONDS);
59
- }
60
-
61
- export default {
62
- name: "上报在线统计",
63
- method: "POST",
64
- body: "none",
65
- auth: false,
66
- fields: {
67
- pagePath: { name: "页面路径", input: "string", min: 0, max: 200 },
68
- pageName: { name: "页面名称", input: "string", min: 0, max: 100 },
69
- source: { name: "来源", input: "string", min: 0, max: 50 },
70
- clientId: { name: "客户端标识", input: "string", min: 0, max: 120 },
71
- productName: { name: "产品名称", input: "string", min: 0, max: 100 },
72
- productCode: { name: "产品代号", input: "string", min: 0, max: 100 },
73
- productVersion: { name: "产品版本", input: "string", min: 0, max: 50 }
74
- },
75
- required: [],
76
- handler: async (befly, ctx) => {
77
- const now = Date.now();
78
- const reportDate = getDateYmdNumber(now);
79
- const productCode = ctx?.body?.productCode ?? "";
80
-
81
- if (!befly.redis) {
82
- return befly.tool.Yes("上报成功", {
83
- reportTime: now,
84
- reportDate: reportDate,
85
- onlineCount: 0,
86
- stored: false
87
- });
88
- }
89
-
90
- await updateOnlineStatsActiveMember(befly, getOnlineStatsMember(ctx), now, productCode);
91
-
92
- await befly.redis.zremrangebyscore(ONLINE_STATS_ACTIVE_KEY, "-inf", now);
93
- const onlineCount = await befly.redis.zcard(ONLINE_STATS_ACTIVE_KEY);
94
-
95
- return befly.tool.Yes("上报成功", {
96
- reportTime: now,
97
- reportDate: reportDate,
98
- onlineCount: onlineCount,
99
- stored: true
100
- });
101
- }
102
- };
@@ -1,85 +0,0 @@
1
- import { getOnlineStatsActiveProductKey } from "./_tongJi.js";
2
-
3
- const ONLINE_STATS_ACTIVE_KEY = "online:active";
4
-
5
- async function getOnlineStatsTotalOnlineCount(befly, now) {
6
- await befly.redis.zremrangebyscore(ONLINE_STATS_ACTIVE_KEY, "-inf", now);
7
- return await befly.redis.zcard(ONLINE_STATS_ACTIVE_KEY);
8
- }
9
-
10
- async function getOnlineStatsProductOnlineCount(befly, productCode, now) {
11
- const productKey = getOnlineStatsActiveProductKey(productCode);
12
-
13
- await befly.redis.zremrangebyscore(productKey, "-inf", now);
14
- return await befly.redis.zcard(productKey);
15
- }
16
-
17
- async function buildOnlineStatsProducts(befly, projectLists, now) {
18
- const products = [];
19
-
20
- for (const item of projectLists) {
21
- products.push({
22
- key: item.code,
23
- productName: item.name,
24
- productCode: item.code,
25
- onlineCount: await getOnlineStatsProductOnlineCount(befly, item.code, now)
26
- });
27
- }
28
-
29
- return products.toSorted((a, b) => {
30
- if (b.onlineCount !== a.onlineCount) {
31
- return b.onlineCount - a.onlineCount;
32
- }
33
-
34
- return String(a.productName).localeCompare(String(b.productName), "zh-CN");
35
- });
36
- }
37
-
38
- function buildEmptyOnlineStatsProducts(projectLists) {
39
- const products = [];
40
-
41
- for (const item of projectLists) {
42
- products.push({
43
- key: item.code,
44
- productName: item.name,
45
- productCode: item.code,
46
- onlineCount: 0
47
- });
48
- }
49
-
50
- return products;
51
- }
52
-
53
- export default {
54
- name: "获取在线统计",
55
- method: "POST",
56
- body: "none",
57
- auth: true,
58
- fields: {
59
- productCode: { name: "产品代号", input: "string", min: 0, max: 100 }
60
- },
61
- required: [],
62
- handler: async (befly, ctx) => {
63
- const now = Date.now();
64
- const productCode = ctx?.body?.productCode ?? "";
65
- const hasProductFilter = productCode.length > 0;
66
- const projectLists = hasProductFilter ? [] : befly.config?.projectLists || [];
67
-
68
- if (!befly.redis) {
69
- return befly.tool.Yes("获取成功", {
70
- queryTime: now,
71
- onlineCount: 0,
72
- products: buildEmptyOnlineStatsProducts(projectLists)
73
- });
74
- }
75
-
76
- const onlineCount = hasProductFilter ? await getOnlineStatsProductOnlineCount(befly, productCode, now) : await getOnlineStatsTotalOnlineCount(befly, now);
77
- const products = await buildOnlineStatsProducts(befly, projectLists, now);
78
-
79
- return befly.tool.Yes("获取成功", {
80
- queryTime: now,
81
- onlineCount: onlineCount,
82
- products: products
83
- });
84
- }
85
- };