befly 3.24.17 → 3.24.19

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.
@@ -17,6 +17,13 @@ const INFO_STATS_FIELDS = [
17
17
  { dbField: "engine_name", key: "engines" },
18
18
  { dbField: "cpu_architecture", key: "cpuArchitectures" }
19
19
  ];
20
+ const INFO_STATS_FALLBACK_COUNT_KEY = "stats:fallback:infoStats:count";
21
+ const INFO_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX = "stats:fallback:infoStats:count:day:";
22
+ const INFO_STATS_FALLBACK_TTL_SECONDS = 7 * 24 * 60 * 60;
23
+
24
+ function getInfoStatsFallbackDailyCountKey(reportDate) {
25
+ return `${INFO_STATS_FALLBACK_DAILY_COUNT_KEY_PREFIX}${reportDate}`;
26
+ }
20
27
 
21
28
  function getInfoStatsWeekStartDate(timestamp = Date.now()) {
22
29
  const date = Reflect.construct(Date, [timestamp]);
@@ -35,7 +42,7 @@ function getInfoStatsMonthStartDate(timestamp = Date.now()) {
35
42
  function formatInfoStatsList(rows = []) {
36
43
  const list = [];
37
44
 
38
- for (const item of rows || []) {
45
+ for (const item of rows) {
39
46
  const name = String(item.name || "").trim();
40
47
  const count = Number(item.count || 0);
41
48
 
@@ -52,96 +59,181 @@ function formatInfoStatsList(rows = []) {
52
59
  return list;
53
60
  }
54
61
 
55
- async function getInfoStatsFieldList(befly, startDate, endDate, dbField) {
56
- const result = await befly.mysql.execute(`SELECT ${dbField} as name, COUNT(*) as count FROM befly_info_report WHERE state = 1 AND report_date BETWEEN ? AND ? AND ${dbField} <> '' GROUP BY ${dbField} ORDER BY count DESC, name ASC LIMIT 10`, [startDate, endDate]);
62
+ function getInfoStatsNamesKey(periodType, periodValue, fieldKey) {
63
+ return `info:${periodType}:${periodValue}:${fieldKey}:names`;
64
+ }
65
+
66
+ function getInfoStatsCountKey(periodType, periodValue, fieldKey, value) {
67
+ return `info:${periodType}:${periodValue}:${fieldKey}:count:${encodeURIComponent(String(value || ""))}`;
68
+ }
69
+
70
+ function getInfoStatsReportTimeKey(periodType, periodValue) {
71
+ return `info:${periodType}:${periodValue}:reportTime`;
72
+ }
73
+
74
+ async function getInfoStatsFieldListFromRedis(befly, periodType, periodValue, fieldKey) {
75
+ const values = await befly.redis.smembers(getInfoStatsNamesKey(periodType, periodValue, fieldKey));
76
+ const rows = [];
77
+
78
+ for (const value of values) {
79
+ const name = String(value || "").trim();
80
+
81
+ if (!name) {
82
+ continue;
83
+ }
84
+
85
+ rows.push({
86
+ name: name,
87
+ count: Number(await befly.redis.getString(getInfoStatsCountKey(periodType, periodValue, fieldKey, name))) || 0
88
+ });
89
+ }
90
+
91
+ rows.sort((a, b) => {
92
+ if (b.count !== a.count) {
93
+ return b.count - a.count;
94
+ }
95
+
96
+ return String(a.name).localeCompare(String(b.name), "zh-CN");
97
+ });
98
+
99
+ return formatInfoStatsList(rows.slice(0, 10));
100
+ }
101
+
102
+ async function getInfoStatsPeriodFromRedis(befly, periodType, periodValue) {
103
+ const reportTime = Number(await befly.redis.getString(getInfoStatsReportTimeKey(periodType, periodValue))) || 0;
104
+
105
+ if (reportTime <= 0) {
106
+ return null;
107
+ }
108
+
109
+ const data = {};
110
+
111
+ for (const item of INFO_STATS_FIELDS) {
112
+ data[item.key] = await getInfoStatsFieldListFromRedis(befly, periodType, periodValue, item.key);
113
+ }
114
+
115
+ return data;
116
+ }
117
+
118
+ async function incrInfoStatsFallbackCount(befly, reportDate) {
119
+ if (!befly.redis) {
120
+ return;
121
+ }
122
+
123
+ const dailyKey = getInfoStatsFallbackDailyCountKey(reportDate);
124
+
125
+ if (typeof befly.redis.incrWithExpire === "function") {
126
+ await befly.redis.incrWithExpire(INFO_STATS_FALLBACK_COUNT_KEY, INFO_STATS_FALLBACK_TTL_SECONDS);
127
+ await befly.redis.incrWithExpire(dailyKey, INFO_STATS_FALLBACK_TTL_SECONDS);
128
+ return;
129
+ }
130
+
131
+ await befly.redis.incr(INFO_STATS_FALLBACK_COUNT_KEY);
132
+ await befly.redis.expire(INFO_STATS_FALLBACK_COUNT_KEY, INFO_STATS_FALLBACK_TTL_SECONDS);
133
+ await befly.redis.incr(dailyKey);
134
+ await befly.redis.expire(dailyKey, INFO_STATS_FALLBACK_TTL_SECONDS);
135
+ }
136
+
137
+ async function getInfoStatsFieldList(befly, startDate, endDate, dbField, productName) {
138
+ const queryParams = [startDate, endDate];
139
+ let sql = `SELECT ${dbField} as name, COUNT(*) as count FROM befly_info_report WHERE state = 1 AND report_date BETWEEN ? AND ?`;
140
+
141
+ if (productName) {
142
+ sql += " AND product_name = ?";
143
+ queryParams.push(productName);
144
+ }
145
+
146
+ sql += ` AND ${dbField} <> '' GROUP BY ${dbField} ORDER BY count DESC, name ASC LIMIT 10`;
147
+
148
+ const result = await befly.mysql.execute(sql, queryParams);
57
149
 
58
150
  return formatInfoStatsList(result.data || []);
59
151
  }
60
152
 
61
- async function getInfoStatsRange(befly, startDate, endDate) {
153
+ async function getInfoStatsRange(befly, startDate, endDate, productName) {
62
154
  const data = {};
63
155
 
64
156
  for (const item of INFO_STATS_FIELDS) {
65
- data[item.key] = await getInfoStatsFieldList(befly, startDate, endDate, item.dbField);
157
+ data[item.key] = await getInfoStatsFieldList(befly, startDate, endDate, item.dbField, productName);
66
158
  }
67
159
 
68
160
  return data;
69
161
  }
70
162
 
163
+ function buildInfoStatsPeriod(periodData, startDate = null, endDate = null, reportDate = null) {
164
+ const result = {
165
+ sources: periodData.sources,
166
+ productNames: periodData.productNames,
167
+ productCodes: periodData.productCodes,
168
+ productVersions: periodData.productVersions,
169
+ pagePaths: periodData.pagePaths,
170
+ pageNames: periodData.pageNames,
171
+ deviceTypes: periodData.deviceTypes,
172
+ browsers: periodData.browsers,
173
+ browserVersions: periodData.browserVersions,
174
+ osList: periodData.osList,
175
+ osVersions: periodData.osVersions,
176
+ deviceVendors: periodData.deviceVendors,
177
+ deviceModels: periodData.deviceModels,
178
+ engines: periodData.engines,
179
+ cpuArchitectures: periodData.cpuArchitectures
180
+ };
181
+
182
+ if (reportDate !== null) {
183
+ result.reportDate = reportDate;
184
+ }
185
+
186
+ if (startDate !== null && endDate !== null) {
187
+ result.startDate = startDate;
188
+ result.endDate = endDate;
189
+ }
190
+
191
+ return result;
192
+ }
193
+
194
+ function buildInfoStatsResult(today, week, month, reportDate, weekStartDate, monthStartDate) {
195
+ return {
196
+ today: buildInfoStatsPeriod(today, null, null, reportDate),
197
+ week: buildInfoStatsPeriod(week, weekStartDate, reportDate, null),
198
+ month: buildInfoStatsPeriod(month, monthStartDate, reportDate, null)
199
+ };
200
+ }
201
+
71
202
  export default {
72
203
  name: "获取访问信息统计",
73
204
  method: "POST",
74
205
  body: "none",
75
206
  auth: true,
76
- fields: {},
207
+ fields: {
208
+ productName: { name: "产品名称", input: "string", min: 0, max: 100 }
209
+ },
77
210
  required: [],
78
- handler: async (befly) => {
211
+ handler: async (befly, ctx) => {
79
212
  const now = Date.now();
80
213
  const reportDate = getDateYmdNumber(now);
81
214
  const weekStartDate = getInfoStatsWeekStartDate(now);
82
215
  const monthStartDate = getInfoStatsMonthStartDate(now);
216
+ const productName = String(ctx?.body?.productName || "").trim();
217
+ const hasProductFilter = productName.length > 0;
83
218
 
84
- const today = await getInfoStatsRange(befly, reportDate, reportDate);
85
- const week = await getInfoStatsRange(befly, weekStartDate, reportDate);
86
- const month = await getInfoStatsRange(befly, monthStartDate, reportDate);
87
-
88
- return befly.tool.Yes("获取成功", {
89
- today: {
90
- reportDate: reportDate,
91
- sources: today.sources,
92
- productNames: today.productNames,
93
- productCodes: today.productCodes,
94
- productVersions: today.productVersions,
95
- pagePaths: today.pagePaths,
96
- pageNames: today.pageNames,
97
- deviceTypes: today.deviceTypes,
98
- browsers: today.browsers,
99
- browserVersions: today.browserVersions,
100
- osList: today.osList,
101
- osVersions: today.osVersions,
102
- deviceVendors: today.deviceVendors,
103
- deviceModels: today.deviceModels,
104
- engines: today.engines,
105
- cpuArchitectures: today.cpuArchitectures
106
- },
107
- week: {
108
- startDate: weekStartDate,
109
- endDate: reportDate,
110
- sources: week.sources,
111
- productNames: week.productNames,
112
- productCodes: week.productCodes,
113
- productVersions: week.productVersions,
114
- pagePaths: week.pagePaths,
115
- pageNames: week.pageNames,
116
- deviceTypes: week.deviceTypes,
117
- browsers: week.browsers,
118
- browserVersions: week.browserVersions,
119
- osList: week.osList,
120
- osVersions: week.osVersions,
121
- deviceVendors: week.deviceVendors,
122
- deviceModels: week.deviceModels,
123
- engines: week.engines,
124
- cpuArchitectures: week.cpuArchitectures
125
- },
126
- month: {
127
- startDate: monthStartDate,
128
- endDate: reportDate,
129
- sources: month.sources,
130
- productNames: month.productNames,
131
- productCodes: month.productCodes,
132
- productVersions: month.productVersions,
133
- pagePaths: month.pagePaths,
134
- pageNames: month.pageNames,
135
- deviceTypes: month.deviceTypes,
136
- browsers: month.browsers,
137
- browserVersions: month.browserVersions,
138
- osList: month.osList,
139
- osVersions: month.osVersions,
140
- deviceVendors: month.deviceVendors,
141
- deviceModels: month.deviceModels,
142
- engines: month.engines,
143
- cpuArchitectures: month.cpuArchitectures
219
+ if (befly.redis && !hasProductFilter) {
220
+ const today = await getInfoStatsPeriodFromRedis(befly, "day", reportDate);
221
+ const week = await getInfoStatsPeriodFromRedis(befly, "week", weekStartDate);
222
+ const month = await getInfoStatsPeriodFromRedis(befly, "month", monthStartDate);
223
+
224
+ if (today && week && month) {
225
+ return befly.tool.Yes("获取成功", buildInfoStatsResult(today, week, month, reportDate, weekStartDate, monthStartDate));
144
226
  }
145
- });
227
+ }
228
+
229
+ if (befly.redis && !hasProductFilter) {
230
+ await incrInfoStatsFallbackCount(befly, reportDate);
231
+ }
232
+
233
+ const today = await getInfoStatsRange(befly, reportDate, reportDate, productName);
234
+ const week = await getInfoStatsRange(befly, weekStartDate, reportDate, productName);
235
+ const month = await getInfoStatsRange(befly, monthStartDate, reportDate, productName);
236
+
237
+ return befly.tool.Yes("获取成功", buildInfoStatsResult(today, week, month, reportDate, weekStartDate, monthStartDate));
146
238
  }
147
239
  };
@@ -4,6 +4,7 @@ import { isValidPositiveInt } from "#root/utils/is.js";
4
4
  const ONLINE_STATS_ONLINE_TTL_SECONDS = 10 * 60;
5
5
  const ONLINE_STATS_REDIS_TTL_SECONDS = 7 * 24 * 60 * 60;
6
6
  const ONLINE_STATS_TEMP_TTL_SECONDS = 45 * 24 * 60 * 60;
7
+ const ONLINE_STATS_ACTIVE_KEY = "online:active";
7
8
 
8
9
  function getOnlineStatsMember(ctx) {
9
10
  if (isValidPositiveInt(ctx.userId)) {
@@ -60,29 +61,21 @@ async function touchRedisKeys(befly, keys, ttl) {
60
61
  }
61
62
 
62
63
  async function getOnlineStatsOnlineCount(befly) {
63
- const members = await befly.redis.smembers("online:visitors");
64
- if (members.length === 0) {
65
- return 0;
66
- }
64
+ const now = Date.now();
65
+ await befly.redis.zremrangebyscore(ONLINE_STATS_ACTIVE_KEY, "-inf", now);
66
+ return await befly.redis.zcard(ONLINE_STATS_ACTIVE_KEY);
67
+ }
67
68
 
68
- const existsList = await befly.redis.existsBatch(members.map((member) => `online:visitor:${member}`));
69
- const expiredMembers = [];
70
- let onlineCount = 0;
69
+ async function updateOnlineStatsActiveMember(befly, member, now) {
70
+ const expireAt = now + ONLINE_STATS_ONLINE_TTL_SECONDS * 1000;
71
71
 
72
- for (let i = 0; i < members.length; i++) {
73
- if (existsList[i]) {
74
- onlineCount += 1;
75
- continue;
72
+ await befly.redis.zadd(ONLINE_STATS_ACTIVE_KEY, [
73
+ {
74
+ score: expireAt,
75
+ member: member
76
76
  }
77
-
78
- expiredMembers.push(members[i]);
79
- }
80
-
81
- if (expiredMembers.length > 0) {
82
- await befly.redis.srem("online:visitors", expiredMembers);
83
- }
84
-
85
- return onlineCount;
77
+ ]);
78
+ await befly.redis.expire(ONLINE_STATS_ACTIVE_KEY, ONLINE_STATS_REDIS_TTL_SECONDS);
86
79
  }
87
80
 
88
81
  async function updateOnlineStatsPeriod(befly, periodType, periodValue, member) {
@@ -131,9 +124,7 @@ export default {
131
124
  const member = getOnlineStatsMember(ctx);
132
125
  const productName = ctx.body?.productName || "";
133
126
 
134
- await befly.redis.setString(`online:visitor:${member}`, "1", ONLINE_STATS_ONLINE_TTL_SECONDS);
135
- await befly.redis.sadd("online:visitors", [member]);
136
- await befly.redis.expire("online:visitors", ONLINE_STATS_REDIS_TTL_SECONDS);
127
+ await updateOnlineStatsActiveMember(befly, member, now);
137
128
 
138
129
  const onlineCount = await getOnlineStatsOnlineCount(befly);
139
130
 
@@ -1,6 +1,7 @@
1
1
  import { addDays, getDateYmdNumber } from "#root/utils/datetime.js";
2
2
 
3
3
  const ONLINE_STATS_DAY_LIMIT = 30;
4
+ const ONLINE_STATS_ACTIVE_KEY = "online:active";
4
5
 
5
6
  function toOnlineStatsNumber(value) {
6
7
  const num = Number(value);
@@ -75,6 +76,14 @@ async function getOnlineStatsPeriodData(befly, periodType, periodValue) {
75
76
  };
76
77
  }
77
78
 
79
+ async function getOnlineStatsPeriodDataByProduct(befly, periodType, periodValue, productName) {
80
+ if (!productName) {
81
+ return await getOnlineStatsPeriodData(befly, periodType, periodValue);
82
+ }
83
+
84
+ return await getOnlineStatsProductPeriodData(befly, periodType, periodValue, productName);
85
+ }
86
+
78
87
  async function getOnlineStatsProductPeriodData(befly, periodType, periodValue, productName) {
79
88
  const keys = getOnlineStatsProductPeriodKeys(periodType, periodValue, productName);
80
89
 
@@ -102,29 +111,9 @@ async function getOnlineStatsProductNames(befly, recentDateList) {
102
111
  }
103
112
 
104
113
  async function getOnlineStatsOnlineCount(befly) {
105
- const members = await befly.redis.smembers("online:visitors");
106
- if (members.length === 0) {
107
- return 0;
108
- }
109
-
110
- const existsList = await befly.redis.existsBatch(members.map((member) => `online:visitor:${member}`));
111
- const expiredMembers = [];
112
- let onlineCount = 0;
113
-
114
- for (let i = 0; i < members.length; i++) {
115
- if (existsList[i]) {
116
- onlineCount += 1;
117
- continue;
118
- }
119
-
120
- expiredMembers.push(members[i]);
121
- }
122
-
123
- if (expiredMembers.length > 0) {
124
- await befly.redis.srem("online:visitors", expiredMembers);
125
- }
126
-
127
- return onlineCount;
114
+ const now = Date.now();
115
+ await befly.redis.zremrangebyscore(ONLINE_STATS_ACTIVE_KEY, "-inf", now);
116
+ return await befly.redis.zcard(ONLINE_STATS_ACTIVE_KEY);
128
117
  }
129
118
 
130
119
  function sumProductTrendField(list, field) {
@@ -137,30 +126,41 @@ function sumProductTrendField(list, field) {
137
126
  return total;
138
127
  }
139
128
 
140
- export default {
141
- name: "获取在线统计",
142
- method: "POST",
143
- body: "none",
144
- auth: true,
145
- fields: {},
146
- required: [],
147
- handler: async (befly) => {
148
- const now = Date.now();
149
- const reportDate = getDateYmdNumber(now);
150
- const weekStartDate = getOnlineStatsWeekStartDate(now);
151
- const monthStartDate = getOnlineStatsMonthStartDate(now);
152
- const recentDateList = getOnlineStatsRecentDateList(now, ONLINE_STATS_DAY_LIMIT);
153
- const days = [];
154
- const products = [];
129
+ async function buildOnlineStatsDays(befly, recentDateList, productName) {
130
+ const days = [];
131
+
132
+ for (const item of recentDateList) {
133
+ const dayData = await getOnlineStatsPeriodDataByProduct(befly, "day", item, productName);
134
+
135
+ if (dayData.reportTime <= 0) {
136
+ continue;
137
+ }
138
+
139
+ days.push({
140
+ reportDate: item,
141
+ reportTime: dayData.reportTime,
142
+ pv: dayData.pv,
143
+ uv: dayData.uv
144
+ });
145
+ }
146
+
147
+ return days;
148
+ }
149
+
150
+ async function buildOnlineStatsProducts(befly, recentDateList, reportDate, weekStartDate, monthStartDate, productNames) {
151
+ const products = [];
152
+
153
+ for (const productItemName of productNames) {
154
+ const productDays = [];
155
155
 
156
156
  for (const item of recentDateList) {
157
- const dayData = await getOnlineStatsPeriodData(befly, "day", item);
157
+ const dayData = await getOnlineStatsProductPeriodData(befly, "day", item, productItemName);
158
158
 
159
159
  if (dayData.reportTime <= 0) {
160
160
  continue;
161
161
  }
162
162
 
163
- days.push({
163
+ productDays.push({
164
164
  reportDate: item,
165
165
  reportTime: dayData.reportTime,
166
166
  pv: dayData.pv,
@@ -168,62 +168,65 @@ export default {
168
168
  });
169
169
  }
170
170
 
171
- const currentDay = await getOnlineStatsPeriodData(befly, "day", reportDate);
172
- const weekCurrent = await getOnlineStatsPeriodData(befly, "week", weekStartDate);
173
- const monthCurrent = await getOnlineStatsPeriodData(befly, "month", monthStartDate);
174
- const onlineCount = await getOnlineStatsOnlineCount(befly);
175
- const productNames = await getOnlineStatsProductNames(befly, recentDateList);
171
+ const productToday = await getOnlineStatsProductPeriodData(befly, "day", reportDate, productItemName);
172
+ const productWeek = await getOnlineStatsProductPeriodData(befly, "week", weekStartDate, productItemName);
173
+ const productMonth = await getOnlineStatsProductPeriodData(befly, "month", monthStartDate, productItemName);
176
174
 
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
- }
175
+ products.push({
176
+ key: productItemName,
177
+ productName: productItemName,
178
+ today: {
179
+ pv: productToday.pv,
180
+ uv: productToday.uv
181
+ },
182
+ week: {
183
+ pv: productWeek.pv,
184
+ uv: productWeek.uv
185
+ },
186
+ month: {
187
+ pv: productMonth.pv,
188
+ uv: productMonth.uv
189
+ },
190
+ days: productDays,
191
+ totalPv: sumProductTrendField(productDays, "pv"),
192
+ totalUv: sumProductTrendField(productDays, "uv")
193
+ });
194
+ }
194
195
 
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
- });
196
+ return products.toSorted((a, b) => {
197
+ if (b.totalPv !== a.totalPv) {
198
+ return b.totalPv - a.totalPv;
218
199
  }
219
200
 
220
- products.sort((a, b) => {
221
- if (b.totalPv !== a.totalPv) {
222
- return b.totalPv - a.totalPv;
223
- }
201
+ return String(a.productName).localeCompare(String(b.productName), "zh-CN");
202
+ });
203
+ }
224
204
 
225
- return String(a.productName).localeCompare(String(b.productName), "zh-CN");
226
- });
205
+ export default {
206
+ name: "获取在线统计",
207
+ method: "POST",
208
+ body: "none",
209
+ auth: true,
210
+ fields: {
211
+ productName: { name: "产品名称", input: "string", min: 0, max: 100 }
212
+ },
213
+ required: [],
214
+ handler: async (befly, ctx) => {
215
+ const now = Date.now();
216
+ const reportDate = getDateYmdNumber(now);
217
+ const weekStartDate = getOnlineStatsWeekStartDate(now);
218
+ const monthStartDate = getOnlineStatsMonthStartDate(now);
219
+ const recentDateList = getOnlineStatsRecentDateList(now, ONLINE_STATS_DAY_LIMIT);
220
+ const productName = String(ctx?.body?.productName || "").trim();
221
+ const hasProductFilter = productName.length > 0;
222
+ const days = await buildOnlineStatsDays(befly, recentDateList, productName);
223
+
224
+ const currentDay = await getOnlineStatsPeriodDataByProduct(befly, "day", reportDate, productName);
225
+ const weekCurrent = await getOnlineStatsPeriodDataByProduct(befly, "week", weekStartDate, productName);
226
+ const monthCurrent = await getOnlineStatsPeriodDataByProduct(befly, "month", monthStartDate, productName);
227
+ const onlineCount = await getOnlineStatsOnlineCount(befly);
228
+ const productNames = hasProductFilter ? [] : await getOnlineStatsProductNames(befly, recentDateList);
229
+ const products = await buildOnlineStatsProducts(befly, recentDateList, reportDate, weekStartDate, monthStartDate, productNames);
227
230
 
228
231
  return befly.tool.Yes("获取成功", {
229
232
  queryTime: now,
@@ -47,9 +47,13 @@ export default {
47
47
 
48
48
  // 4. 角色权限检查
49
49
  // apiPath 在 apiHandler 中已统一生成并写入 ctx.apiPath
50
+ let hasPermission = false;
50
51
 
51
- // 极简方案:每个角色一个 Set,直接判断成员是否存在
52
- const hasPermission = await befly.redis.sismember(`role:apis:${ctx.roleCode}`, ctx.apiPath);
52
+ if (befly.cache && typeof befly.cache.checkRoleApis === "function") {
53
+ hasPermission = await befly.cache.checkRoleApis(ctx.roleCode, ctx.apiPath);
54
+ } else {
55
+ hasPermission = await befly.redis.sismember(`role:apis:${ctx.roleCode}`, ctx.apiPath);
56
+ }
53
57
 
54
58
  if (!hasPermission) {
55
59
  ctx.response = ErrorResponse(