befly 3.8.29 → 3.8.30

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 (52) hide show
  1. package/README.md +8 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkTable.ts +3 -2
  4. package/hooks/parser.ts +5 -3
  5. package/hooks/permission.ts +12 -5
  6. package/lib/cacheHelper.ts +76 -62
  7. package/lib/connect.ts +8 -35
  8. package/lib/dbHelper.ts +14 -11
  9. package/lib/jwt.ts +58 -437
  10. package/lib/logger.ts +76 -197
  11. package/lib/redisHelper.ts +163 -1
  12. package/lib/sqlBuilder.ts +2 -1
  13. package/lib/validator.ts +9 -8
  14. package/loader/loadApis.ts +4 -7
  15. package/loader/loadHooks.ts +2 -2
  16. package/loader/loadPlugins.ts +4 -4
  17. package/main.ts +4 -17
  18. package/package.json +9 -8
  19. package/paths.ts +0 -6
  20. package/plugins/db.ts +2 -2
  21. package/plugins/jwt.ts +5 -5
  22. package/plugins/redis.ts +1 -1
  23. package/router/api.ts +2 -2
  24. package/router/static.ts +1 -2
  25. package/sync/syncAll.ts +2 -2
  26. package/sync/syncApi.ts +10 -7
  27. package/sync/syncDb/apply.ts +1 -2
  28. package/sync/syncDb.ts +6 -10
  29. package/sync/syncDev.ts +10 -48
  30. package/sync/syncMenu.ts +11 -8
  31. package/tests/cacheHelper.test.ts +327 -0
  32. package/tests/dbHelper-columns.test.ts +5 -20
  33. package/tests/dbHelper-execute.test.ts +14 -68
  34. package/tests/fields-redis-cache.test.ts +5 -3
  35. package/tests/integration.test.ts +15 -26
  36. package/tests/jwt.test.ts +36 -94
  37. package/tests/logger.test.ts +32 -34
  38. package/tests/redisHelper.test.ts +270 -0
  39. package/tests/redisKeys.test.ts +76 -0
  40. package/tests/sync-connection.test.ts +0 -6
  41. package/tests/syncDb-constants.test.ts +12 -12
  42. package/tests/util.test.ts +5 -1
  43. package/types/befly.d.ts +2 -15
  44. package/types/common.d.ts +11 -93
  45. package/types/database.d.ts +216 -5
  46. package/types/index.ts +1 -0
  47. package/types/logger.d.ts +11 -41
  48. package/types/table.d.ts +213 -0
  49. package/hooks/_rateLimit.ts +0 -64
  50. package/lib/regexAliases.ts +0 -59
  51. package/lib/xml.ts +0 -383
  52. package/tests/xml.test.ts +0 -101
package/README.md CHANGED
@@ -37,12 +37,14 @@ bunx befly init
37
37
 
38
38
  ```typescript
39
39
  // main.ts
40
- import { Server } from 'befly';
40
+ import { Befly } from 'befly';
41
41
 
42
- await Server({
43
- name: 'My API',
44
- port: 3000
42
+ const app = new Befly({
43
+ appName: 'My API',
44
+ appPort: 3000
45
45
  });
46
+
47
+ await app.start();
46
48
  ```
47
49
 
48
50
  运行项目:
@@ -55,7 +57,7 @@ bun run main.ts
55
57
 
56
58
  ```typescript
57
59
  // apis/user/hello.ts
58
- import type { ApiRoute } from 'befly';
60
+ import type { ApiRoute } from 'befly/types/index';
59
61
 
60
62
  export default {
61
63
  name: '问候接口',
@@ -79,7 +81,7 @@ export default {
79
81
  ### TypeScript 全面支持
80
82
 
81
83
  ```typescript
82
- import type { ApiRoute, BeflyContext } from 'befly';
84
+ import type { ApiRoute, BeflyContext } from 'befly/types/index';
83
85
  import type { User } from './types/models';
84
86
 
