befly 3.0.0 → 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 (60) hide show
  1. package/checks/conflict.ts +35 -114
  2. package/checks/table.ts +31 -63
  3. package/config/env.ts +3 -3
  4. package/config/fields.ts +55 -0
  5. package/config/regexAliases.ts +51 -0
  6. package/config/reserved.ts +1 -1
  7. package/main.ts +17 -71
  8. package/package.json +7 -28
  9. package/plugins/db.ts +11 -10
  10. package/plugins/redis.ts +5 -9
  11. package/scripts/syncDb/apply.ts +3 -3
  12. package/scripts/syncDb/constants.ts +2 -1
  13. package/scripts/syncDb/ddl.ts +15 -8
  14. package/scripts/syncDb/helpers.ts +3 -2
  15. package/scripts/syncDb/index.ts +23 -35
  16. package/scripts/syncDb/state.ts +8 -6
  17. package/scripts/syncDb/table.ts +32 -22
  18. package/scripts/syncDb/tableCreate.ts +9 -3
  19. package/scripts/syncDb/tests/constants.test.ts +2 -1
  20. package/scripts/syncDb.ts +10 -9
  21. package/types/addon.d.ts +53 -0
  22. package/types/api.d.ts +17 -14
  23. package/types/befly.d.ts +2 -6
  24. package/types/context.d.ts +7 -0
  25. package/types/database.d.ts +9 -14
  26. package/types/index.d.ts +442 -8
  27. package/types/index.ts +35 -56
  28. package/types/redis.d.ts +2 -0
  29. package/types/validator.d.ts +0 -2
  30. package/types/validator.ts +43 -0
  31. package/utils/colors.ts +117 -37
  32. package/utils/database.ts +348 -0
  33. package/utils/dbHelper.ts +687 -116
  34. package/utils/helper.ts +812 -0
  35. package/utils/index.ts +10 -23
  36. package/utils/logger.ts +78 -171
  37. package/utils/redisHelper.ts +135 -152
  38. package/{types/context.ts → utils/requestContext.ts} +3 -3
  39. package/utils/sqlBuilder.ts +142 -165
  40. package/utils/validate.ts +51 -9
  41. package/apis/health/info.ts +0 -64
  42. package/apis/tool/tokenCheck.ts +0 -51
  43. package/bin/befly.ts +0 -202
  44. package/bunfig.toml +0 -3
  45. package/plugins/tool.ts +0 -34
  46. package/scripts/syncDev.ts +0 -112
  47. package/system.ts +0 -149
  48. package/tables/_common.json +0 -21
  49. package/tables/admin.json +0 -10
  50. package/utils/addonHelper.ts +0 -60
  51. package/utils/api.ts +0 -23
  52. package/utils/datetime.ts +0 -51
  53. package/utils/errorHandler.ts +0 -68
  54. package/utils/objectHelper.ts +0 -68
  55. package/utils/pluginHelper.ts +0 -62
  56. package/utils/response.ts +0 -38
  57. package/utils/sqlHelper.ts +0 -447
  58. package/utils/tableHelper.ts +0 -167
  59. package/utils/tool.ts +0 -230
  60. package/utils/typeHelper.ts +0 -101
package/utils/colors.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * 终端颜色工具 - TypeScript 版本
3
+ * 提供 ANSI 颜色和样式支持,自动检测终端颜色能力
3
4
  */
4
5
 
5
6
  /**
@@ -11,15 +12,28 @@ type Formatter = (input: string | number) => string;
11
12
  * 颜色工具接口
12
13
  */
