befly 3.36.0 → 3.37.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,227 +1,192 @@
1
- import { getDateYmdNumber } from "#befly/utils/datetime.js";
2
-
3
- import { getTongJiMonthStartDate, getTongJiWeekStartDate } from "./_tongJi.js";
4
-
5
- const INFO_STATS_FIELDS = [
6
- { dbField: "source", key: "sources" },
7
- { dbField: "product_name", key: "productNames" },
8
- { dbField: "product_code", key: "productCodes" },
9
- { dbField: "product_version", key: "productVersions" },
10
- { dbField: "page_path", key: "pagePaths" },
11
- { dbField: "page_name", key: "pageNames" },
12
- { dbField: "device_type", key: "deviceTypes" },
13
- { dbField: "browser_name", key: "browsers" },
14
- { dbField: "browser_version", key: "browserVersions" },
15
- { dbField: "os_name", key: "osList" },
16
- { dbField: "os_version", key: "osVersions" },
17
- { dbField: "device_vendor", key: "deviceVendors" },
18
- { dbField: "device_model", key: "deviceModels" },
19
- { dbField: "engine_name", key: "engines" },
20
- { dbField: "cpu_architecture", key: "cpuArchitectures" }
21
- ];
22
- const INFO_STATS_FALLBACK_COUNT_KEY = "stats:fallback:infoStats:count";
23
- const INFO_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX = "stats:fallback:infoStats:count:day:";
24
- const INFO_STATS_FALLBACK_TTL_SECONDS = 7 * 24 * 60 * 60;
25
-
26
- function getInfoStatsFallbackDailyCountKey(reportDate) {
27
- return `${INFO_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX}${reportDate}`;
28
- }
29
-
30
- function formatInfoStatsList(rows = []) {
31
- const list = [];
1
+ import { addDays, getDateYmdNumber, getDayStartTime } from "#befly/utils/datetime.js";
32
2
 
33
- for (const item of rows) {
34
- const name = String(item.name || "").trim();
35
- const count = Number(item.count || 0);
36
-
37
- if (!name || count <= 0) {
38
- continue;
39
- }
3
+ import { getTongJiMonthStartDate, getTongJiNumber, getTongJiRecentDateList, getVisitStatsDayMembersKey, getVisitStatsDayProductMembersKey, getVisitStatsMonthMembersKey, getVisitStatsMonthProductMembersKey, getVisitStatsRecentMembersKey, getVisitStatsRecentProductMembersKey } from "./_tongJi.js";
40
4
 
41
- list.push({
42
- name: name,
43
- count: count
44
- });
45
- }
5
+ const VISIT_STATS_DAY_LIMIT = 30;
46
6
 
47
- return list;
7
+ async function getRedisSetCount(befly, key) {
8
+ return getTongJiNumber(await befly.redis.scard(key));
48
9
  }
49
10
 
50
- function getInfoStatsNamesKey(periodType, periodValue, fieldKey) {
51
- return `info:${periodType}:${periodValue}:${fieldKey}:names`;
52
- }
11
+ async function getRedisRecentCount(befly, key, now) {
12
+ const cutoffTime = getDayStartTime(addDays(now, -29));
53
13
 
54
- function getInfoStatsCountKey(periodType, periodValue, fieldKey, value) {
55
- return `info:${periodType}:${periodValue}:${fieldKey}:count:${encodeURIComponent(String(value || ""))}`;
14
+ await befly.redis.zremrangebyscore(key, "-inf", cutoffTime - 1);
15
+ return getTongJiNumber(await befly.redis.zcard(key));
56
16
  }
57
17
 
58
- function getInfoStatsReportTimeKey(periodType, periodValue) {
59
- return `info:${periodType}:${periodValue}:reportTime`;
60
- }
18
+ async function getVisitStatsDays(befly, recentDateList, productCode) {
19
+ const days = [];
61
20
 
62
- async function getInfoStatsFieldListFromRedis(befly, periodType, periodValue, fieldKey) {
63
- const values = await befly.redis.smembers(getInfoStatsNamesKey(periodType, periodValue, fieldKey));
64
- const rows = [];
65
-
66
- for (const value of values) {
67
- const name = String(value || "").trim();
68
-
69
- if (!name) {
70
- continue;
71
- }
72
-
73
- rows.push({
74
- name: name,
75
- count: Number(await befly.redis.getString(getInfoStatsCountKey(periodType, periodValue, fieldKey, name))) || 0
21
+ for (const reportDate of recentDateList) {
22
+ days.push({
23
+ reportDate: reportDate,
24
+ count: await getRedisSetCount(befly, productCode ? getVisitStatsDayProductMembersKey(reportDate, productCode) : getVisitStatsDayMembersKey(reportDate))
76
25
  });
77
26
  }
78
27
 
79
- rows.sort((a, b) => {
80
- if (b.count !== a.count) {
81
- return b.count - a.count;
82
- }
83
-
84
- return String(a.name).localeCompare(String(b.name), "zh-CN");
85
- });
86
-
87
- return formatInfoStatsList(rows.slice(0, 10));
88
- }
89
-
90
- async function getInfoStatsPeriodFromRedis(befly, periodType, periodValue) {
91
- const reportTime = Number(await befly.redis.getString(getInfoStatsReportTimeKey(periodType, periodValue))) || 0;
92
-
93
- if (reportTime <= 0) {
94
- return null;
95
- }
96
-
97
- const data = {};
98
-
99
- for (const item of INFO_STATS_FIELDS) {
100
- data[item.key] = await getInfoStatsFieldListFromRedis(befly, periodType, periodValue, item.key);
101
- }
102
-
103
- return data;
104
- }
105
-
106
- async function incrInfoStatsFallbackCount(befly, reportDate) {
107
- if (!befly.redis) {
108
- return;
109
- }
110
-
111
- const dailyKey = getInfoStatsFallbackDailyCountKey(reportDate);
112
-
113
- if (typeof befly.redis.incrWithExpire === "function") {
114
- await befly.redis.incrWithExpire(INFO_STATS_FALLBACK_COUNT_KEY, INFO_STATS_FALLBACK_TTL_SECONDS);
115
- await befly.redis.incrWithExpire(dailyKey, INFO_STATS_FALLBACK_TTL_SECONDS);
116
- return;
117
- }
118
-
119
- await befly.redis.incr(INFO_STATS_FALLBACK_COUNT_KEY);
120
- await befly.redis.expire(INFO_STATS_FALLBACK_COUNT_KEY, INFO_STATS_FALLBACK_TTL_SECONDS);
121
- await befly.redis.incr(dailyKey);
122
- await befly.redis.expire(dailyKey, INFO_STATS_FALLBACK_TTL_SECONDS);
28
+ return days;
123
29
  }
124
30
 
125
- async function getInfoStatsFieldList(befly, startDate, endDate, dbField, productName) {
126
- const queryParams = [startDate, endDate];
127
- let sql = `SELECT ${dbField} as name, COUNT(*) as count FROM befly_info_report WHERE state = 1 AND report_date BETWEEN ? AND ?`;
128
-
129
- if (productName) {
130
- sql += " AND product_name = ?";
131
- queryParams.push(productName);
132
- }
133
-
134
- sql += ` AND ${dbField} <> '' GROUP BY ${dbField} ORDER BY count DESC, name ASC LIMIT 10`;
135
-
136
- const result = await befly.mysql.execute(sql, queryParams);
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;
137
34
 
138
- return formatInfoStatsList(result.data || []);
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
+ };
139
60
  }
140
61
 
141
- async function getInfoStatsRange(befly, startDate, endDate, productName) {
142
- const data = {};
143
-
144
- for (const item of INFO_STATS_FIELDS) {
145
- data[item.key] = await getInfoStatsFieldList(befly, startDate, endDate, item.dbField, productName);
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.productCode,
76
+ productName: item.productName,
77
+ productCode: item.productCode,
78
+ productVersion: item.productVersion || "",
79
+ today: 0,
80
+ yesterday: 0,
81
+ dayBeforeYesterday: 0,
82
+ month: 0,
83
+ recent30: 0,
84
+ days: days
85
+ });
146
86
  }
147
87
 
148
- return data;
149
- }
150
-
151
- function buildInfoStatsPeriod(periodData, startDate = null, endDate = null, reportDate = null) {
152
- const result = {
153
- sources: periodData.sources,
154
- productNames: periodData.productNames,
155
- productCodes: periodData.productCodes,
156
- productVersions: periodData.productVersions,
157
- pagePaths: periodData.pagePaths,
158
- pageNames: periodData.pageNames,
159
- deviceTypes: periodData.deviceTypes,
160
- browsers: periodData.browsers,
161
- browserVersions: periodData.browserVersions,
162
- osList: periodData.osList,
163
- osVersions: periodData.osVersions,
164
- deviceVendors: periodData.deviceVendors,
165
- deviceModels: periodData.deviceModels,
166
- engines: periodData.engines,
167
- cpuArchitectures: periodData.cpuArchitectures
88
+ return {
89
+ today: {
90
+ reportDate: reportDate,
91
+ count: 0
92
+ },
93
+ yesterday: {
94
+ reportDate: yesterdayDate,
95
+ count: 0
96
+ },
97
+ dayBeforeYesterday: {
98
+ reportDate: dayBeforeYesterdayDate,
99
+ count: 0
100
+ },
101
+ month: {
102
+ startDate: monthStartDate,
103
+ endDate: reportDate,
104
+ count: 0
105
+ },
106
+ recent30: {
107
+ startDate: recentDateList[0],
108
+ endDate: reportDate,
109
+ count: 0
110
+ },
111
+ days: days,
112
+ products: products
168
113
  };
114
+ }
169
115
 
170
- if (reportDate !== null) {
171
- result.reportDate = reportDate;
172
- }
173
-
174
- if (startDate !== null && endDate !== null) {
175
- result.startDate = startDate;
176
- result.endDate = endDate;
116
+ async function buildVisitStatsProducts(befly, now, reportDate, monthStartDate, recentDateList, projectLists) {
117
+ const products = [];
118
+
119
+ for (const item of projectLists) {
120
+ const summary = await getVisitStatsSummary(befly, now, reportDate, monthStartDate, recentDateList, item.productCode);
121
+
122
+ products.push({
123
+ key: item.productCode,
124
+ productName: item.productName,
125
+ productCode: item.productCode,
126
+ productVersion: item.productVersion || "",
127
+ today: summary.today.count,
128
+ yesterday: summary.yesterday.count,
129
+ dayBeforeYesterday: summary.dayBeforeYesterday.count,
130
+ month: summary.month.count,
131
+ recent30: summary.recent30.count,
132
+ days: summary.days
133
+ });
177
134
  }
178
135
 
179
- return result;
180
- }
136
+ return products.toSorted((a, b) => {
137
+ if (b.recent30 !== a.recent30) {
138
+ return b.recent30 - a.recent30;
139
+ }
181
140
 
182
- function buildInfoStatsResult(today, week, month, reportDate, weekStartDate, monthStartDate) {
183
- return {
184
- today: buildInfoStatsPeriod(today, null, null, reportDate),
185
- week: buildInfoStatsPeriod(week, weekStartDate, reportDate, null),
186
- month: buildInfoStatsPeriod(month, monthStartDate, reportDate, null)
187
- };
141
+ return String(a.productName).localeCompare(String(b.productName), "zh-CN");
142
+ });
188
143
  }
189
144
 
190
145
  export default {
191
- name: "获取访问信息统计",
146
+ name: "获取独立访客统计",
192
147
  method: "POST",
193
148
  body: "none",
194
149
  auth: true,
195
150
  fields: {
196
- productName: { name: "产品名称", input: "string", min: 0, max: 100 }
151
+ productCode: { name: "产品代号", input: "string", min: 0, max: 100 }
197
152
  },
198
153
  required: [],
199
154
  handler: async (befly, ctx) => {
200
155
  const now = Date.now();
201
156
  const reportDate = getDateYmdNumber(now);
202
- const weekStartDate = getTongJiWeekStartDate(now);
203
157
  const monthStartDate = getTongJiMonthStartDate(now);
204
- const productName = ctx?.body?.productName ?? "";
205
- const hasProductFilter = productName.length > 0;
206
-
207
- if (befly.redis && !hasProductFilter) {
208
- const today = await getInfoStatsPeriodFromRedis(befly, "day", reportDate);
209
- const week = await getInfoStatsPeriodFromRedis(befly, "week", weekStartDate);
210
- const month = await getInfoStatsPeriodFromRedis(befly, "month", monthStartDate);
211
-
212
- if (today && week && month) {
213
- return befly.tool.Yes("获取成功", buildInfoStatsResult(today, week, month, reportDate, weekStartDate, monthStartDate));
214
- }
158
+ const recentDateList = getTongJiRecentDateList(now, VISIT_STATS_DAY_LIMIT);
159
+ const productCode = ctx?.body?.productCode ?? "";
160
+ const hasProductFilter = productCode.length > 0;
161
+ const projectLists = hasProductFilter ? [] : befly.config?.projectLists || [];
162
+
163
+ if (!befly.redis) {
164
+ const emptyStats = buildEmptyVisitStats(reportDate, monthStartDate, recentDateList, projectLists);
165
+
166
+ return befly.tool.Yes("获取成功", {
167
+ queryTime: now,
168
+ today: emptyStats.today,
169
+ yesterday: emptyStats.yesterday,
170
+ dayBeforeYesterday: emptyStats.dayBeforeYesterday,
171
+ month: emptyStats.month,
172
+ recent30: emptyStats.recent30,
173
+ days: emptyStats.days,
174
+ products: emptyStats.products
175
+ });
215
176
  }
216
177
 
217
- if (befly.redis && !hasProductFilter) {
218
- await incrInfoStatsFallbackCount(befly, reportDate);
219
- }
220
-
221
- const today = await getInfoStatsRange(befly, reportDate, reportDate, productName);
222
- const week = await getInfoStatsRange(befly, weekStartDate, reportDate, productName);
223
- const month = await getInfoStatsRange(befly, monthStartDate, reportDate, productName);
224
-
225
- return befly.tool.Yes("获取成功", buildInfoStatsResult(today, week, month, reportDate, weekStartDate, monthStartDate));
178
+ const summary = await getVisitStatsSummary(befly, now, reportDate, monthStartDate, recentDateList, productCode);
179
+ const products = await buildVisitStatsProducts(befly, now, reportDate, monthStartDate, recentDateList, projectLists);
180
+
181
+ return befly.tool.Yes("获取成功", {
182
+ queryTime: now,
183
+ today: summary.today,
184
+ yesterday: summary.yesterday,
185
+ dayBeforeYesterday: summary.dayBeforeYesterday,
186
+ month: summary.month,
187
+ recent30: summary.recent30,
188
+ days: summary.days,
189
+ products: products
190
+ });
226
191
  }
227
192
  };
@@ -1,42 +1,41 @@
1
1
  import { getDateYmdNumber } from "#befly/utils/datetime.js";
2
2
  import { isValidPositiveInt } from "#befly/utils/is.js";
3
3
 
4
- import { expireTongJiRedisKeys, getTongJiMonthStartDate, getTongJiWeekStartDate } from "./_tongJi.js";
4
+ import { getOnlineStatsActiveProductKey } from "./_tongJi.js";
5
5
 
6
6
  const ONLINE_STATS_ONLINE_TTL_SECONDS = 10 * 60;
7
7
  const ONLINE_STATS_REDIS_TTL_SECONDS = 7 * 24 * 60 * 60;
8
- const ONLINE_STATS_TEMP_TTL_SECONDS = 45 * 24 * 60 * 60;
9
8
  const ONLINE_STATS_ACTIVE_KEY = "online:active";
10
9
 
11
10
  function getOnlineStatsMember(ctx) {
12
- if (isValidPositiveInt(ctx.userId)) {
11
+ if (isValidPositiveInt(ctx?.userId)) {
13
12
  return `user:${ctx.userId}`;
14
13
  }
15
14
 
16
- const clientId = ctx.body.clientId ?? "";
15
+ const clientId = ctx?.body?.clientId ?? "";
17
16
 
18
17
  if (clientId) {
19
18
  return `client:${clientId}`;
20
19
  }
21
20
 
22
- return `ip:${ctx.ip || "unknown"}`;
21
+ return `ip:${ctx?.ip || "unknown"}`;
23
22
  }
24
23
 
25
- async function updateOnlineStatsActiveMemberProduct(befly, member, productName, expireAt) {
26
- if (!productName) {
24
+ async function updateOnlineStatsActiveMemberProduct(befly, member, productCode, expireAt) {
25
+ if (!productCode) {
27
26
  return;
28
27
  }
29
28
 
30
29
  const memberProductKey = `online:active:member:${encodeURIComponent(member)}:product`;
31
- const previousProductName = String((await befly.redis.getString(memberProductKey)) || "").trim();
30
+ const previousProductCode = String((await befly.redis.getString(memberProductKey)) || "").trim();
32
31
 
33
- if (previousProductName && previousProductName !== productName) {
34
- await befly.redis.zrem(`online:active:product:${encodeURIComponent(previousProductName)}`, [member]);
32
+ if (previousProductCode && previousProductCode !== productCode) {
33
+ await befly.redis.zrem(getOnlineStatsActiveProductKey(previousProductCode), [member]);
35
34
  }
36
35
 
37
- const activeProductKey = `online:active:product:${encodeURIComponent(productName)}`;
36
+ const activeProductKey = getOnlineStatsActiveProductKey(productCode);
38
37
 
39
- await befly.redis.setString(memberProductKey, productName, ONLINE_STATS_ONLINE_TTL_SECONDS);
38
+ await befly.redis.setString(memberProductKey, productCode, ONLINE_STATS_ONLINE_TTL_SECONDS);
40
39
  await befly.redis.zadd(activeProductKey, [
41
40
  {
42
41
  score: expireAt,
@@ -46,7 +45,7 @@ async function updateOnlineStatsActiveMemberProduct(befly, member, productName,
46
45
  await befly.redis.expire(activeProductKey, ONLINE_STATS_REDIS_TTL_SECONDS);
47
46
  }
48
47
 
49
- async function updateOnlineStatsActiveMember(befly, member, now, productName) {
48
+ async function updateOnlineStatsActiveMember(befly, member, now, productCode) {
50
49
  const expireAt = now + ONLINE_STATS_ONLINE_TTL_SECONDS * 1000;
51
50
 
52
51
  await befly.redis.zadd(ONLINE_STATS_ACTIVE_KEY, [
@@ -55,43 +54,10 @@ async function updateOnlineStatsActiveMember(befly, member, now, productName) {
55
54
  member: member
56
55
  }
57
56
  ]);
58
- await updateOnlineStatsActiveMemberProduct(befly, member, productName, expireAt);
57
+ await updateOnlineStatsActiveMemberProduct(befly, member, productCode, expireAt);
59
58
  await befly.redis.expire(ONLINE_STATS_ACTIVE_KEY, ONLINE_STATS_REDIS_TTL_SECONDS);
60
59
  }
61
60
 
62
- async function updateOnlineStatsPeriod(befly, periodType, periodValue, member) {
63
- const keys = {
64
- pv: `online:${periodType}:${periodValue}:pv`,
65
- members: `online:${periodType}:${periodValue}:members`,
66
- reportTime: `online:${periodType}:${periodValue}:reportTime`
67
- };
68
-
69
- await befly.redis.incr(keys.pv);
70
- await befly.redis.sadd(keys.members, [member]);
71
- await befly.redis.setString(keys.reportTime, String(Date.now()), ONLINE_STATS_TEMP_TTL_SECONDS);
72
- await expireTongJiRedisKeys(befly, Object.values(keys), ONLINE_STATS_TEMP_TTL_SECONDS);
73
- }
74
-
75
- async function updateOnlineStatsProductPeriod(befly, periodType, periodValue, member, productName) {
76
- if (!productName) {
77
- return;
78
- }
79
-
80
- const productKey = encodeURIComponent(productName);
81
- const keys = {
82
- pv: `online:${periodType}:${periodValue}:product:${productKey}:pv`,
83
- members: `online:${periodType}:${periodValue}:product:${productKey}:members`,
84
- reportTime: `online:${periodType}:${periodValue}:product:${productKey}:reportTime`
85
- };
86
- const productsKey = `online:${periodType}:${periodValue}:products`;
87
-
88
- await befly.redis.incr(keys.pv);
89
- await befly.redis.sadd(keys.members, [member]);
90
- await befly.redis.setString(keys.reportTime, String(Date.now()), ONLINE_STATS_TEMP_TTL_SECONDS);
91
- await befly.redis.sadd(productsKey, [productName]);
92
- await expireTongJiRedisKeys(befly, [...Object.values(keys), productsKey], ONLINE_STATS_TEMP_TTL_SECONDS);
93
- }
94
-
95
61
  export default {
96
62
  name: "上报在线统计",
97
63
  method: "POST",
@@ -110,23 +76,22 @@ export default {
110
76
  handler: async (befly, ctx) => {
111
77
  const now = Date.now();
112
78
  const reportDate = getDateYmdNumber(now);
113
- const weekStartDate = getTongJiWeekStartDate(now);
114
- const monthStartDate = getTongJiMonthStartDate(now);
115
- const member = getOnlineStatsMember(ctx);
116
- const productName = ctx.body.productName ?? "";
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
+ }
117
89
 
118
- await updateOnlineStatsActiveMember(befly, member, now, productName);
90
+ await updateOnlineStatsActiveMember(befly, getOnlineStatsMember(ctx), now, productCode);
119
91
 
120
92
  await befly.redis.zremrangebyscore(ONLINE_STATS_ACTIVE_KEY, "-inf", now);
121
93
  const onlineCount = await befly.redis.zcard(ONLINE_STATS_ACTIVE_KEY);
122
94
 
123
- await updateOnlineStatsPeriod(befly, "day", reportDate, member);
124
- await updateOnlineStatsPeriod(befly, "week", weekStartDate, member);
125
- await updateOnlineStatsPeriod(befly, "month", monthStartDate, member);
126
- await updateOnlineStatsProductPeriod(befly, "day", reportDate, member, productName);
127
- await updateOnlineStatsProductPeriod(befly, "week", weekStartDate, member, productName);
128
- await updateOnlineStatsProductPeriod(befly, "month", monthStartDate, member, productName);
129
-
130
95
  return befly.tool.Yes("上报成功", {
131
96
  reportTime: now,
132
97
  reportDate: reportDate,