befly 2.3.3 → 3.0.1

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.
Files changed (87) hide show
  1. package/checks/conflict.ts +329 -0
  2. package/checks/table.ts +252 -0
  3. package/config/env.ts +218 -0
  4. package/config/fields.ts +55 -0
  5. package/config/regexAliases.ts +51 -0
  6. package/config/reserved.ts +96 -0
  7. package/main.ts +47 -0
  8. package/package.json +26 -11
  9. package/plugins/db.ts +60 -0
  10. package/plugins/logger.ts +28 -0
  11. package/plugins/redis.ts +47 -0
  12. package/scripts/syncDb/apply.ts +171 -0
  13. package/scripts/syncDb/constants.ts +71 -0
  14. package/scripts/syncDb/ddl.ts +189 -0
  15. package/scripts/syncDb/helpers.ts +173 -0
  16. package/scripts/syncDb/index.ts +203 -0
  17. package/scripts/syncDb/schema.ts +199 -0
  18. package/scripts/syncDb/sqlite.ts +50 -0
  19. package/scripts/syncDb/state.ts +106 -0
  20. package/scripts/syncDb/table.ts +214 -0
  21. package/scripts/syncDb/tableCreate.ts +148 -0
  22. package/scripts/syncDb/tests/constants.test.ts +105 -0
  23. package/scripts/syncDb/tests/ddl.test.ts +134 -0
  24. package/scripts/syncDb/tests/helpers.test.ts +70 -0
  25. package/scripts/syncDb/types.ts +92 -0
  26. package/scripts/syncDb/version.ts +73 -0
  27. package/scripts/syncDb.ts +10 -0
  28. package/tsconfig.json +58 -0
  29. package/types/addon.d.ts +53 -0
  30. package/types/api.d.ts +249 -0
  31. package/types/befly.d.ts +230 -0
  32. package/types/common.d.ts +215 -0
  33. package/types/context.d.ts +7 -0
  34. package/types/crypto.d.ts +23 -0
  35. package/types/database.d.ts +273 -0
  36. package/types/index.d.ts +450 -0
  37. package/types/index.ts +438 -0
  38. package/types/jwt.d.ts +99 -0
  39. package/types/logger.d.ts +43 -0
  40. package/types/plugin.d.ts +109 -0
  41. package/types/redis.d.ts +46 -0
  42. package/types/tool.d.ts +67 -0
  43. package/types/validator.d.ts +43 -0
  44. package/types/validator.ts +43 -0
  45. package/utils/colors.ts +221 -0
  46. package/utils/crypto.ts +308 -0
  47. package/utils/database.ts +348 -0
  48. package/utils/dbHelper.ts +713 -0
  49. package/utils/helper.ts +812 -0
  50. package/utils/index.ts +33 -0
  51. package/utils/jwt.ts +493 -0
  52. package/utils/logger.ts +191 -0
  53. package/utils/redisHelper.ts +321 -0
  54. package/utils/requestContext.ts +167 -0
  55. package/utils/sqlBuilder.ts +611 -0
  56. package/utils/validate.ts +493 -0
  57. package/utils/{xml.js → xml.ts} +100 -74
  58. package/.npmrc +0 -3
  59. package/.prettierignore +0 -2
  60. package/.prettierrc +0 -11
  61. package/apis/health/info.js +0 -49
  62. package/apis/tool/tokenCheck.js +0 -29
  63. package/bin/befly.js +0 -109
  64. package/bunfig.toml +0 -3
  65. package/checks/table.js +0 -206
  66. package/config/env.js +0 -64
  67. package/main.js +0 -579
  68. package/plugins/db.js +0 -46
  69. package/plugins/logger.js +0 -14
  70. package/plugins/redis.js +0 -32
  71. package/plugins/tool.js +0 -8
  72. package/scripts/syncDb.js +0 -752
  73. package/scripts/syncDev.js +0 -96
  74. package/system.js +0 -118
  75. package/tables/common.json +0 -16
  76. package/tables/tool.json +0 -6
  77. package/utils/api.js +0 -27
  78. package/utils/colors.js +0 -83
  79. package/utils/crypto.js +0 -260
  80. package/utils/index.js +0 -334
  81. package/utils/jwt.js +0 -387
  82. package/utils/logger.js +0 -143
  83. package/utils/redisHelper.js +0 -74
  84. package/utils/sqlBuilder.js +0 -498
  85. package/utils/sqlManager.js +0 -471
  86. package/utils/tool.js +0 -31
  87. package/utils/validate.js +0 -226