13
14
  interface ColorsInterface {
15
+ /** 是否支持颜色 */
14
16
  isColorSupported: boolean;
17
+
18
+ // 样式
19
+ /** 重置所有样式 */
15
20
  reset: Formatter;
21
+ /** 加粗 */
16
22
  bold: Formatter;
23
+ /** 变暗 */
17
24
  dim: Formatter;
25
+ /** 斜体 */
18
26
  italic: Formatter;
27
+ /** 下划线 */
19
28
  underline: Formatter;
29
+ /** 反色 */
20
30
  inverse: Formatter;
31
+ /** 隐藏 */
21
32
  hidden: Formatter;
33
+ /** 删除线 */
22
34
  strikethrough: Formatter;
35
+
36
+ // 前景色(标准)
23
37
  black: Formatter;
24
38
  red: Formatter;
25
39
  green: Formatter;
@@ -29,6 +43,18 @@ interface ColorsInterface {
29
43
  cyan: Formatter;
30
44
  white: Formatter;
31
45
  gray: Formatter;
46
+
47
+ // 前景色(高亮)
48
+ blackBright: Formatter;
49
+ redBright: Formatter;
50
+ greenBright: Formatter;
51
+ yellowBright: Formatter;
52
+ blueBright: Formatter;
53
+ magentaBright: Formatter;
54
+ cyanBright: Formatter;
55
+ whiteBright: Formatter;
56
+
57
+ // 背景色(标准)
32
58
  bgBlack: Formatter;
33
59
  bgRed: Formatter;
34
60
  bgGreen: Formatter;
@@ -37,29 +63,71 @@ interface ColorsInterface {
37
63
  bgMagenta: Formatter;
38
64
  bgCyan: Formatter;
39
65
  bgWhite: Formatter;
40
- }
41
66
 
42
- interface Process {
43
- argv?: string[];
44
- env?: Record<string, string | undefined>;
45
- platform?: string;
46
- stdout?: { isTTY?: boolean };
67
+ // 背景色(高亮)
68
+ bgBlackBright: Formatter;
69
+ bgRedBright: Formatter;
70
+ bgGreenBright: Formatter;
71
+ bgYellowBright: Formatter;
72
+ bgBlueBright: Formatter;
73
+ bgMagentaBright: Formatter;
74
+ bgCyanBright: Formatter;
75
+ bgWhiteBright: Formatter;
76
+
77
+ // 语义化颜色(带图标)
78
+ info: Formatter;
79
+ success: Formatter;
80
+ warn: Formatter;
81
+ error: Formatter;
47
82
  }
48
83
 
49
- const p: Process = (typeof process !== 'undefined' ? process : {}) as Process;
50
- const argv = p.argv || [];
51
- const env = p.env || {};
84
+ /**
85
+ * 检测是否支持颜色
86
+ */
87
+ const detectColorSupport = (): boolean => {
88
+ const env = process.env || {};
89
+ const argv = process.argv || [];
90
+
91
+ // 明确禁用颜色
92
+ if (env.NO_COLOR || argv.includes('--no-color')) {
93
+ return false;
94
+ }
95
+
96
+ // 明确启用颜色
97
+ if (env.FORCE_COLOR || argv.includes('--color')) {
98
+ return true;
99
+ }
100
+
101
+ // 根据平台和终端环境判断
102
+ return process.platform === 'win32' || (process.stdout?.isTTY && env.TERM !== 'dumb') || !!env.CI;
103
+ };
52
104
 
53
- const isColorSupported = !(!!env.NO_COLOR || argv.includes('--no-color')) && (!!env.FORCE_COLOR || argv.includes('--color') || p.platform === 'win32' || ((p.stdout || {}).isTTY && env.TERM !== 'dumb') || !!env.CI);
105
+ const isColorSupported = detectColorSupport();
54
106
 
107
+ /**
108
+ * 创建格式化函数
109
+ * @param open - 开始 ANSI 码
110
+ * @param close - 结束 ANSI 码
111
+ * @param replace - 替换的 ANSI 码(用于嵌套)
112
+ * @returns 格式化函数
113
+ */
55
114
  const formatter =
56
115
  (open: string, close: string, replace: string = open): Formatter =>
57
116
  (input: string | number): string => {
58
- const string = '' + input;
117
+ const string = String(input);
59
118
  const index = string.indexOf(close, open.length);
119
+ // 如果字符串中包含结束码,需要处理嵌套情况
60
120
  return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
61
121
  };
62
122
 
123
+ /**
124
+ * 替换字符串中的结束码(处理嵌套颜色)
125
+ * @param string - 原始字符串
126
+ * @param close - 结束码
127
+ * @param replace - 替换码
128
+ * @param index - 起始索引
129
+ * @returns 处理后的字符串
130
+ */
63
131
  const replaceClose = (string: string, close: string, replace: string, index: number): string => {
64
132
  let result = '';
65
133
  let cursor = 0;
@@ -71,11 +139,19 @@ const replaceClose = (string: string, close: string, replace: string, index: num
71
139
  return result + string.substring(cursor);
72
140
  };
73
141
 
142
+ /**
143
+ * 创建颜色工具实例
144
+ * @param enabled - 是否启用颜色
145
+ * @returns 颜色工具对象
146
+ */
74
147
  const createColors = (enabled: boolean = isColorSupported): ColorsInterface => {
75
- const f = enabled ? formatter : (): Formatter => String as Formatter;
148
+ // 如果禁用颜色,返回直接转字符串的函数
149
+ const f = enabled ? formatter : (): Formatter => (input: string | number) => String(input);
76
150
 
77
- const baseColors = {
151
+ return {
78
152
  isColorSupported: enabled,
153
+
154
+ // 样式 - https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
79
155
  reset: f('\x1b[0m', '\x1b[0m'),
80
156
  bold: f('\x1b[1m', '\x1b[22m', '\x1b[22m\x1b[1m'),
81
157
  dim: f('\x1b[2m', '\x1b[22m', '\x1b[22m\x1b[2m'),
@@ -85,6 +161,7 @@ const createColors = (enabled: boolean = isColorSupported): ColorsInterface => {
85
161
  hidden: f('\x1b[8m', '\x1b[28m'),
86
162
  strikethrough: f('\x1b[9m', '\x1b[29m'),
87
163
 
164
+ // 前景色(标准) - 30-37
88
165
  black: f('\x1b[30m', '\x1b[39m'),
89
166
  red: f('\x1b[31m', '\x1b[39m'),
90
167
  green: f('\x1b[32m', '\x1b[39m'),
@@ -93,17 +170,9 @@ const createColors = (enabled: boolean = isColorSupported): ColorsInterface => {
93
170
  magenta: f('\x1b[35m', '\x1b[39m'),
94
171
  cyan: f('\x1b[36m', '\x1b[39m'),
95
172
  white: f('\x1b[37m', '\x1b[39m'),
96
- gray: f('\x1b[90m', '\x1b[39m'),
97
-
98
- bgBlack: f('\x1b[40m', '\x1b[49m'),
99
- bgRed: f('\x1b[41m', '\x1b[49m'),
100
- bgGreen: f('\x1b[42m', '\x1b[49m'),
101
- bgYellow: f('\x1b[43m', '\x1b[49m'),
102
- bgBlue: f('\x1b[44m', '\x1b[49m'),
103
- bgMagenta: f('\x1b[45m', '\x1b[49m'),
104
- bgCyan: f('\x1b[46m', '\x1b[49m'),
105
- bgWhite: f('\x1b[47m', '\x1b[49m'),
173
+ gray: f('\x1b[90m', '\x1b[39m'), // 实际上是 blackBright
106
174
 
175
+ // 前景色(高亮) - 90-97
107
176
  blackBright: f('\x1b[90m', '\x1b[39m'),
108
177
  redBright: f('\x1b[91m', '\x1b[39m'),
109
178
  greenBright: f('\x1b[92m', '\x1b[39m'),
@@ -113,6 +182,17 @@ const createColors = (enabled: boolean = isColorSupported): ColorsInterface => {
113
182
  cyanBright: f('\x1b[96m', '\x1b[39m'),
114
183
  whiteBright: f('\x1b[97m', '\x1b[39m'),
115
184
 
185
+ // 背景色(标准) - 40-47
186
+ bgBlack: f('\x1b[40m', '\x1b[49m'),
187
+ bgRed: f('\x1b[41m', '\x1b[49m'),
188
+ bgGreen: f('\x1b[42m', '\x1b[49m'),
189
+ bgYellow: f('\x1b[43m', '\x1b[49m'),
190
+ bgBlue: f('\x1b[44m', '\x1b[49m'),
191
+ bgMagenta: f('\x1b[45m', '\x1b[49m'),
192
+ bgCyan: f('\x1b[46m', '\x1b[49m'),
193
+ bgWhite: f('\x1b[47m', '\x1b[49m'),
194
+
195
+ // 背景色(高亮) - 100-107
116
196
  bgBlackBright: f('\x1b[100m', '\x1b[49m'),
117
197
  bgRedBright: f('\x1b[101m', '\x1b[49m'),
118
198
  bgGreenBright: f('\x1b[102m', '\x1b[49m'),
@@ -122,20 +202,20 @@ const createColors = (enabled: boolean = isColorSupported): ColorsInterface => {
122
202
  bgCyanBright: f('\x1b[106m', '\x1b[49m'),
123
203
  bgWhiteBright: f('\x1b[107m', '\x1b[49m'),
124
204
 
125
- info: '',
126
- success: '',
127
- warn: '',
128
- error: ''
205
+ // 语义化颜色(函数形式,带图标前缀)
206
+ info: (input: string | number) => f('\x1b[34m', '\x1b[39m')(`ℹ ${input}`),
207
+ success: (input: string | number) => f('\x1b[32m', '\x1b[39m')(`✓ ${input}`),
208
+ warn: (input: string | number) => f('\x1b[33m', '\x1b[39m')(`⚠ ${input}`),
209
+ error: (input: string | number) => f('\x1b[31m', '\x1b[39m')(`✖ ${input}`)
129
210
  };
130
-
131
- // 添加图标
132
- baseColors.info = baseColors.blue('i');
133
- baseColors.success = baseColors.green('√');
134
- baseColors.warn = baseColors.yellow('‼');
135
- baseColors.error = baseColors.red('x');
136
-
137
- return baseColors;
138
211
  };
139
212
 
140
- export const colors = createColors();
141
- export { colors as Colors }; // 别名导出,保持向后兼容
213
+ /**
214
+ * 默认颜色工具实例
215
+ */
216
+ export const Colors = createColors();
217
+
218
+ /**
219
+ * 导出类型
220
+ */
221
+ export type { ColorsInterface, Formatter };
@@ -0,0 +1,348 @@
1
+ /**
2
+ * 数据库统一管理工具
3
+ * 提供 Redis 和 SQL 连接的统一初始化和管理
4
+ */
5
+
6
+ import { SQL, RedisClient } from 'bun';
7
+ import { Env } from '../config/env.js';
8
+ import { Logger } from './logger.js';
9
+ import { DbHelper } from './dbHelper.js';
10
+ import { RedisHelper } from './redisHelper.js';
11
+ import type { BeflyContext } from '../types/befly.js';
12
+ import type { SqlClientOptions } from '../types/database.js';
13
+
14
+ /**
15
+ * 数据库连接实例
16
+ */
17
+ interface DatabaseConnections {
18
+ redis: RedisClient | null;
19
+ sql: any;
20
+ helper: DbHelper | null;
21
+ }
22
+
23
+ /**
24
+ * 全局连接实例
25
+ */
26
+ const connections: DatabaseConnections = {
27
+ redis: null,
28
+ sql: null,
29
+ helper: null
30
+ };
31
+
32
+ /**
33
+ * 构建 Redis 连接 URL
34
+ * @returns Redis 连接 URL
35
+ */
36
+ export function buildRedisUrl(): string {
37
+ const { REDIS_HOST, REDIS_PORT, REDIS_USERNAME, REDIS_PASSWORD, REDIS_DB } = Env;
38
+
39
+ // 构建认证部分
40
+ let auth = '';
41
+ if (REDIS_USERNAME && REDIS_PASSWORD) {
42
+ auth = `${REDIS_USERNAME}:${REDIS_PASSWORD}@`;
43
+ } else if (REDIS_PASSWORD) {
44
+ auth = `:${REDIS_PASSWORD}@`;
45
+ }
46
+
47
+ // 构建完整 URL
48
+ const url = `redis://${auth}${REDIS_HOST}:${REDIS_PORT}/${REDIS_DB}`;
49
+
50
+ return url;
51
+ }
52
+
53
+ /**
54
+ * 构建数据库连接字符串
55
+ * 根据环境变量自动构建 SQLite、PostgreSQL 或 MySQL 的连接 URL
56
+ * @returns 数据库连接字符串
57
+ * @throws 如果配置不完整或数据库类型不支持
58
+ */
59
+ export function buildDatabaseUrl(): string {
60
+ const type = Env.DB_TYPE || '';
61
+ const host = Env.DB_HOST || '';
62
+ const port = Env.DB_PORT;
63
+ const user = encodeURIComponent(Env.DB_USER || '');
64
+ const pass = encodeURIComponent(Env.DB_PASS || '');
65
+ const name = Env.DB_NAME || '';
66
+
67
+ if (!type) throw new Error('DB_TYPE 未配置');
68
+ if (!name && type !== 'sqlite') throw new Error('DB_NAME 未配置');
69
+
70
+ if (type === 'sqlite') {
71
+ // 支持内存数据库
72
+ if (!name || name === ':memory:') {
73
+ return 'sqlite://:memory:';
74
+ }
75
+ // 支持绝对路径(以 / 或盘符开头,如 /path 或 C:\path)
76
+ if (name.startsWith('/') || /^[a-zA-Z]:/.test(name)) {
77
+ return `sqlite://${name}`;
78
+ }
79
+ // 相对路径和普通文件名
80
+ return `sqlite://${name}`;
81
+ }
82
+
83
+ if (type === 'postgresql' || type === 'postgres') {
84
+ if (!host || !port) throw new Error('DB_HOST/DB_PORT 未配置');
85
+ const auth = user || pass ? `${user}:${pass}@` : '';
86
+ return `postgres://${auth}${host}:${port}/${encodeURIComponent(name)}`;
87
+ }
88
+
89
+ if (type === 'mysql') {
90
+ if (!host || !port) throw new Error('DB_HOST/DB_PORT 未配置');
91
+ const auth = user || pass ? `${user}:${pass}@` : '';
92
+ return `mysql://${auth}${host}:${port}/${encodeURIComponent(name)}`;
93
+ }
94
+
95
+ throw new Error(`不支持的 DB_TYPE: ${type}`);
96
+ }
97
+
98
+ /**
99
+ * 创建 Redis 客户端
100
+ * @returns Redis 客户端实例
101
+ * @throws 如果连接失败
102
+ */
103
+ export async function createRedisClient(): Promise<RedisClient> {
104
+ try {
105
+ const url = buildRedisUrl();
106
+ const redis = new RedisClient(url, {
107
+ // 连接超时(毫秒)
108
+ connectionTimeout: 10000,
109
+ // 空闲超时设为 0,表示永不超时
110
+ idleTimeout: 0,
111
+ // 断开连接时自动重连
112
+ autoReconnect: true,
113
+ // 最大重连次数,0 表示无限重连
114
+ maxRetries: 0,
115
+ // 断开连接时缓存命令
116
+ enableOfflineQueue: true,
117
+ // 自动管道化命令
118
+ enableAutoPipelining: true
119
+ });
120
+
121
+ // 测试连接是否成功
122
+ await redis.ping();
123
+ Logger.info('Redis 连接成功');
124
+
125
+ return redis;
126
+ } catch (error: any) {
127
+ Logger.error('Redis 连接失败', error);
128
+ throw new Error(`Redis 连接失败: ${error.message}`);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * 创建 SQL 客户端
134
+ * @param options SQL 客户端选项
135
+ * @returns SQL 客户端实例
136
+ * @throws 如果连接失败
137
+ */
138
+ export async function createSqlClient(options: SqlClientOptions = {}): Promise<any> {
139
+ const finalUrl = buildDatabaseUrl();
140
+ let sql: any = null;
141
+
142
+ if (Env.DB_TYPE === 'sqlite') {
143
+ sql = new SQL(finalUrl);
144
+ } else {
145
+ sql = new SQL({
146
+ url: finalUrl,
147
+ max: options.max ?? 1,
148
+ bigint: false,
149
+ ...options
150
+ });
151
+ }
152
+
153
+ try {
154
+ // 连接健康检查 - 添加超时机制
155
+ const timeout = options.connectionTimeout ?? 5000; // 默认5秒超时
156
+
157
+ const healthCheckPromise = (async () => {
158
+ let version = '';
159
+ if (Env.DB_TYPE === 'sqlite') {
160
+ const v = await sql`SELECT sqlite_version() AS version`;
161
+ version = v?.[0]?.version;
162
+ } else if (Env.DB_TYPE === 'postgresql' || Env.DB_TYPE === 'postgres') {
163
+ const v = await sql`SELECT version() AS version`;
164
+ version = v?.[0]?.version;
165
+ } else {
166
+ const v = await sql`SELECT VERSION() AS version`;
167
+ version = v?.[0]?.version;
168
+ }
169
+ return version;
170
+ })();
171
+
172
+ // 创建超时 Promise
173
+ const timeoutPromise = new Promise<never>((_, reject) => {
174
+ setTimeout(() => {
175
+ reject(new Error(`数据库连接超时 (${timeout}ms)`));
176
+ }, timeout);
177
+ });
178
+
179
+ // 使用 Promise.race 实现超时控制
180
+ const version = await Promise.race([healthCheckPromise, timeoutPromise]);
181
+
182
+ Logger.info(`数据库连接成功,version: ${version}`);
183
+ return sql;
184
+ } catch (error: any) {
185
+ Logger.error('数据库连接测试失败', error);
186
+
187
+ // 清理资源
188
+ try {
189
+ await sql.close();
190
+ } catch (cleanupError) {
191
+ // 忽略清理错误
192
+ }
193
+
194
+ throw error;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * 初始化数据库连接(Redis + SQL)
200
+ * @param options SQL 客户端选项
201
+ * @returns 数据库连接实例
202
+ */
203
+ export async function initDatabase(options: SqlClientOptions = {}): Promise<DatabaseConnections> {
204
+ try {
205
+ // 1. 初始化 Redis
206
+ Logger.info('正在初始化 Redis 连接...');
207
+ connections.redis = await createRedisClient();
208
+
209
+ // 2. 初始化 SQL
210
+ Logger.info('正在初始化 SQL 连接...');
211
+ connections.sql = await createSqlClient(options);
212
+
213
+ // 3. 创建 DbHelper 实例
214
+ const befly: BeflyContext = {
215
+ redis: RedisHelper, // 使用 RedisHelper 对象而不是 RedisClient
216
+ db: null as any,
217
+ tool: null as any,
218
+ logger: null as any
219
+ };
220
+ connections.helper = new DbHelper(befly, connections.sql);
221
+
222
+ Logger.info('数据库连接初始化完成(Redis + SQL + DbHelper)');
223
+
224
+ return {
225
+ redis: connections.redis,
226
+ sql: connections.sql,
227
+ helper: connections.helper
228
+ };
229
+ } catch (error: any) {
230
+ Logger.error('数据库初始化失败', error);
231
+ // 清理已创建的连接
232
+ await closeDatabase();
233
+ throw error;
234
+ }
235
+ }
236
+
237
+ /**
238
+ * 关闭所有数据库连接
239
+ */
240
+ export async function closeDatabase(): Promise<void> {
241
+ try {
242
+ // 关闭 SQL 连接
243
+ if (connections.sql) {
244
+ try {
245
+ await connections.sql.close();
246
+ Logger.info('SQL 连接已关闭');
247
+ } catch (error: any) {
248
+ Logger.warn('关闭 SQL 连接时出错:', error.message);
249
+ }
250
+ connections.sql = null;
251
+ }
252
+
253
+ // 关闭 Redis 连接
254
+ if (connections.redis) {
255
+ try {
256
+ connections.redis.close();
257
+ Logger.info('Redis 连接已关闭');
258
+ } catch (error: any) {
259
+ Logger.warn('关闭 Redis 连接时出错:', error);
260
+ }
261
+ connections.redis = null;
262
+ }
263
+
264
+ // 清理 DbHelper
265
+ connections.helper = null;
266
+ } catch (error: any) {
267
+ Logger.error('关闭数据库连接时出错', error);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * 获取 Redis 客户端
273
+ * @returns Redis 客户端实例
274
+ */
275
+ export function getRedis(): RedisClient | null {
276
+ return connections.redis;
277
+ }
278
+
279
+ /**
280
+ * 获取 SQL 客户端
281
+ * @returns SQL 客户端实例
282
+ */
283
+ export function getSql(): any {
284
+ return connections.sql;
285
+ }
286
+
287
+ /**
288
+ * 获取 DbHelper 实例
289
+ * @returns DbHelper 实例
290
+ */
291
+ export function getDbHelper(): DbHelper | null {
292
+ return connections.helper;
293
+ }
294
+
295
+ /**
296
+ * 检查数据库连接是否已初始化
297
+ * @returns 是否已初始化
298
+ */
299
+ export function isDatabaseInitialized(): boolean {
300
+ return connections.redis !== null && connections.sql !== null && connections.helper !== null;
301
+ }
302
+
303
+ /**
304
+ * 仅初始化 SQL 连接(不需要 Redis 时使用)
305
+ * @param options SQL 客户端选项
306
+ * @returns SQL 客户端和 DbHelper 实例
307
+ */
308
+ export async function initSqlOnly(options: SqlClientOptions = {}): Promise<{ sql: any; helper: DbHelper }> {
309
+ try {
310
+ Logger.info('正在初始化 SQL 连接(不含 Redis)...');
311
+ connections.sql = await createSqlClient(options);
312
+
313
+ // 创建最小化 befly 上下文(不含 redis)
314
+ const befly: BeflyContext = {
315
+ redis: null as any,
316
+ db: null as any,
317
+ tool: null as any,
318
+ logger: null as any
319
+ };
320
+ connections.helper = new DbHelper(befly, connections.sql);
321
+
322
+ Logger.info('SQL 连接初始化完成');
323
+
324
+ return {
325
+ sql: connections.sql,
326
+ helper: connections.helper
327
+ };
328
+ } catch (error: any) {
329
+ Logger.error('SQL 初始化失败', error);
330
+ throw error;
331
+ }
332
+ }
333
+
334
+ /**
335
+ * 仅初始化 Redis 连接(不需要 SQL 时使用)
336
+ * @returns Redis 客户端实例
337
+ */
338
+ export async function initRedisOnly(): Promise<RedisClient> {
339
+ try {
340
+ Logger.info('正在初始化 Redis 连接(不含 SQL)...');
341
+ connections.redis = await createRedisClient();
342
+ Logger.info('Redis 连接初始化完成');
343
+ return connections.redis;
344
+ } catch (error: any) {
345
+ Logger.error('Redis 初始化失败', error);
346
+ throw error;
347
+ }
348
+ }