befly 3.8.1 → 3.8.2
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/check.ts +57 -24
- package/lib/cacheHelper.ts +169 -0
- package/lib/redisHelper.ts +49 -65
- package/lib/validator.ts +29 -32
- package/lifecycle/bootstrap.ts +5 -19
- package/lifecycle/lifecycle.ts +16 -59
- package/lifecycle/loadApis.ts +164 -0
- package/lifecycle/loadPlugins.ts +244 -0
- package/main.ts +12 -15
- package/menu.json +13 -13
- package/package.json +2 -2
- package/plugins/cache.ts +3 -167
- package/types/common.d.ts +28 -7
- package/util.ts +4 -48
- package/lifecycle/loader.ts +0 -427
package/check.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import { basename } from 'pathe';
|
|
7
7
|
import { Logger } from './lib/logger.js';
|
|
8
|
-
import { parseRule } from './util.js';
|
|
9
8
|
import { projectTableDir } from './paths.js';
|
|
10
9
|
import { Addon } from './lib/addon.js';
|
|
10
|
+
import type { FieldDefinition } from './types/common.d.ts';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* 表文件信息接口
|
|
@@ -112,9 +112,9 @@ export const checkDefault = async function (): Promise<void> {
|
|
|
112
112
|
let fileRules = 0;
|
|
113
113
|
|
|
114
114
|
// 检查 table 中的每个验证规则
|
|
115
|
-
for (const [colKey,
|
|
116
|
-
if (typeof
|
|
117
|
-
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey}
|
|
115
|
+
for (const [colKey, fieldDef] of Object.entries(table)) {
|
|
116
|
+
if (typeof fieldDef !== 'object' || fieldDef === null || Array.isArray(fieldDef)) {
|
|
117
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 规则必须为对象`);
|
|
118
118
|
fileValid = false;
|
|
119
119
|
continue;
|
|
120
120
|
}
|
|
@@ -129,17 +129,58 @@ export const checkDefault = async function (): Promise<void> {
|
|
|
129
129
|
fileValid = false;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey}
|
|
132
|
+
// 直接使用字段对象
|
|
133
|
+
const field = fieldDef as FieldDefinition;
|
|
134
|
+
|
|
135
|
+
// 检查必填字段:name, type, min, max
|
|
136
|
+
if (!field.name || typeof field.name !== 'string') {
|
|
137
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 缺少必填字段 name 或类型错误`);
|
|
138
|
+
fileValid = false;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!field.type || typeof field.type !== 'string') {
|
|
142
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 缺少必填字段 type 或类型错误`);
|
|
143
|
+
fileValid = false;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (field.min === undefined) {
|
|
147
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 缺少必填字段 min`);
|
|
148
|
+
fileValid = false;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (field.max === undefined) {
|
|
152
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 缺少必填字段 max`);
|
|
138
153
|
fileValid = false;
|
|
139
154
|
continue;
|
|
140
155
|
}
|
|
141
156
|
|
|
142
|
-
|
|
157
|
+
// 检查可选字段的类型
|
|
158
|
+
if (field.detail !== undefined && typeof field.detail !== 'string') {
|
|
159
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 字段 detail 类型错误,必须为字符串`);
|
|
160
|
+
fileValid = false;
|
|
161
|
+
}
|
|
162
|
+
if (field.index !== undefined && typeof field.index !== 'boolean') {
|
|
163
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 字段 index 类型错误,必须为布尔值`);
|
|
164
|
+
fileValid = false;
|
|
165
|
+
}
|
|
166
|
+
if (field.unique !== undefined && typeof field.unique !== 'boolean') {
|
|
167
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 字段 unique 类型错误,必须为布尔值`);
|
|
168
|
+
fileValid = false;
|
|
169
|
+
}
|
|
170
|
+
if (field.nullable !== undefined && typeof field.nullable !== 'boolean') {
|
|
171
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 字段 nullable 类型错误,必须为布尔值`);
|
|
172
|
+
fileValid = false;
|
|
173
|
+
}
|
|
174
|
+
if (field.unsigned !== undefined && typeof field.unsigned !== 'boolean') {
|
|
175
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 字段 unsigned 类型错误,必须为布尔值`);
|
|
176
|
+
fileValid = false;
|
|
177
|
+
}
|
|
178
|
+
if (field.regexp !== undefined && field.regexp !== null && typeof field.regexp !== 'string') {
|
|
179
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 字段 regexp 类型错误,必须为 null 或字符串`);
|
|
180
|
+
fileValid = false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const { name: fieldName, type: fieldType, min: fieldMin, max: fieldMax, default: fieldDefault, index: fieldIndex, regexp: fieldRegexp } = field;
|
|
143
184
|
|
|
144
185
|
// 第1个值:名称必须为中文、数字、字母、下划线、短横线、空格
|
|
145
186
|
if (!FIELD_NAME_REGEX.test(fieldName)) {
|
|
@@ -171,18 +212,9 @@ export const checkDefault = async function (): Promise<void> {
|
|
|
171
212
|
}
|
|
172
213
|
}
|
|
173
214
|
|
|
174
|
-
// 第6个值:是否创建索引必须为0或1
|
|
175
|
-
if (fieldIndex !== 0 && fieldIndex !== 1) {
|
|
176
|
-
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 索引标识 "${fieldIndex}" 格式错误,必须为0或1`);
|
|
177
|
-
fileValid = false;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// 第7个值:必须为null或正则表达式(parseRule已经验证过了)
|
|
181
|
-
// parseRule 已经将正则字符串转换为 RegExp 或 null,这里不需要再验证
|
|
182
|
-
|
|
183
215
|
// 第4个值与类型联动校验 + 默认值规则
|
|
184
216
|
if (fieldType === 'text') {
|
|
185
|
-
// text:min/max 必须为 null,默认值必须为
|
|
217
|
+
// text:min/max 必须为 null,默认值必须为 null
|
|
186
218
|
if (fieldMin !== null) {
|
|
187
219
|
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 的 text 类型最小值必须为 null,当前为 "${fieldMin}"`);
|
|
188
220
|
fileValid = false;
|
|
@@ -191,7 +223,7 @@ export const checkDefault = async function (): Promise<void> {
|
|
|
191
223
|
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 的 text 类型最大长度必须为 null,当前为 "${fieldMax}"`);
|
|
192
224
|
fileValid = false;
|
|
193
225
|
}
|
|
194
|
-
if (fieldDefault !==
|
|
226
|
+
if (fieldDefault !== null) {
|
|
195
227
|
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 为 text 类型,默认值必须为 null,当前为 "${fieldDefault}"`);
|
|
196
228
|
fileValid = false;
|
|
197
229
|
}
|
|
@@ -204,8 +236,9 @@ export const checkDefault = async function (): Promise<void> {
|
|
|
204
236
|
fileValid = false;
|
|
205
237
|
}
|
|
206
238
|
} else if (fieldType === 'number') {
|
|
207
|
-
|
|
208
|
-
|
|
239
|
+
// number 类型:default 如果存在,必须为 null 或 number
|
|
240
|
+
if (fieldDefault !== undefined && fieldDefault !== null && typeof fieldDefault !== 'number') {
|
|
241
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${colKey} 为 number 类型,` + `默认值必须为数字或 null,当前为 "${fieldDefault}"`);
|
|
209
242
|
fileValid = false;
|
|
210
243
|
}
|
|
211
244
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 缓存助手 - TypeScript 版本
|
|
3
|
+
* 负责在服务器启动时缓存接口、菜单和角色权限到 Redis
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Logger } from './logger.js';
|
|
7
|
+
import type { BeflyContext } from '../types/befly.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 缓存助手类
|
|
11
|
+
*/
|
|
12
|
+
export class CacheHelper {
|
|
13
|
+
private befly: BeflyContext;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 构造函数
|
|
17
|
+
* @param befly - Befly 上下文
|
|
18
|
+
*/
|
|
19
|
+
constructor(befly: BeflyContext) {
|
|
20
|
+
this.befly = befly;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 缓存所有接口到 Redis
|
|
25
|
+
*/
|
|
26
|
+
async cacheApis(): Promise<void> {
|
|
27
|
+
try {
|
|
28
|
+
// 检查表是否存在
|
|
29
|
+
const tableExists = await this.befly.db.tableExists('core_api');
|
|
30
|
+
if (!tableExists) {
|
|
31
|
+
Logger.warn('⚠️ 接口表不存在,跳过接口缓存');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 从数据库查询所有接口(与 apiAll.ts 保持一致)
|
|
36
|
+
const apiList = await this.befly.db.getAll({
|
|
37
|
+
table: 'core_api',
|
|
38
|
+
fields: ['id', 'name', 'path', 'method', 'description', 'addonName', 'addonTitle'],
|
|
39
|
+
orderBy: ['addonName#ASC', 'path#ASC']
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// 缓存到 Redis
|
|
43
|
+
const result = await this.befly.redis.setObject('apis:all', apiList);
|
|
44
|
+
|
|
45
|
+
if (result === null) {
|
|
46
|
+
Logger.warn('⚠️ 接口缓存失败');
|
|
47
|
+
} else {
|
|
48
|
+
Logger.info(`✅ 已缓存 ${apiList.length} 个接口到 Redis (Key: apis:all)`);
|
|
49
|
+
}
|
|
50
|
+
} catch (error: any) {
|
|
51
|
+
Logger.error('⚠️ 接口缓存异常:', error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 缓存所有菜单到 Redis(从数据库读取)
|
|
57
|
+
*/
|
|
58
|
+
async cacheMenus(): Promise<void> {
|
|
59
|
+
try {
|
|
60
|
+
// 检查表是否存在
|
|
61
|
+
const tableExists = await this.befly.db.tableExists('core_menu');
|
|
62
|
+
if (!tableExists) {
|
|
63
|
+
Logger.warn('⚠️ 菜单表不存在,跳过菜单缓存');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 从数据库查询所有菜单
|
|
68
|
+
const menus = await this.befly.db.getAll({
|
|
69
|
+
table: 'core_menu',
|
|
70
|
+
fields: ['id', 'pid', 'name', 'path', 'icon', 'type', 'sort'],
|
|
71
|
+
orderBy: ['sort#ASC', 'id#ASC']
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 缓存到 Redis
|
|
75
|
+
const result = await this.befly.redis.setObject('menus:all', menus);
|
|
76
|
+
|
|
77
|
+
if (result === null) {
|
|
78
|
+
Logger.warn('⚠️ 菜单缓存失败');
|
|
79
|
+
} else {
|
|
80
|
+
Logger.info(`✅ 已缓存 ${menus.length} 个菜单到 Redis (Key: menus:all)`);
|
|
81
|
+
}
|
|
82
|
+
} catch (error: any) {
|
|
83
|
+
const errorMessage = error?.message || error?.toString?.() || String(error);
|
|
84
|
+
Logger.warn('⚠️ 菜单缓存异常:', errorMessage);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 缓存所有角色的接口权限到 Redis
|
|
90
|
+
*/
|
|
91
|
+
async cacheRolePermissions(): Promise<void> {
|
|
92
|
+
try {
|
|
93
|
+
// 检查表是否存在
|
|
94
|
+
const apiTableExists = await this.befly.db.tableExists('core_api');
|
|
95
|
+
const roleTableExists = await this.befly.db.tableExists('core_role');
|
|
96
|
+
|
|
97
|
+
if (!apiTableExists || !roleTableExists) {
|
|
98
|
+
Logger.warn('⚠️ 接口或角色表不存在,跳过角色权限缓存');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 查询所有角色
|
|
103
|
+
const roles = await this.befly.db.getAll({
|
|
104
|
+
table: 'core_role',
|
|
105
|
+
fields: ['id', 'code', 'apis']
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 查询所有接口(用于权限映射)
|
|
109
|
+
const allApis = await this.befly.db.getAll({
|
|
110
|
+
table: 'core_api',
|
|
111
|
+
fields: ['id', 'name', 'path', 'method', 'description', 'addonName']
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// 为每个角色缓存接口权限
|
|
115
|
+
let cachedRoles = 0;
|
|
116
|
+
for (const role of roles) {
|
|
117
|
+
if (!role.apis) continue;
|
|
118
|
+
|
|
119
|
+
// 解析角色的接口 ID 列表
|
|
120
|
+
const apiIds = role.apis
|
|
121
|
+
.split(',')
|
|
122
|
+
.map((id: string) => parseInt(id.trim()))
|
|
123
|
+
.filter((id: number) => !isNaN(id));
|
|
124
|
+
|
|
125
|
+
// 根据 ID 过滤出接口路径
|
|
126
|
+
const roleApiPaths = allApis.filter((api: any) => apiIds.includes(api.id)).map((api: any) => `${api.method}${api.path}`);
|
|
127
|
+
|
|
128
|
+
if (roleApiPaths.length === 0) continue;
|
|
129
|
+
|
|
130
|
+
// 使用 Redis Set 缓存角色权限(性能优化:SADD + SISMEMBER)
|
|
131
|
+
const redisKey = `role:apis:${role.code}`;
|
|
132
|
+
|
|
133
|
+
// 先删除旧数据
|
|
134
|
+
await this.befly.redis.del(redisKey);
|
|
135
|
+
|
|
136
|
+
// 批量添加到 Set
|
|
137
|
+
const result = await this.befly.redis.sadd(redisKey, roleApiPaths);
|
|
138
|
+
|
|
139
|
+
if (result > 0) {
|
|
140
|
+
cachedRoles++;
|
|
141
|
+
Logger.debug(` └ 角色 ${role.code}: ${result} 个接口`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
Logger.info(`✅ 已缓存 ${cachedRoles} 个角色的接口权限`);
|
|
146
|
+
} catch (error: any) {
|
|
147
|
+
const errorMessage = error?.message || error?.toString?.() || String(error);
|
|
148
|
+
Logger.warn('⚠️ 角色权限缓存异常:', errorMessage);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 缓存所有数据
|
|
154
|
+
*/
|
|
155
|
+
async cacheAll(): Promise<void> {
|
|
156
|
+
Logger.info('========== 开始缓存数据到 Redis ==========');
|
|
157
|
+
|
|
158
|
+
// 1. 缓存接口
|
|
159
|
+
await this.cacheApis();
|
|
160
|
+
|
|
161
|
+
// 2. 缓存菜单
|
|
162
|
+
await this.cacheMenus();
|
|
163
|
+
|
|
164
|
+
// 3. 缓存角色权限
|
|
165
|
+
await this.cacheRolePermissions();
|
|
166
|
+
|
|
167
|
+
Logger.info('========== 数据缓存完成 ==========\n');
|
|
168
|
+
}
|
|
169
|
+
}
|
package/lib/redisHelper.ts
CHANGED
|
@@ -14,22 +14,22 @@ import { Database } from './database.js';
|
|
|
14
14
|
const prefix = Env.REDIS_KEY_PREFIX ? `${Env.REDIS_KEY_PREFIX}:` : '';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @returns Redis 客户端实例
|
|
19
|
-
* @throws 如果客户端未初始化
|
|
17
|
+
* Redis 助手类
|
|
20
18
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
export class RedisHelper {
|
|
20
|
+
private client: RedisClient;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 构造函数
|
|
24
|
+
*/
|
|
25
|
+
constructor() {
|
|
26
|
+
const client = Database.getRedis();
|
|
27
|
+
if (!client) {
|
|
28
|
+
throw new Error('Redis 客户端未初始化,请先调用 Database.connectRedis()');
|
|
29
|
+
}
|
|
30
|
+
this.client = client;
|
|
25
31
|
}
|
|
26
|
-
return client;
|
|
27
|
-
}
|
|
28
32
|
|
|
29
|
-
/**
|
|
30
|
-
* Redis 助手对象
|
|
31
|
-
*/
|
|
32
|
-
export const RedisHelper = {
|
|
33
33
|
/**
|
|
34
34
|
* 设置对象到 Redis
|
|
35
35
|
* @param key - 键名
|
|
@@ -39,19 +39,18 @@ export const RedisHelper = {
|
|
|
39
39
|
*/
|
|
40
40
|
async setObject<T = any>(key: string, obj: T, ttl: number | null = null): Promise<string | null> {
|
|
41
41
|
try {
|
|
42
|
-
const client = getClient();
|
|
43
42
|
const data = JSON.stringify(obj);
|
|
44
43
|
const pkey = `${prefix}${key}`;
|
|
45
44
|
|
|
46
45
|
if (ttl) {
|
|
47
|
-
return await client.setex(pkey, ttl, data);
|
|
46
|
+
return await this.client.setex(pkey, ttl, data);
|
|
48
47
|
}
|
|
49
|
-
return await client.set(pkey, data);
|
|
48
|
+
return await this.client.set(pkey, data);
|
|
50
49
|
} catch (error: any) {
|
|
51
50
|
Logger.error('Redis setObject 错误', error);
|
|
52
51
|
return null;
|
|
53
52
|
}
|
|
54
|
-
}
|
|
53
|
+
}
|
|
55
54
|
|
|
56
55
|
/**
|
|
57
56
|
* 从 Redis 获取对象
|
|
@@ -60,15 +59,14 @@ export const RedisHelper = {
|
|
|
60
59
|
*/
|
|
61
60
|
async getObject<T = any>(key: string): Promise<T | null> {
|
|
62
61
|
try {
|
|
63
|
-
const client = getClient();
|
|
64
62
|
const pkey = `${prefix}${key}`;
|
|
65
|
-
const data = await client.get(pkey);
|
|
63
|
+
const data = await this.client.get(pkey);
|
|
66
64
|
return data ? JSON.parse(data) : null;
|
|
67
65
|
} catch (error: any) {
|
|
68
66
|
Logger.error('Redis getObject 错误', error);
|
|
69
67
|
return null;
|
|
70
68
|
}
|
|
71
|
-
}
|
|
69
|
+
}
|
|
72
70
|
|
|
73
71
|
/**
|
|
74
72
|
* 从 Redis 删除对象
|
|
@@ -76,13 +74,12 @@ export const RedisHelper = {
|
|
|
76
74
|
*/
|
|
77
75
|
async delObject(key: string): Promise<void> {
|
|
78
76
|
try {
|
|
79
|
-
const client = getClient();
|
|
80
77
|
const pkey = `${prefix}${key}`;
|
|
81
|
-
await client.del(pkey);
|
|
78
|
+
await this.client.del(pkey);
|
|
82
79
|
} catch (error: any) {
|
|
83
80
|
Logger.error('Redis delObject 错误', error);
|
|
84
81
|
}
|
|
85
|
-
}
|
|
82
|
+
}
|
|
86
83
|
|
|
87
84
|
/**
|
|
88
85
|
* 生成基于时间的唯一 ID
|
|
@@ -92,17 +89,16 @@ export const RedisHelper = {
|
|
|
92
89
|
* @returns 唯一 ID (14位纯数字)
|
|
93
90
|
*/
|
|
94
91
|
async genTimeID(): Promise<number> {
|
|
95
|
-
const client = getClient();
|
|
96
92
|
const timestamp = Math.floor(Date.now() / 1000); // 秒级时间戳
|
|
97
93
|
const key = `${prefix}time_id_counter:${timestamp}`;
|
|
98
94
|
|
|
99
|
-
const counter = await client.incr(key);
|
|
100
|
-
await client.expire(key, 1);
|
|
95
|
+
const counter = await this.client.incr(key);
|
|
96
|
+
await this.client.expire(key, 1);
|
|
101
97
|
|
|
102
98
|
const counterSuffix = (counter % 10000).toString().padStart(4, '0');
|
|
103
99
|
|
|
104
100
|
return Number(`${timestamp}${counterSuffix}`);
|
|
105
|
-
}
|
|
101
|
+
}
|
|
106
102
|
|
|
107
103
|
/**
|
|
108
104
|
* 批量生成基于时间的唯一 ID
|
|
@@ -121,13 +117,12 @@ export const RedisHelper = {
|
|
|
121
117
|
throw new Error(`批量大小 ${count} 超过最大限制 ${MAX_BATCH_SIZE}`);
|
|
122
118
|
}
|
|
123
119
|
|
|
124
|
-
const client = getClient();
|
|
125
120
|
const timestamp = Math.floor(Date.now() / 1000); // 秒级时间戳
|
|
126
121
|
const key = `${prefix}time_id_counter:${timestamp}`;
|
|
127
122
|
|
|
128
123
|
// 使用 INCRBY 一次性获取 N 个连续计数
|
|
129
|
-
const startCounter = await client.incrby(key, count);
|
|
130
|
-
await client.expire(key, 1);
|
|
124
|
+
const startCounter = await this.client.incrby(key, count);
|
|
125
|
+
await this.client.expire(key, 1);
|
|
131
126
|
|
|
132
127
|
// 生成 ID 数组
|
|
133
128
|
const ids: number[] = [];
|
|
@@ -138,7 +133,7 @@ export const RedisHelper = {
|
|
|
138
133
|
}
|
|
139
134
|
|
|
140
135
|
return ids;
|
|
141
|
-
}
|
|
136
|
+
}
|
|
142
137
|
|
|
143
138
|
/**
|
|
144
139
|
* 设置字符串值
|
|
@@ -148,17 +143,16 @@ export const RedisHelper = {
|
|
|
148
143
|
*/
|
|
149
144
|
async setString(key: string, value: string, ttl: number | null = null): Promise<string | null> {
|
|
150
145
|
try {
|
|
151
|
-
const client = getClient();
|
|
152
146
|
const pkey = `${prefix}${key}`;
|
|
153
147
|
if (ttl) {
|
|
154
|
-
return await client.setex(pkey, ttl, value);
|
|
148
|
+
return await this.client.setex(pkey, ttl, value);
|
|
155
149
|
}
|
|
156
|
-
return await client.set(pkey, value);
|
|
150
|
+
return await this.client.set(pkey, value);
|
|
157
151
|
} catch (error: any) {
|
|
158
152
|
Logger.error('Redis setString 错误', error);
|
|
159
153
|
return null;
|
|
160
154
|
}
|
|
161
|
-
}
|
|
155
|
+
}
|
|
162
156
|
|
|
163
157
|
/**
|
|
164
158
|
* 获取字符串值
|
|
@@ -166,14 +160,13 @@ export const RedisHelper = {
|
|
|
166
160
|
*/
|
|
167
161
|
async getString(key: string): Promise<string | null> {
|
|
168
162
|
try {
|
|
169
|
-
const client = getClient();
|
|
170
163
|
const pkey = `${prefix}${key}`;
|
|
171
|
-
return await client.get(pkey);
|
|
164
|
+
return await this.client.get(pkey);
|
|
172
165
|
} catch (error: any) {
|
|
173
166
|
Logger.error('Redis getString 错误', error);
|
|
174
167
|
return null;
|
|
175
168
|
}
|
|
176
|
-
}
|
|
169
|
+
}
|
|
177
170
|
|
|
178
171
|
/**
|
|
179
172
|
* 检查键是否存在
|
|
@@ -181,14 +174,13 @@ export const RedisHelper = {
|
|
|
181
174
|
*/
|
|
182
175
|
async exists(key: string): Promise<number> {
|
|
183
176
|
try {
|
|
184
|
-
const client = getClient();
|
|
185
177
|
const pkey = `${prefix}${key}`;
|
|
186
|
-
return await client.exists(pkey);
|
|
178
|
+
return await this.client.exists(pkey);
|
|
187
179
|
} catch (error: any) {
|
|
188
180
|
Logger.error('Redis exists 错误', error);
|
|
189
181
|
return 0;
|
|
190
182
|
}
|
|
191
|
-
}
|
|
183
|
+
}
|
|
192
184
|
|
|
193
185
|
/**
|
|
194
186
|
* 设置过期时间
|
|
@@ -197,14 +189,13 @@ export const RedisHelper = {
|
|
|
197
189
|
*/
|
|
198
190
|
async expire(key: string, seconds: number): Promise<number> {
|
|
199
191
|
try {
|
|
200
|
-
const client = getClient();
|
|
201
192
|
const pkey = `${prefix}${key}`;
|
|
202
|
-
return await client.expire(pkey, seconds);
|
|
193
|
+
return await this.client.expire(pkey, seconds);
|
|
203
194
|
} catch (error: any) {
|
|
204
195
|
Logger.error('Redis expire 错误', error);
|
|
205
196
|
return 0;
|
|
206
197
|
}
|
|
207
|
-
}
|
|
198
|
+
}
|
|
208
199
|
|
|
209
200
|
/**
|
|
210
201
|
* 获取剩余过期时间
|
|
@@ -212,14 +203,13 @@ export const RedisHelper = {
|
|
|
212
203
|
*/
|
|
213
204
|
async ttl(key: string): Promise<number> {
|
|
214
205
|
try {
|
|
215
|
-
const client = getClient();
|
|
216
206
|
const pkey = `${prefix}${key}`;
|
|
217
|
-
return await client.ttl(pkey);
|
|
207
|
+
return await this.client.ttl(pkey);
|
|
218
208
|
} catch (error: any) {
|
|
219
209
|
Logger.error('Redis ttl 错误', error);
|
|
220
210
|
return -1;
|
|
221
211
|
}
|
|
222
|
-
}
|
|
212
|
+
}
|
|
223
213
|
|
|
224
214
|
/**
|
|
225
215
|
* 向 Set 中添加一个或多个成员
|
|
@@ -231,14 +221,13 @@ export const RedisHelper = {
|
|
|
231
221
|
try {
|
|
232
222
|
if (members.length === 0) return 0;
|
|
233
223
|
|
|
234
|
-
const client = getClient();
|
|
235
224
|
const pkey = `${prefix}${key}`;
|
|
236
|
-
return await client.sadd(pkey, ...members);
|
|
225
|
+
return await this.client.sadd(pkey, ...members);
|
|
237
226
|
} catch (error: any) {
|
|
238
227
|
Logger.error('Redis sadd 错误', error);
|
|
239
228
|
return 0;
|
|
240
229
|
}
|
|
241
|
-
}
|
|
230
|
+
}
|
|
242
231
|
|
|
243
232
|
/**
|
|
244
233
|
* 判断成员是否在 Set 中
|
|
@@ -248,14 +237,13 @@ export const RedisHelper = {
|
|
|
248
237
|
*/
|
|
249
238
|
async sismember(key: string, member: string): Promise<number> {
|
|
250
239
|
try {
|
|
251
|
-
const client = getClient();
|
|
252
240
|
const pkey = `${prefix}${key}`;
|
|
253
|
-
return await client.sismember(pkey, member);
|
|
241
|
+
return await this.client.sismember(pkey, member);
|
|
254
242
|
} catch (error: any) {
|
|
255
243
|
Logger.error('Redis sismember 错误', error);
|
|
256
244
|
return 0;
|
|
257
245
|
}
|
|
258
|
-
}
|
|
246
|
+
}
|
|
259
247
|
|
|
260
248
|
/**
|
|
261
249
|
* 获取 Set 的成员数量
|
|
@@ -264,14 +252,13 @@ export const RedisHelper = {
|
|
|
264
252
|
*/
|
|
265
253
|
async scard(key: string): Promise<number> {
|
|
266
254
|
try {
|
|
267
|
-
const client = getClient();
|
|
268
255
|
const pkey = `${prefix}${key}`;
|
|
269
|
-
return await client.scard(pkey);
|
|
256
|
+
return await this.client.scard(pkey);
|
|
270
257
|
} catch (error: any) {
|
|
271
258
|
Logger.error('Redis scard 错误', error);
|
|
272
259
|
return 0;
|
|
273
260
|
}
|
|
274
|
-
}
|
|
261
|
+
}
|
|
275
262
|
|
|
276
263
|
/**
|
|
277
264
|
* 获取 Set 的所有成员
|
|
@@ -280,14 +267,13 @@ export const RedisHelper = {
|
|
|
280
267
|
*/
|
|
281
268
|
async smembers(key: string): Promise<string[]> {
|
|
282
269
|
try {
|
|
283
|
-
const client = getClient();
|
|
284
270
|
const pkey = `${prefix}${key}`;
|
|
285
|
-
return await client.smembers(pkey);
|
|
271
|
+
return await this.client.smembers(pkey);
|
|
286
272
|
} catch (error: any) {
|
|
287
273
|
Logger.error('Redis smembers 错误', error);
|
|
288
274
|
return [];
|
|
289
275
|
}
|
|
290
|
-
}
|
|
276
|
+
}
|
|
291
277
|
|
|
292
278
|
/**
|
|
293
279
|
* 删除键
|
|
@@ -296,14 +282,13 @@ export const RedisHelper = {
|
|
|
296
282
|
*/
|
|
297
283
|
async del(key: string): Promise<number> {
|
|
298
284
|
try {
|
|
299
|
-
const client = getClient();
|
|
300
285
|
const pkey = `${prefix}${key}`;
|
|
301
|
-
return await client.del(pkey);
|
|
286
|
+
return await this.client.del(pkey);
|
|
302
287
|
} catch (error: any) {
|
|
303
288
|
Logger.error('Redis del 错误', error);
|
|
304
289
|
return 0;
|
|
305
290
|
}
|
|
306
|
-
}
|
|
291
|
+
}
|
|
307
292
|
|
|
308
293
|
/**
|
|
309
294
|
* 测试 Redis 连接
|
|
@@ -311,11 +296,10 @@ export const RedisHelper = {
|
|
|
311
296
|
*/
|
|
312
297
|
async ping(): Promise<string> {
|
|
313
298
|
try {
|
|
314
|
-
|
|
315
|
-
return await client.ping();
|
|
299
|
+
return await this.client.ping();
|
|
316
300
|
} catch (error: any) {
|
|
317
301
|
Logger.error('Redis ping 错误', error);
|
|
318
302
|
throw error;
|
|
319
303
|
}
|
|
320
304
|
}
|
|
321
|
-
}
|
|
305
|
+
}
|