85
87
  export default {
@@ -3,7 +3,8 @@ import { existsSync } from 'node:fs';
3
3
 
4
4
  // 外部依赖
5
5
  import { isPlainObject } from 'es-toolkit/compat';
6
- import { scanAddons, getAddonDir, addonDirExists, scanFiles } from 'befly-util';
6
+ import { scanFiles } from 'befly-shared/scanFiles';
7
+ import { scanAddons, getAddonDir, addonDirExists } from 'befly-shared/addonHelper';
7
8
 
8
9
  // 相对导入
9
10
  import { Logger } from '../lib/logger.js';
@@ -3,14 +3,15 @@ import { existsSync } from 'node:fs';
3
3
 
4
4
  // 外部依赖
5
5
  import { basename } from 'pathe';
6
- import { scanAddons, getAddonDir, scanFiles } from 'befly-util';
6
+ import { scanFiles } from 'befly-shared/scanFiles';
7
+ import { scanAddons, getAddonDir } from 'befly-shared/addonHelper';
7
8
 
8
9
  // 相对导入
9
10
  import { Logger } from '../lib/logger.js';
10
11
  import { projectTableDir } from '../paths.js';
11
12
 
12
13
  // 类型导入
13
- import type { FieldDefinition } from '../types/common.js';
14
+ import type { FieldDefinition } from 'befly-shared/types';
14
15
 
15
16
  /**
16
17
  * 表文件信息接口
package/hooks/parser.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  // 外部依赖
2
2
  import { isPlainObject, isEmpty } from 'es-toolkit/compat';
3
- import { pickFields } from 'befly-util';
3
+ import { pickFields } from 'befly-shared/pickFields';
4
+ import { XMLParser } from 'fast-xml-parser';
4
5
 
5
6
  // 相对导入
6
- import { Xml } from '../lib/xml.js';
7
7
  import { ErrorResponse } from '../util.js';
8
8
 
9
9
  // 类型导入
10
10
  import type { Hook } from '../types/hook.js';
11
11
 
12
+ const xmlParser = new XMLParser();
13
+
12
14
  /**
13
15
  * 请求参数解析钩子
14
16
  * - GET 请求:解析 URL 查询参数
@@ -43,7 +45,7 @@ const hook: Hook = {
43
45
  } else if (contentType.includes('application/xml') || contentType.includes('text/xml')) {
44
46
  // XML 格式
45
47
  const text = await ctx.req.text();
46
- const body = await Xml.parse(text);
48
+ const body = xmlParser.parse(text);
47
49
  if (isPlainObject(ctx.api.fields) && !isEmpty(ctx.api.fields)) {
48
50
  ctx.body = pickFields(body, Object.keys(ctx.api.fields));
49
51
  } else {
@@ -1,5 +1,6 @@
1
1
  // 相对导入
2
2
  import { ErrorResponse } from '../util.js';
3
+ import { RedisKeys } from 'befly-shared/redisKeys';
3
4
 
4
5
  // 类型导入
5
6
  import type { Hook } from '../types/hook.js';
@@ -35,11 +36,17 @@ const hook: Hook = {
35
36
  // 4. 角色权限检查
36
37
  let hasPermission = false;
37
38
  if (ctx.user.roleCode && befly.redis) {
38
- // 验证角色权限
39
- const apiPath = `${ctx.req.method}${new URL(ctx.req.url).pathname}`;
40
- const roleApisKey = `role:apis:${ctx.user.roleCode}`;
41
- const isMember = await befly.redis.sismember(roleApisKey, apiPath);
42
- hasPermission = isMember === 1;
39
+ try {
40
+ // 验证角色权限
41
+ const apiPath = `${ctx.req.method}${new URL(ctx.req.url).pathname}`;
42
+ const roleApisKey = RedisKeys.roleApis(ctx.user.roleCode);
43
+ const isMember = await befly.redis.sismember(roleApisKey, apiPath);
44
+ hasPermission = isMember === 1;
45
+ } catch (error) {
46
+ // Redis 异常时降级为拒绝访问
47
+ befly.logger.warn({ err: error, route: ctx.route }, 'Redis 权限检查失败');
48
+ hasPermission = false;
49
+ }
43
50
  }
44
51
 
45
52
  if (!hasPermission) {
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  import { Logger } from './logger.js';
7
+ import { RedisKeys } from 'befly-shared/redisKeys';
8
+
7
9
  import type { BeflyContext } from '../types/befly.js';
8
10
 
9
11
  /**
@@ -26,29 +28,29 @@ export class CacheHelper {
26
28
  async cacheApis(): Promise<void> {
27
29
  try {
28
30
  // 检查表是否存在
29
- const tableExists = await this.befly.db.tableExists('core_api');
31
+ const tableExists = await this.befly.db.tableExists('addon_admin_api');
30
32
  if (!tableExists) {
31
33
  Logger.warn('⚠️ 接口表不存在,跳过接口缓存');
32
34
  return;
33
35
  }
34
36
 
35
- // 从数据库查询所有接口(与 apiAll.ts 保持一致)
37
+ // 从数据库查询所有接口
36
38
  const apiList = await this.befly.db.getAll({
37
- table: 'core_api',
39
+ table: 'addon_admin_api',
38
40
  fields: ['id', 'name', 'path', 'method', 'description', 'addonName', 'addonTitle'],
39
41
  orderBy: ['addonName#ASC', 'path#ASC']
40
42
  });
41
43
 
42
44
  // 缓存到 Redis
43
- const result = await this.befly.redis.setObject('apis:all', apiList);
45
+ const result = await this.befly.redis.setObject(RedisKeys.apisAll(), apiList);
44
46
 
45
47
  if (result === null) {
46
48
  Logger.warn('⚠️ 接口缓存失败');
47
49
  } else {
48
- Logger.info(`✅ 已缓存 ${apiList.length} 个接口到 Redis (Key: apis:all)`);
50
+ Logger.info(`✅ 已缓存 ${apiList.length} 个接口到 Redis (Key: ${RedisKeys.apisAll()})`);
49
51
  }
50
52
  } catch (error: any) {
51
- Logger.error('⚠️ 接口缓存异常:', error);
53
+ Logger.error({ err: error }, '⚠️ 接口缓存异常');
52
54
  }
53
55
  }
54
56
 
@@ -58,7 +60,7 @@ export class CacheHelper {
58
60
  async cacheMenus(): Promise<void> {
59
61
  try {
60
62
  // 检查表是否存在
61
- const tableExists = await this.befly.db.tableExists('core_menu');
63
+ const tableExists = await this.befly.db.tableExists('addon_admin_menu');
62
64
  if (!tableExists) {
63
65
  Logger.warn('⚠️ 菜单表不存在,跳过菜单缓存');
64
66
  return;
@@ -66,86 +68,101 @@ export class CacheHelper {
66
68
 
67
69
  // 从数据库查询所有菜单
68
70
  const menus = await this.befly.db.getAll({
69
- table: 'core_menu',
71
+ table: 'addon_admin_menu',
70
72
  fields: ['id', 'pid', 'name', 'path', 'icon', 'type', 'sort'],
71
73
  orderBy: ['sort#ASC', 'id#ASC']
72
74
  });
73
75
 
74
76
  // 缓存到 Redis
75
- const result = await this.befly.redis.setObject('menus:all', menus);
77
+ const result = await this.befly.redis.setObject(RedisKeys.menusAll(), menus);
76
78
 
77
79
  if (result === null) {
78
80
  Logger.warn('⚠️ 菜单缓存失败');
79
81
  } else {
80
- Logger.info(`✅ 已缓存 ${menus.length} 个菜单到 Redis (Key: menus:all)`);
82
+ Logger.info(`✅ 已缓存 ${menus.length} 个菜单到 Redis (Key: ${RedisKeys.menusAll()})`);
81
83
  }
82
84
  } catch (error: any) {
83
- const errorMessage = error?.message || error?.toString?.() || String(error);
84
- Logger.warn('⚠️ 菜单缓存异常:', errorMessage);
85
+ Logger.warn({ err: error }, '⚠️ 菜单缓存异常');
85
86
  }
86
87
  }
87
88
 
88
89
  /**
89
90
  * 缓存所有角色的接口权限到 Redis
91
+ * 优化:使用 Promise.all 利用 Bun Redis 自动 pipeline 特性
90
92
  */
91
93
  async cacheRolePermissions(): Promise<void> {
92
94
  try {
93
95
  // 检查表是否存在
94
- const apiTableExists = await this.befly.db.tableExists('core_api');
95
- const roleTableExists = await this.befly.db.tableExists('core_role');
96
+ const apiTableExists = await this.befly.db.tableExists('addon_admin_api');
97
+ const roleTableExists = await this.befly.db.tableExists('addon_admin_role');
96
98
 
97
99
  if (!apiTableExists || !roleTableExists) {
98
100
  Logger.warn('⚠️ 接口或角色表不存在,跳过角色权限缓存');
99
101
  return;
100
102
  }
101
103
 
102
- // 查询所有角色
103
- const roles = await this.befly.db.getAll({
104
- table: 'core_role',
105
- fields: ['id', 'code', 'apis']
106
- });
104
+ // 并行查询角色和接口(利用自动 pipeline)
105
+ const [roles, allApis] = await Promise.all([
106
+ this.befly.db.getAll({
107
+ table: 'addon_admin_role',
108
+ fields: ['id', 'code', 'apis']
109
+ }),
110
+ this.befly.db.getAll({
111
+ table: 'addon_admin_api',
112
+ fields: ['id', 'path', 'method']
113
+ })
114
+ ]);
115
+
116
+ // 构建接口 ID -> 路径的映射(避免重复过滤)
117
+ const apiMap = new Map<number, string>();
118
+ for (const api of allApis) {
119
+ apiMap.set(api.id, `${api.method}${api.path}`);
120
+ }
107
121
 
108
- // 查询所有接口(用于权限映射)
109
- const allApis = await this.befly.db.getAll({
110
- table: 'core_api',
111
- fields: ['id', 'name', 'path', 'method', 'description', 'addonName']
112
- });
122
+ // 收集需要缓存的角色权限
123
+ const cacheOperations: Array<{ roleCode: string; apiPaths: string[] }> = [];
113
124
 
114
- // 为每个角色缓存接口权限
115
- let cachedRoles = 0;
116
125
  for (const role of roles) {
117
126
  if (!role.apis) continue;
118
127
 
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}`);
128
+ // 解析角色的接口 ID 列表并映射到路径
129
+ const apiPaths: string[] = [];
130
+ const apiIds = role.apis.split(',');
131
+
132
+ for (const idStr of apiIds) {
133
+ const id = parseInt(idStr.trim());
134
+ if (!isNaN(id)) {
135
+ const path = apiMap.get(id);
136
+ if (path) {
137
+ apiPaths.push(path);
138
+ }
139
+ }
140
+ }
127
141
 
128
- if (roleApiPaths.length === 0) continue;
142
+ if (apiPaths.length > 0) {
143
+ cacheOperations.push({ roleCode: role.code, apiPaths: apiPaths });
144
+ }
145
+ }
129
146
 
130
- // 使用 Redis Set 缓存角色权限(性能优化:SADD + SISMEMBER)
131
- const redisKey = `role:apis:${role.code}`;
147
+ if (cacheOperations.length === 0) {
148
+ Logger.info('✅ 没有需要缓存的角色权限');
149
+ return;
150
+ }
132
151
 
133
- // 先删除旧数据
134
- await this.befly.redis.del(redisKey);
152
+ // 批量删除旧缓存(利用自动 pipeline)
153
+ const deletePromises = cacheOperations.map((op) => this.befly.redis.del(RedisKeys.roleApis(op.roleCode)));
154
+ await Promise.all(deletePromises);
135
155
 
136
- // 批量添加到 Set
137
- const result = await this.befly.redis.sadd(redisKey, roleApiPaths);
156
+ // 批量添加新缓存(利用自动 pipeline)
157
+ const addPromises = cacheOperations.map((op) => this.befly.redis.sadd(RedisKeys.roleApis(op.roleCode), op.apiPaths));
158
+ const results = await Promise.all(addPromises);
138
159
 
139
- if (result > 0) {
140
- cachedRoles++;
141
- Logger.debug(` └ 角色 ${role.code}: ${result} 个接口`);
142
- }
143
- }
160
+ // 统计成功缓存的角色数
161
+ const cachedRoles = results.filter((r) => r > 0).length;
144
162
 
145
- Logger.info(`✅ 已缓存 ${cachedRoles} 个角色的接口权限`);
163
+ Logger.info(`✅ 已缓存 ${cachedRoles} 个角色的接口权限(共 ${cacheOperations.reduce((sum, op) => sum + op.apiPaths.length, 0)} 个接口)`);
146
164
  } catch (error: any) {
147
- const errorMessage = error?.message || error?.toString?.() || String(error);
148
- Logger.warn('⚠️ 角色权限缓存异常:', errorMessage);
165
+ Logger.warn({ err: error }, '⚠️ 角色权限缓存异常');
149
166
  }
150
167
  }
151
168
 
@@ -169,10 +186,10 @@ export class CacheHelper {
169
186
  */
170
187
  async getApis(): Promise<any[]> {
171
188
  try {
172
- const apis = await this.befly.redis.getObject<any[]>('apis:all');
189
+ const apis = await this.befly.redis.getObject<any[]>(RedisKeys.apisAll());
173
190
  return apis || [];
174
191
  } catch (error: any) {
175
- Logger.error('获取接口缓存失败:', error);
192
+ Logger.error({ err: error }, '获取接口缓存失败');
176
193
  return [];
177
194
  }
178
195
  }
@@ -183,10 +200,10 @@ export class CacheHelper {
183
200
  */
184
201
  async getMenus(): Promise<any[]> {
185
202
  try {
186
- const menus = await this.befly.redis.getObject<any[]>('menus:all');
203
+ const menus = await this.befly.redis.getObject<any[]>(RedisKeys.menusAll());
187
204
  return menus || [];
188
205
  } catch (error: any) {
189
- Logger.error('获取菜单缓存失败:', error);
206
+ Logger.error({ err: error }, '获取菜单缓存失败');
190
207
  return [];
191
208
  }
192
209
  }
@@ -198,11 +215,10 @@ export class CacheHelper {
198
215
  */
199
216
  async getRolePermissions(roleCode: string): Promise<string[]> {
200
217
  try {
201
- const redisKey = `role:apis:${roleCode}`;
202
- const permissions = await this.befly.redis.smembers(redisKey);
218
+ const permissions = await this.befly.redis.smembers(RedisKeys.roleApis(roleCode));
203
219
  return permissions || [];
204
220
  } catch (error: any) {
205
- Logger.error(`获取角色 ${roleCode} 权限缓存失败:`, error);
221
+ Logger.error({ err: error, roleCode: roleCode }, '获取角色权限缓存失败');
206
222
  return [];
207
223
  }
208
224
  }
@@ -215,11 +231,10 @@ export class CacheHelper {
215
231
  */
216
232
  async checkRolePermission(roleCode: string, apiPath: string): Promise<boolean> {
217
233
  try {
218
- const redisKey = `role:apis:${roleCode}`;
219
- const result = await this.befly.redis.sismember(redisKey, apiPath);
234
+ const result = await this.befly.redis.sismember(RedisKeys.roleApis(roleCode), apiPath);
220
235
  return result === 1;
221
236
  } catch (error: any) {
222
- Logger.error(`检查角色 ${roleCode} 权限失败:`, error);
237
+ Logger.error({ err: error, roleCode: roleCode }, '检查角色权限失败');
223
238
  return false;
224
239
  }
225
240
  }
@@ -231,15 +246,14 @@ export class CacheHelper {
231
246
  */
232
247
  async deleteRolePermissions(roleCode: string): Promise<boolean> {
233
248
  try {
234
- const redisKey = `role:apis:${roleCode}`;
235
- const result = await this.befly.redis.del(redisKey);
249
+ const result = await this.befly.redis.del(RedisKeys.roleApis(roleCode));
236
250
  if (result > 0) {
237
251
  Logger.info(`✅ 已删除角色 ${roleCode} 的权限缓存`);
238
252
  return true;
239
253
  }
240
254
  return false;
241
255
  } catch (error: any) {
242
- Logger.error(`删除角色 ${roleCode} 权限缓存失败:`, error);
256
+ Logger.error({ err: error, roleCode: roleCode }, '删除角色权限缓存失败');
243
257
  return false;
244
258
  }
245
259
  }
package/lib/connect.ts CHANGED
@@ -4,10 +4,10 @@
4
4
  */
5
5
 
6
6
  import { SQL, RedisClient } from 'bun';
7
+
7
8
  import { Logger } from './logger.js';
8
- import { DbHelper } from './dbHelper.js';
9
- import { RedisHelper } from './redisHelper.js';
10
- import type { BeflyContext, BeflyOptions, DatabaseConfig, RedisConfig } from '../types/befly.js';
9
+
10
+ import type { BeflyOptions, DatabaseConfig, RedisConfig } from '../types/befly.js';
11
11
  import type { SqlClientOptions } from '../types/database.js';
12
12
 
13
13
  /**
@@ -17,7 +17,6 @@ import type { SqlClientOptions } from '../types/database.js';
17
17
  export class Connect {
18
18
  private static sqlClient: SQL | null = null;
19
19
  private static redisClient: RedisClient | null = null;
20
- private static dbHelper: DbHelper | null = null;
21
20
 
22
21
  // 连接统计信息
23
22
  private static sqlConnectedAt: number | null = null;
@@ -95,7 +94,7 @@ export class Connect {
95
94
  this.sqlPoolMax = config.poolMax ?? 1;
96
95
  return sql;
97
96
  } catch (error: any) {
98
- Logger.error('[Connect] SQL 连接失败', error);
97
+ Logger.error({ err: error }, '[Connect] SQL 连接失败');
99
98
  try {
100
99
  await sql?.close();
101
100
  } catch (cleanupError) {}
@@ -112,15 +111,11 @@ export class Connect {
112
111
  try {
113
112
  await this.sqlClient.close();
114
113
  } catch (error: any) {
115
- Logger.error('[Connect] 关闭 SQL 连接时出错', error);
114
+ Logger.error({ err: error }, '[Connect] 关闭 SQL 连接时出错');
116
115
  }
117
116
  this.sqlClient = null;
118
117
  this.sqlConnectedAt = null;
119
118
  }
120
-
121
- if (this.dbHelper) {
122
- this.dbHelper = null;
123
- }
124
119
  }
125
120
 
126
121
  /**
@@ -134,27 +129,6 @@ export class Connect {
134
129
  return this.sqlClient;
135
130
  }
136
131
 
137
- /**
138
- * 获取 DbHelper 实例
139
- * @throws 如果未连接则抛出错误
140
- */
141
- static getDbHelper(befly?: BeflyContext): DbHelper {
142
- if (!this.dbHelper) {
143
- if (!this.sqlClient) {
144
- throw new Error('SQL 客户端未连接,请先调用 Connect.connectSql()');
145
- }
146
- // 创建临时 befly 上下文(仅用于 DbHelper)
147
- const ctx = befly || {
148
- redis: new RedisHelper(),
149
- db: null as any,
150
- tool: null as any,
151
- logger: null as any
152
- };
153
- this.dbHelper = new DbHelper(ctx, this.sqlClient);
154
- }
155
- return this.dbHelper;
156
- }
157
-
158
132
  // ========================================
159
133
  // Redis 连接管理
160
134
  // ========================================
@@ -197,7 +171,7 @@ export class Connect {
197
171
  this.redisConnectedAt = Date.now();
198
172
  return redis;
199
173
  } catch (error: any) {
200
- Logger.error('[Connect] Redis 连接失败', error);
174
+ Logger.error({ err: error }, '[Connect] Redis 连接失败');
201
175
  throw new Error(`Redis 连接失败: ${error.message}`);
202
176
  }
203
177
  }
@@ -211,7 +185,7 @@ export class Connect {
211
185
  this.redisClient.close();
212
186
  this.redisConnectedAt = null;
213
187
  } catch (error: any) {
214
- Logger.error('[Connect] 关闭 Redis 连接时出错', error);
188
+ Logger.error({ err: error }, '[Connect] 关闭 Redis 连接时出错');
215
189
  }
216
190
  this.redisClient = null;
217
191
  }
@@ -250,7 +224,7 @@ export class Connect {
250
224
  const redisConfig = config?.redis || {};
251
225
  await this.connectRedis(redisConfig);
252
226
  } catch (error: any) {
253
- Logger.error('数据库初始化失败', error);
227
+ Logger.error({ err: error }, '数据库初始化失败');
254
228
  await this.disconnect();
255
229
  throw error;
256
230
  }
@@ -330,7 +304,6 @@ export class Connect {
330
304
  static __reset(): void {
331
305
  this.sqlClient = null;
332
306
  this.redisClient = null;
333
- this.dbHelper = null;
334
307
  this.sqlConnectedAt = null;
335
308
  this.redisConnectedAt = null;
336
309
  this.sqlPoolMax = 1;
package/lib/dbHelper.ts CHANGED
@@ -5,8 +5,12 @@
5
5
 
6
6
  import { snakeCase } from 'es-toolkit/string';
7
7
  import { SqlBuilder } from './sqlBuilder.js';
8
- import { keysToCamel, arrayKeysToCamel, keysToSnake, fieldClear } from 'befly-util';
9
- import { Logger } from '../lib/logger.js';
8
+ import { keysToCamel } from 'befly-shared/keysToCamel';
9
+ import { arrayKeysToCamel } from 'befly-shared/arrayKeysToCamel';
10
+ import { keysToSnake } from 'befly-shared/keysToSnake';
11
+ import { fieldClear } from 'befly-shared/fieldClear';
12
+ import { RedisTTL, RedisKeys } from 'befly-shared/redisKeys';
13
+ import { Logger } from './logger.js';
10
14
  import type { WhereConditions } from '../types/common.js';
11
15
  import type { BeflyContext } from '../types/befly.js';
12
16
  import type { QueryOptions, InsertOptions, UpdateOptions, DeleteOptions, ListResult, TransactionCallback } from '../types/database.js';
@@ -81,7 +85,7 @@ export class DbHelper {
81
85
  */
82
86
  private async getTableColumns(table: string): Promise<string[]> {
83
87
  // 1. 先查 Redis 缓存
84
- const cacheKey = `table:columns:${table}`;
88
+ const cacheKey = RedisKeys.tableColumns(table);
85
89
  let columns = await this.befly.redis.getObject<string[]>(cacheKey);
86
90
 
87
91
  if (columns && columns.length > 0) {
@@ -98,8 +102,8 @@ export class DbHelper {
98
102
 
99
103
  columns = result.map((row: any) => row.Field);
100
104
 
101
- // 3. 写入 Redis 缓存(1小时过期)
102
- await this.befly.redis.setObject(cacheKey, columns, 3600);
105
+ // 3. 写入 Redis 缓存
106
+ await this.befly.redis.setObject(cacheKey, columns, RedisTTL.tableColumns);
103
107
 
104
108
  return columns;
105
109
  }
@@ -507,12 +511,12 @@ export class DbHelper {
507
511
 
508
512
  // 警告日志:返回数据超过警告阈值
509
513
  if (result.length >= WARNING_LIMIT) {
510
- Logger.warn(`⚠️ getAll 从表 \`${options.table}\` 返回了 ${result.length} 行数据,建议使用 getList 分页查询以获得更好的性能。`);
514
+ Logger.warn({ table: options.table, count: result.length }, 'getAll 返回数据过多,建议使用 getList 分页查询');
511
515
  }
512
516
 
513
517
  // 如果达到上限,额外警告
514
518
  if (result.length >= MAX_LIMIT) {
515
- Logger.warn(`🚨 getAll 达到了最大限制 (${MAX_LIMIT}),可能还有更多数据。请使用 getList 分页查询。`);
519
+ Logger.warn({ table: options.table, limit: MAX_LIMIT }, 'getAll 达到最大限制,可能还有更多数据');
516
520
  }
517
521
 
518
522
  // 字段名转换:下划线 → 小驼峰
@@ -625,8 +629,7 @@ export class DbHelper {
625
629
  await this.executeWithConn(sql, params);
626
630
  return ids;
627
631
  } catch (error: any) {
628
- // 批量插入失败,记录错误
629
- Logger.error(`表 \`${table}\` 批量插入失败`, error);
632
+ Logger.error({ err: error, table: table }, '批量插入失败');
630
633
  throw error;
631
634
  }
632
635
  }
@@ -759,7 +762,7 @@ export class DbHelper {
759
762
  await conn.query('COMMIT');
760
763
  committed = true;
761
764
  } catch (commitError: any) {
762
- Logger.error('事务提交失败,正在回滚', commitError);
765
+ Logger.error({ err: commitError }, '事务提交失败,正在回滚');
763
766
  await conn.query('ROLLBACK');
764
767
  throw new Error(`事务提交失败: ${commitError.message}`);
765
768
  }
@@ -772,7 +775,7 @@ export class DbHelper {
772
775
  await conn.query('ROLLBACK');
773
776
  Logger.warn('事务已回滚');
774
777
  } catch (rollbackError: any) {
775
- Logger.error('事务回滚失败', rollbackError);
778
+ Logger.error({ err: rollbackError }, '事务回滚失败');
776
779
  }
777
780
  }
778
781
  throw error;