package/utils/jwt.js DELETED
@@ -1,387 +0,0 @@
1
- import { createHmac } from 'crypto';
2
- import { Env } from '../config/env.js';
3
-
4
- /**
5
- * JWT 工具类
6
- * 提供JWT token的签名、验证和解码功能以及应用层的便捷接口
7
- */
8
- export class Jwt {
9
- // 原基础工具:算法映射
10
- static ALGORITHMS = {
11
- HS256: 'sha256',
12
- HS384: 'sha384',
13
- HS512: 'sha512'
14
- };
15
-
16
- // 原基础工具:Base64 URL 编解码
17
- static base64UrlEncode(input) {
18
- const base64 = Buffer.isBuffer(input) ? input.toString('base64') : Buffer.from(input, 'utf8').toString('base64');
19
- return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
20
- }
21
- static base64UrlDecode(str) {
22
- const padding = 4 - (str.length % 4);
23
- if (padding !== 4) str += '='.repeat(padding);
24
- str = str.replace(/-/g, '+').replace(/_/g, '/');
25
- return Buffer.from(str, 'base64').toString('utf8');
26
- }
27
-
28
- // 原基础工具:过期时间解析与签名/比较
29
- static parseExpiration(expiresIn) {
30
- if (typeof expiresIn === 'number') return expiresIn;
31
- if (typeof expiresIn !== 'string') throw new Error('过期时间格式无效');
32
- const numericValue = parseInt(expiresIn);
33
- if (!isNaN(numericValue) && numericValue.toString() === expiresIn) return numericValue;
34
- const match = expiresIn.match(/^(\d+)(ms|[smhdwy])$/);
35
- if (!match) throw new Error('过期时间格式无效');
36
- const value = parseInt(match[1]);
37
- const unit = match[2];
38
- if (unit === 'ms') return Math.floor(value / 1000);
39
- const multipliers = { s: 1, m: 60, h: 3600, d: 86400, w: 604800, y: 31536000 };
40
- return value * multipliers[unit];
41
- }
42
- static createSignature(algorithm, secret, data) {
43
- const hashAlgorithm = this.ALGORITHMS[algorithm];
44
- if (!hashAlgorithm) throw new Error(`不支持的算法: ${algorithm}`);
45
- const hmac = createHmac(hashAlgorithm, secret);
46
- hmac.update(data);
47
- return this.base64UrlEncode(hmac.digest());
48
- }
49
- static constantTimeCompare(a, b) {
50
- if (a.length !== b.length) return false;
51
- let result = 0;
52
- for (let i = 0; i < a.length; i++) {
53
- result |= a.charCodeAt(i) ^ b.charCodeAt(i);
54
- }
55
- return result === 0;
56
- }
57
- /**
58
- * 签名JWT token
59
- * @param {object} payload - JWT载荷数据
60
- * @param {object} options - 签名选项
61
- * @returns {string} JWT token字符串
62
- */
63
- static sign(payload, options = {}) {
64
- if (!payload || typeof payload !== 'object') {
65
- throw new Error('载荷必须是非空对象');
66
- }
67
-
68
- const secret = options.secret || Env.JWT_SECRET;
69
- const algorithm = options.algorithm || Env.JWT_ALGORITHM || 'HS256';
70
-
71
- if (!secret) {
72
- throw new Error('JWT密钥是必需的');
73
- }
74
- const now = Math.floor(Date.now() / 1000);
75
-
76
- // 创建header
77
- const header = Jwt.base64UrlEncode(
78
- JSON.stringify({
79
- alg: algorithm,
80
- typ: 'JWT'
81
- })
82
- );
83
-
84
- // 创建payload
85
- const jwtPayload = { ...payload, iat: now };
86
-
87
- if (options.expiresIn || Env.JWT_EXPIRES_IN) {
88
- const expSeconds = Jwt.parseExpiration(options.expiresIn || Env.JWT_EXPIRES_IN);
89
- jwtPayload.exp = now + expSeconds;
90
- }
91
- if (options.issuer) jwtPayload.iss = options.issuer;
92
- if (options.audience) jwtPayload.aud = options.audience;
93
- if (options.subject) jwtPayload.sub = options.subject;
94
- if (options.notBefore) {
95
- jwtPayload.nbf = typeof options.notBefore === 'number' ? options.notBefore : now + Jwt.parseExpiration(options.notBefore);
96
- }
97
- if (options.jwtId) jwtPayload.jti = options.jwtId;
98
-
99
- const encodedPayload = Jwt.base64UrlEncode(JSON.stringify(jwtPayload));
100
-
101
- // 创建签名
102
- const data = `${header}.${encodedPayload}`;
103
- const signature = Jwt.createSignature(algorithm, secret, data);
104
-
105
- return `${data}.${signature}`;
106
- }
107
-
108
- /**
109
- * 验证JWT token
110
- * @param {string} token - JWT token字符串
111
- * @param {object} options - 验证选项
112
- * @returns {object} 解码后的载荷数据
113
- */
114
- static verify(token, options = {}) {
115
- if (!token || typeof token !== 'string') {
116
- throw new Error('Token必须是非空字符串');
117
- }
118
-
119
- const secret = options.secret || Env.JWT_SECRET;
120
- if (!secret) {
121
- throw new Error('JWT密钥是必需的');
122
- }
123
-
124
- const parts = token.split('.');
125
- if (parts.length !== 3) {
126
- throw new Error('JWT格式无效');
127
- }
128
-
129
- try {
130
- // 解析header和payload
131
- const header = JSON.parse(Jwt.base64UrlDecode(parts[0]));
132
- const payload = JSON.parse(Jwt.base64UrlDecode(parts[1]));
133
- const signature = parts[2];
134
-
135
- // 验证算法
136
- if (!Jwt.ALGORITHMS[header.alg]) {
137
- throw new Error(`不支持的算法: ${header.alg}`);
138
- }
139
-
140
- // 验证签名
141
- const data = `${parts[0]}.${parts[1]}`;
142
- const expectedSignature = Jwt.createSignature(header.alg, secret, data);
143
-
144
- if (!Jwt.constantTimeCompare(signature, expectedSignature)) {
145
- throw new Error('Token签名无效');
146
- }
147
-
148
- // 验证时间
149
- const now = Math.floor(Date.now() / 1000);
150
-
151
- if (!options.ignoreExpiration && payload.exp && payload.exp < now) {
152
- throw new Error('Token已过期');
153
- }
154
- if (!options.ignoreNotBefore && payload.nbf && payload.nbf > now) {
155
- throw new Error('Token尚未生效');
156
- }
157
-
158
- // 验证issuer、audience、subject
159
- if (options.issuer && payload.iss !== options.issuer) {
160
- throw new Error('Token发行者无效');
161
- }
162
- if (options.audience) {
163
- const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
164
- if (!audiences.includes(options.audience)) {
165
- throw new Error('Token受众无效');
166
- }
167
- }
168
- if (options.subject && payload.sub !== options.subject) {
169
- throw new Error('Token主题无效');
170
- }
171
-
172
- return payload;
173
- } catch (error) {
174
- if (error.message.includes('JWT') || error.message.includes('Token') || error.message.includes('无效') || error.message.includes('过期') || error.message.includes('不支持')) {
175
- throw error;
176
- }
177
- throw new Error('Token验证失败: ' + error.message);
178
- }
179
- }
180
-
181
- /**
182
- * 解码JWT token (不验证签名)
183
- * @param {string} token - JWT token字符串
184
- * @param {boolean} complete - 是否返回完整信息(包含header)
185
- * @returns {object} 解码后的内容
186
- */
187
- static decode(token, complete = false) {
188
- if (!token || typeof token !== 'string') {
189
- throw new Error('Token必须是非空字符串');
190
- }
191
-
192
- const parts = token.split('.');
193
- if (parts.length !== 3) {
194
- throw new Error('JWT格式无效');
195
- }
196
-
197
- try {
198
- const header = JSON.parse(Jwt.base64UrlDecode(parts[0]));
199
- const payload = JSON.parse(Jwt.base64UrlDecode(parts[1]));
200
-
201
- return complete ? { header, payload, signature: parts[2] } : payload;
202
- } catch (error) {
203
- throw new Error('JWT解码失败: ' + error.message);
204
- }
205
- }
206
-
207
- /**
208
- * 获取token剩余有效时间
209
- * @param {string} token - JWT token字符串
210
- * @returns {number} 剩余秒数,-1表示已过期或无过期时间
211
- */
212
- static getTimeToExpiry(token) {
213
- try {
214
- const payload = this.decode(token);
215
- if (!payload.exp) return -1;
216
- const remaining = payload.exp - Math.floor(Date.now() / 1000);
217
- return remaining > 0 ? remaining : -1;
218
- } catch {
219
- return -1;
220
- }
221
- }
222
-
223
- /**
224
- * 检查token是否已过期
225
- * @param {string} token - JWT token字符串
226
- * @returns {boolean} 是否已过期
227
- */
228
- static isExpired(token) {
229
- return this.getTimeToExpiry(token) <= 0;
230
- }
231
-
232
- // 以下是应用层的便捷方法
233
-
234
- /**
235
- * 签名用户认证token
236
- * @param {object} userInfo - 用户信息对象
237
- * @param {object} options - 签名选项
238
- * @returns {string} JWT token字符串
239
- */
240
- static signUserToken(userInfo, options = {}) {
241
- return this.sign(userInfo, options);
242
- }
243
-
244
- /**
245
- * 签名API访问token
246
- * @param {object} payload - 载荷数据
247
- * @param {object} options - 签名选项
248
- * @returns {string} API token字符串
249
- */
250
- static signAPIToken(payload, options = {}) {
251
- return this.sign(payload, { audience: 'api', expiresIn: '1h', ...options });
252
- }
253
-
254
- /**
255
- * 签名刷新token
256
- * @param {object} payload - 载荷数据
257
- * @param {object} options - 签名选项
258
- * @returns {string} 刷新token字符串
259
- */
260
- static signRefreshToken(payload, options = {}) {
261
- return this.sign(payload, { audience: 'refresh', expiresIn: '30d', ...options });
262
- }
263
-
264
- /**
265
- * 签名临时token (用于重置密码等)
266
- * @param {object} payload - 载荷数据
267
- * @param {object} options - 签名选项
268
- * @returns {string} 临时token字符串
269
- */
270
- static signTempToken(payload, options = {}) {
271
- return this.sign(payload, { audience: 'temporary', expiresIn: '15m', ...options });
272
- }
273
-
274
- /**
275
- * 验证用户认证token
276
- * @param {string} token - JWT token字符串
277
- * @param {object} options - 验证选项
278
- * @returns {object} 用户信息对象
279
- */
280
- static verifyUserToken(token, options = {}) {
281
- return this.verify(token, options);
282
- }
283
-
284
- /**
285
- * 验证API访问token
286
- * @param {string} token - API token字符串
287
- * @param {object} options - 验证选项
288
- * @returns {object} 解码后的载荷数据
289
- */
290
- static verifyAPIToken(token, options = {}) {
291
- return this.verify(token, { audience: 'api', ...options });
292
- }
293
-
294
- /**
295
- * 验证刷新token
296
- * @param {string} token - 刷新token字符串
297
- * @param {object} options - 验证选项
298
- * @returns {object} 解码后的载荷数据
299
- */
300
- static verifyRefreshToken(token, options = {}) {
301
- return this.verify(token, { audience: 'refresh', ...options });
302
- }
303
-
304
- /**
305
- * 验证临时token
306
- * @param {string} token - 临时token字符串
307
- * @param {object} options - 验证选项
308
- * @returns {object} 解码后的载荷数据
309
- */
310
- static verifyTempToken(token, options = {}) {
311
- return this.verify(token, { audience: 'temporary', ...options });
312
- }
313
-
314
- /**
315
- * 验证token并检查权限
316
- * @param {string} token - JWT token字符串
317
- * @param {string|Array<string>} requiredPermissions - 需要的权限列表
318
- * @param {object} options - 验证选项
319
- * @returns {object} 解码后的载荷数据
320
- */
321
- static verifyWithPermissions(token, requiredPermissions, options = {}) {
322
- const payload = this.verify(token, options);
323
-
324
- if (!payload.permissions) {
325
- throw new Error('Token中不包含权限信息');
326
- }
327
-
328
- const permissions = Array.isArray(requiredPermissions) ? requiredPermissions : [requiredPermissions];
329
-
330
- const hasPermission = permissions.every((permission) => payload.permissions.includes(permission));
331
-
332
- if (!hasPermission) {
333
- throw new Error('权限不足');
334
- }
335
-
336
- return payload;
337
- }
338
-
339
- /**
340
- * 验证token并检查角色
341
- * @param {string} token - JWT token字符串
342
- * @param {string|Array<string>} requiredRoles - 需要的角色列表
343
- * @param {object} options - 验证选项
344
- * @returns {object} 解码后的载荷数据
345
- */
346
- static verifyWithRoles(token, requiredRoles, options = {}) {
347
- const payload = this.verify(token, options);
348
-
349
- if (!payload.role && !payload.roles) {
350
- throw new Error('Token中不包含角色信息');
351
- }
352
-
353
- const userRoles = payload.roles || [payload.role];
354
- const roles = Array.isArray(requiredRoles) ? requiredRoles : [requiredRoles];
355
-
356
- const hasRole = roles.some((role) => userRoles.includes(role));
357
-
358
- if (!hasRole) {
359
- throw new Error('角色权限不足');
360
- }
361
-
362
- return payload;
363
- }
364
-
365
- /**
366
- * 软验证token (忽略过期时间)
367
- * @param {string} token - JWT token字符串
368
- * @param {object} options - 验证选项
369
- * @returns {object} 解码后的载荷数据
370
- */
371
- static verifySoft(token, options = {}) {
372
- return this.verify(token, { ignoreExpiration: true, ...options });
373
- }
374
-
375
- /**
376
- * 检查token是否即将过期
377
- * @param {string} token - JWT token字符串
378
- * @param {number} thresholdSeconds - 过期阈值(秒),默认300秒(5分钟)
379
- * @returns {boolean} 是否即将过期
380
- */
381
- static isNearExpiry(token, thresholdSeconds = 300) {
382
- const timeToExpiry = this.getTimeToExpiry(token);
383
- return timeToExpiry > 0 && timeToExpiry <= thresholdSeconds;
384
- }
385
- }
386
-
387
- // 已使用 `export class Jwt` 具名导出
package/utils/logger.js DELETED
@@ -1,143 +0,0 @@
1
- import path from 'path';
2
- import { appendFile, stat } from 'node:fs/promises';
3
- import { formatDate } from './index.js';
4
- import { Env } from '../config/env.js';
5
-
6
- export class Logger {
7
- // 静态属性
8
- static level = Env.LOG_LEVEL || 'info';
9
- static levels = {
10
- error: 0,
11
- warn: 1,
12
- info: 2,
13
- debug: 3
14
- };
15
- static logDir = Env.LOG_DIR || 'logs';
16
- static maxFileSize = Env.LOG_MAX_SIZE || 50 * 1024 * 1024; // 50MB
17
- static currentFiles = new Map(); // key: prefix, value: filepath
18
-
19
- static formatMessage(level, message) {
20
- const timestamp = formatDate();
21
- const levelStr = level.toUpperCase().padStart(5);
22
-
23
- let msg = `[${timestamp}] ${levelStr} - `;
24
-
25
- if (Object.keys(message).length > 0) {
26
- msg += `${JSON.stringify(message).replace(/\s+/g, ' ').replace(/\\"/g, '"').replace(/\\n/g, ' ')}`;
27
- }
28
-
29
- return msg;
30
- }
31
-
32
- static async log(level, message) {
33
- // 内联 shouldLog 逻辑,检查日志级别
34
- if (this.levels[level] > this.levels[this.level]) return;
35
-
36
- const formattedMessage = this.formatMessage(level, message);
37
-
38
- // 控制台输出
39
- if (Env.LOG_TO_CONSOLE === 1) {
40
- console.log(formattedMessage);
41
- }
42
-
43
- await this.writeToFile(formattedMessage, level);
44
- }
45
-
46
- static async writeToFile(message, level = 'info') {
47
- try {
48
- let prefix;
49
-
50
- // debug 日志使用单独的文件名
51
- if (level === 'debug') {
52
- prefix = 'debug';
53
- } else {
54
- prefix = new Date().toISOString().split('T')[0];
55
- }
56
-
57
- // 检查缓存的当前文件是否仍然可用
58
- let currentLogFile = this.currentFiles.get(prefix);
59
-
60
- if (currentLogFile) {
61
- try {
62
- const stats = await stat(currentLogFile);
63
- // 如果文件超过最大大小,清除缓存
64
- if (stats.size >= this.maxFileSize) {
65
- this.currentFiles.delete(prefix);
66
- currentLogFile = null;
67
- }
68
- } catch (error) {
69
- // 文件不存在或无法访问,清除缓存
70
- this.currentFiles.delete(prefix);
71
- currentLogFile = null;
72
- }
73
- }
74
-
75
- // 如果没有缓存的文件或文件已满,查找合适的文件
76
- if (!currentLogFile) {
77
- currentLogFile = await this.findAvailableLogFile(prefix);
78
- this.currentFiles.set(prefix, currentLogFile);
79
- }
80
-
81
- // 使用 Node.js 的 appendFile 进行文件追加
82
- await appendFile(currentLogFile, message + '\n', 'utf8');
83
- } catch (error) {
84
- console.error('写入日志文件失败:', error.message);
85
- }
86
- }
87
-
88
- static async findAvailableLogFile(prefix) {
89
- const glob = new Bun.Glob(`${prefix}.*.log`);
90
- const files = await Array.fromAsync(glob.scan(this.logDir));
91
-
92
- // 按文件名排序
93
- files.sort((a, b) => {
94
- const aNum = parseInt(a.match(/\.(\d+)\.log$/)?.[1] || '0');
95
- const bNum = parseInt(b.match(/\.(\d+)\.log$/)?.[1] || '0');
96
- return aNum - bNum;
97
- });
98
-
99
- // 从最后一个文件开始检查
100
- for (let i = files.length - 1; i >= 0; i--) {
101
- const filePath = path.join(this.logDir, files[i]);
102
- try {
103
- const stats = await stat(filePath);
104
- if (stats.size < this.maxFileSize) {
105
- return filePath;
106
- }
107
- } catch (error) {
108
- // 文件不存在或无法访问,跳过
109
- continue;
110
- }
111
- }
112
-
113
- // 所有文件都已满或没有文件,创建新文件
114
- const nextIndex = files.length > 0 ? Math.max(...files.map((f) => parseInt(f.match(/\.(\d+)\.log$/)?.[1] || '0'))) + 1 : 0;
115
-
116
- return path.join(this.logDir, `${prefix}.${nextIndex}.log`);
117
- }
118
-
119
- // 静态便捷方法
120
- static async error(message) {
121
- await this.log('error', message);
122
- }
123
-
124
- static async warn(message) {
125
- await this.log('warn', message);
126
- }
127
-
128
- static async info(message) {
129
- await this.log('info', message);
130
- }
131
-
132
- static async debug(message) {
133
- // debug 级别必须记录,忽略级别检查
134
- const formattedMessage = this.formatMessage('debug', message);
135
-
136
- // 控制台输出
137
- if (Env.LOG_TO_CONSOLE === 1) {
138
- console.log(formattedMessage);
139
- }
140
-
141
- await this.writeToFile(formattedMessage, 'debug');
142
- }
143
- }
@@ -1,74 +0,0 @@
1
- import { redis as bunRedis } from 'bun';
2
- import { Env } from '../config/env.js';
3
- import { Logger } from './logger.js';
4
-
5
- const prefix = Env.REDIS_KEY_PREFIX ? `${Env.REDIS_KEY_PREFIX}:` : '';
6
-
7
- let redisClient = bunRedis;
8
- export const setRedisClient = (client) => {
9
- redisClient = client || bunRedis;
10
- };
11
- export const getRedisClient = () => redisClient;
12
-
13
- export const RedisHelper = {
14
- async setObject(key, obj, ttl = null) {
15
- try {
16
- const data = JSON.stringify(obj);
17
- const pkey = `${prefix}${key}`;
18
- if (ttl) {
19
- return await redisClient.setEx(pkey, ttl, data);
20
- }
21
- return await redisClient.set(pkey, data);
22
- } catch (error) {
23
- Logger.error({
24
- msg: 'Redis setObject 错误',
25
- message: error.message,
26
- stack: error.stack
27
- });
28
- }
29
- },
30
-
31
- async getObject(key) {
32
- try {
33
- const pkey = `${prefix}${key}`;
34
- const data = await redisClient.get(pkey);
35
- return data ? JSON.parse(data) : null;
36
- } catch (error) {
37
- Logger.error({
38
- msg: 'Redis getObject 错误',
39
- message: error.message,
40
- stack: error.stack
41
- });
42
- return null;
43
- }
44
- },
45
-
46
- async delObject(key) {
47
- try {
48
- const pkey = `${prefix}${key}`;
49
- await redisClient.del(pkey);
50
- } catch (error) {
51
- Logger.error({
52
- msg: 'Redis delObject 错误',
53
- message: error.message,
54
- stack: error.stack
55
- });
56
- }
57
- },
58
-
59
- async genTimeID() {
60
- const timestamp = Math.floor(Date.now() / 1000);
61
- const key = `${prefix}time_id_counter:${timestamp}`;
62
-
63
- const counter = await redisClient.incr(key);
64
- await redisClient.expire(key, 2);
65
-
66
- const counterPrefix = (counter % 1000).toString().padStart(3, '0');
67
- const randomSuffix = Math.floor(Math.random() * 1000)
68
- .toString()
69
- .padStart(3, '0');
70
- const suffix = `${counterPrefix}${randomSuffix}`;
71
-
72
- return Number(`${timestamp}${suffix}`);
73
- }
74
- };