befly 3.0.0 → 3.1.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
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 内置正则表达式别名
3
+ *
4
+ * 使用方式:在字段定义的 regex 位置使用 @别名 格式
5
+ * 例如:'字段名|array_text|null|null|null|0|@number'
6
+ */
7
+
8
+ export const RegexAliases = {
9
+ // 数字类型
10
+ number: '^\\d+$', // 纯数字
11
+ integer: '^-?\\d+$', // 整数(含负数)
12
+ float: '^-?\\d+(\\.\\d+)?$', // 浮点数
13
+ positive: '^[1-9]\\d*$', // 正整数(不含0)
14
+
15
+ // 字符串类型
16
+ word: '^[a-zA-Z]+$', // 纯字母
17
+ alphanumeric: '^[a-zA-Z0-9]+$', // 字母+数字
18
+ alphanumeric_: '^[a-zA-Z0-9_]+$', // 字母+数字+下划线
19
+ lowercase: '^[a-z]+$', // 小写字母
20
+ uppercase: '^[A-Z]+$', // 大写字母
21
+
22
+ // 中文
23
+ chinese: '^[\\u4e00-\\u9fa5]+$', // 纯中文
24
+ chinese_word: '^[\\u4e00-\\u9fa5a-zA-Z]+$', // 中文+字母
25
+
26
+ // 常用格式
27
+ email: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$', // 邮箱
28
+ phone: '^1[3-9]\\d{9}$', // 中国手机号
29
+ url: '^https?://', // URL
30
+ ip: '^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$', // IPv4
31
+
32
+ // 特殊格式
33
+ uuid: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', // UUID
34
+ hex: '^[0-9a-fA-F]+$', // 十六进制
35
+ base64: '^[A-Za-z0-9+/=]+$', // Base64
36
+
37
+ // 日期时间
38
+ date: '^\\d{4}-\\d{2}-\\d{2}$', // YYYY-MM-DD
39
+ time: '^\\d{2}:\\d{2}:\\d{2}$', // HH:MM:SS
40
+ datetime: '^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}', // ISO 8601
41
+
42
+ // 代码相关
43
+ variable: '^[a-zA-Z_][a-zA-Z0-9_]*$', // 变量名
44
+ constant: '^[A-Z][A-Z0-9_]*$', // 常量名(大写)
45
+
46
+ // 空值
47
+ empty: '^$', // 空字符串
48
+ notempty: '.+' // 非空
49
+ } as const;
50
+
51
+ export type RegexAliasName = keyof typeof RegexAliases;
@@ -25,7 +25,7 @@ export const RESERVED_NAMES = {
25
25
  /**
26
26
  * 禁止用作 addon 名称
27
27
  */
28
- addonNames: ['core', 'system', 'admin', 'sys', 'befly', 'app', 'api', 'config', 'utils', 'types']
28
+ addonNames: ['app', 'api']
29
29
  } as const;
30
30
 
31
31
  /**
package/main.ts CHANGED
@@ -1,101 +1,47 @@
1
1
  /**
2
- * Befly 框架主入口文件 - TypeScript 版本
3
- * 提供完整的框架功能:检查系统、加载插件、加载 API、启动 HTTP 服务器
2
+ * Befly 框架主入口文件
3
+ * 提供简洁的框架接口,核心逻辑已提取到 lifecycle
4
4
  */
5
5
 
6
6
  import { Env } from './config/env.js';
7
- import { Api } from './utils/api.js';
8
- import { Yes, No } from './utils/index.js';
7
+ import { Fields } from './config/fields.js';
8
+ import { Yes, No, cleanData } from './utils/index.js';
9
9
  import { Logger } from './utils/logger.js';
10
10
  import { Jwt } from './utils/jwt.js';
11
11
  import { Validator } from './utils/validate.js';
12
12
  import { Crypto2 } from './utils/crypto.js';
13
- import { calcPerfTime } from './utils/index.js';
14
- import { scanAddons, hasAddonDir } from './utils/addonHelper.js';
15
- import { Checker } from './lifecycle/checker.js';
16
- import { Loader } from './lifecycle/loader.js';
17
- import { Bootstrap } from './lifecycle/bootstrap.js';
13
+ import { DbHelper } from './utils/dbHelper.js';
14
+ import { createSqlClient, getRedis, getSql, getDbHelper, initDatabase, closeDatabase } from './utils/database.js';
15
+ import { RedisHelper } from './utils/redisHelper.js';
16
+ import { Lifecycle } from './lifecycle/lifecycle.js';
18
17
 
19
18
  import type { Server } from 'bun';
20
- import type { Plugin } from './types/plugin.js';
21
- import type { ApiRoute, ApiHandler } from './types/api.js';
22
- import type { BeflyContext, BeflyOptions, RequestContext } from './types/befly.js';
19
+ import type { BeflyContext, BeflyOptions } from './types/befly.js';
23
20
 
24
21
  /**
25
22
  * Befly 框架核心类
23
+ * 职责:管理应用上下文和生命周期
26
24
  */
27
25
  export class Befly {
28
- /** API 路由映射表 */
29
- private apiRoutes: Map<string, ApiRoute>;
30
-
31
- /** 插件列表 */
32
- private pluginLists: Plugin[];
26
+ /** 生命周期管理器 */
27
+ private lifecycle: Lifecycle;
33
28
 
34
29
  /** 应用上下文 */
35
30
  public appContext: BeflyContext;
36
31
 
37
- /** 应用配置选项 */
38
- private appOptions: BeflyOptions;
39
-
40
- // 原构造函数被替换
41
32
  constructor(options: BeflyOptions = {}) {
42
- this.apiRoutes = new Map();
43
- this.pluginLists = [];
33
+ this.lifecycle = new Lifecycle(options);
44
34
  this.appContext = {};
45
- this.appOptions = options;
46
- }
47
-
48
- /**
49
- * 系统检查(已提取到 lifecycle/checker.ts)
50
- */
51
- async initCheck(): Promise<void> {
52
- await Checker.run();
53
- }
54
-
55
- /**
56
- * 加载插件(已提取到 lifecycle/loader.ts)
57
- */
58
- async loadPlugins(): Promise<void> {
59
- await Loader.loadPlugins(this);
60
- }
61
-
62
- /**
63
- * 加载API路由(已提取到 lifecycle/loader.ts)
64
- */
65
- async loadApis(dirName: string, options?: { isAddon?: boolean; addonName?: string }): Promise<void> {
66
- await Loader.loadApis(dirName, this.apiRoutes, options);
67
35
  }
68
36
 
69
37
  /**
70
38
  * 启动服务器
39
+ * @param callback - 启动完成后的回调函数
71
40
  */
72
41
  async listen(callback?: (server: Server) => void): Promise<void> {
73
- const serverStartTime = Bun.nanoseconds();
74
- Logger.info('开始启动 Befly 服务器...');
75
-
76
- // 执行启动前的检查和加载
77
- await this.initCheck();
78
- await this.loadPlugins();
79
- await this.loadApis('core');
80
-
81
- // 加载 addon APIs
82
- const addons = scanAddons();
83
- for (const addon of addons) {
84
- if (hasAddonDir(addon, 'apis')) {
85
- await this.loadApis(addon, { isAddon: true, addonName: addon });
86
- }
87
- }
88
-
89
- await this.loadApis('app');
90
-
91
- const totalStartupTime = calcPerfTime(serverStartTime);
92
- Logger.info(`服务器启动准备完成,总耗时: ${totalStartupTime}`);
93
-
94
- // 启动HTTP服务器(路由处理逻辑已提取到 router/*.ts)
95
- await Bootstrap.start(this, callback);
42
+ await this.lifecycle.start(this.appContext, callback);
96
43
  }
97
44
  }
98
45
 
99
- // 核心类已通过 export class 导出
100
- export { Env, Api, Jwt, Validator, Crypto2, Logger, Yes, No };
101
- export type { ApiOptions, FieldRules } from './types/api.js';
46
+ // 核心类和工具导出(只导出运行时代码,不导出类型)
47
+ export { Env, Fields, Jwt, Validator, Crypto2, Logger, Yes, No, cleanData, DbHelper, createSqlClient, RedisHelper, getRedis, getSql, getDbHelper, initDatabase, closeDatabase };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -9,37 +9,19 @@
9
9
  "registry": "https://registry.npmjs.org"
10
10
  },
11
11
  "main": "main.ts",
12
- "types": "main.ts",
12
+ "types": "./types/index.d.ts",
13
13
  "exports": {
14
14
  ".": {
15
- "types": "./main.ts",
16
15
  "default": "./main.ts"
17
16
  },
18
17
  "./types": {
19
- "types": "./types/index.ts",
20
- "default": "./types/index.ts"
21
- },
22
- "./types/*": {
23
- "types": "./types/*.ts",
24
- "default": "./types/*.ts"
25
- },
26
- "./utils": {
27
- "types": "./utils/index.ts",
28
- "default": "./utils/index.ts"
29
- },
30
- "./utils/*": {
31
- "types": "./utils/*.ts",
32
- "default": "./utils/*.ts"
33
- },
34
- "./config": {
35
- "types": "./config/env.ts",
36
- "default": "./config/env.ts"
18
+ "types": "./types/index.d.ts",
19
+ "default": "./types/index.d.ts"
37
20
  }
38
21
  },
39
- "bin": {
40
- "befly": "./bin/befly.ts"
22
+ "scripts": {
23
+ "bundler": "bun build ./main.ts --outfile ./main.single.ts --minify --target bun"
41
24
  },
42
- "scripts": {},
43
25
  "keywords": [
44
26
  "bun",
45
27
  "api",
@@ -56,7 +38,6 @@
56
38
  "homepage": "https://chensuiyi.me",
57
39
  "license": "Apache-2.0",
58
40
  "files": [
59
- "bin/",
60
41
  "apis/",
61
42
  "checks/",
62
43
  "config/",
@@ -85,7 +66,5 @@
85
66
  "engines": {
86
67
  "bun": ">=1.3.0"
87
68
  },
88
- "devDependencies": {
89
- "@types/bun": "latest"
90
- }
69
+ "gitHead": "ac46f8ed275518b2494b47323df5ed37feb6fb7d"
91
70
  }
package/plugins/db.ts CHANGED
@@ -5,8 +5,8 @@
5
5
 
6
6
  import { Env } from '../config/env.js';
7
7
  import { Logger } from '../utils/logger.js';
8
- import { createSqlClient } from '../utils/index.js';
9
- import { SqlHelper } from '../utils/sqlHelper.js';
8
+ import { createSqlClient } from '../utils/database.js';
9
+ import { DbHelper } from '../utils/dbHelper.js';
10
10
  import type { Plugin } from '../types/plugin.js';
11
11
  import type { BeflyContext } from '../types/befly.js';
12
12
 
@@ -17,16 +17,21 @@ const dbPlugin: Plugin = {
17
17
  name: '_db',
18
18
  after: ['_redis'],
19
19
 
20
- async onInit(befly: BeflyContext): Promise<SqlHelper | Record<string, never>> {
20
+ async onInit(befly: BeflyContext): Promise<DbHelper | Record<string, never>> {
21
21
  let sql: any = null;
22
22
 
23
23
  try {
24
24
  if (Env.DB_ENABLE === 1) {
25
25
  // 创建 Bun SQL 客户端(内置连接池),并确保连接验证成功后再继续
26
- sql = await createSqlClient();
26
+ // 从环境变量读取连接超时配置
27
+ const connectionTimeout = process.env.DB_CONNECTION_TIMEOUT ? parseInt(process.env.DB_CONNECTION_TIMEOUT) : 5000;
28
+
29
+ sql = await createSqlClient({
30
+ connectionTimeout
31
+ });
27
32
 
28
33
  // 创建数据库管理器实例,直接传入 sql 对象
29
- const dbManager = new SqlHelper(befly, sql);
34
+ const dbManager = new DbHelper(befly, sql);
30
35
 
31
36
  Logger.info('数据库插件初始化成功');
32
37
  return dbManager;
@@ -35,11 +40,7 @@ const dbPlugin: Plugin = {
35
40
  return {};
36
41
  }
37
42
  } catch (error: any) {
38
- Logger.error({
39
- msg: '数据库初始化失败',
40
- message: error.message,
41
- stack: error.stack
42
- });
43
+ Logger.error('数据库初始化失败', error);
43
44
 
44
45
  // 清理资源
45
46
  if (sql) {
package/plugins/redis.ts CHANGED
@@ -5,7 +5,8 @@
5
5
 
6
6
  import { Env } from '../config/env.js';
7
7
  import { Logger } from '../utils/logger.js';
8
- import { RedisHelper, initRedisClient, getRedisClient } from '../utils/redisHelper.js';
8
+ import { RedisHelper } from '../utils/redisHelper.js';
9
+ import { initRedisOnly } from '../utils/database.js';
9
10
  import type { Plugin } from '../types/plugin.js';
10
11
  import type { BeflyContext } from '../types/befly.js';
11
12
 
@@ -19,8 +20,8 @@ const redisPlugin: Plugin = {
19
20
  async onInit(befly: BeflyContext): Promise<typeof RedisHelper | Record<string, never>> {
20
21
  try {
21
22
  if (Env.REDIS_ENABLE === 1) {
22
- // 初始化 Redis 客户端(带连接测试)
23
- await initRedisClient();
23
+ // 初始化 Redis 客户端(统一使用 database.ts 的连接管理)
24
+ await initRedisOnly();
24
25
 
25
26
  Logger.info('Redis 插件初始化成功', {
26
27
  host: Env.REDIS_HOST,
@@ -35,12 +36,7 @@ const redisPlugin: Plugin = {
35
36
  return {};
36
37
  }
37
38
  } catch (error: any) {
38
- Logger.error({
39
- msg: 'Redis 初始化失败',
40
- message: error.message,
41
- code: error.code,
42
- stack: error.stack
43
- });
39
+ Logger.error('Redis 初始化失败', error);
44
40
 
45
41
  // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
46
42
  throw error;
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { Logger } from '../../utils/logger.js';
10
- import { parseRule } from '../../utils/tableHelper.js';
10
+ import { parseRule } from '../../utils/helper.js';
11
11
  import { IS_MYSQL, IS_PG, IS_SQLITE, CHANGE_TYPE_LABELS, typeMapping } from './constants.js';
12
12
  import { logFieldChange, resolveDefaultValue, isStringOrArrayType } from './helpers.js';
13
13
  import { executeDDLSafely, buildIndexSQL } from './ddl.js';
@@ -151,8 +151,8 @@ export async function applyTablePlan(sql: SQL, tableName: string, fields: Record
151
151
  Logger.info(`[索引变化] 删除索引 ${tableName}.${act.indexName} (${act.fieldName})`);
152
152
  }
153
153
  } catch (error: any) {
154
- Logger.error(`${act.action === 'create' ? '创建' : '删除'}索引失败: ${error.message}`);
155
- Logger.error(`表名: ${tableName}, 索引名: ${act.indexName}, 字段: ${act.fieldName}`);
154
+ Logger.error(`${act.action === 'create' ? '创建' : '删除'}索引失败:`, error);
155
+ Logger.warn(`表名: ${tableName}, 索引名: ${act.indexName}, 字段: ${act.fieldName}`);
156
156
  throw error;
157
157
  }
158
158
  }
@@ -66,5 +66,6 @@ export const typeMapping = {
66
66
  number: IS_SQLITE ? 'INTEGER' : 'BIGINT',
67
67
  string: IS_SQLITE ? 'TEXT' : IS_PG ? 'character varying' : 'VARCHAR',
68
68
  text: IS_MYSQL ? 'MEDIUMTEXT' : 'TEXT',
69
- array: IS_SQLITE ? 'TEXT' : IS_PG ? 'character varying' : 'VARCHAR'
69
+ array_string: IS_SQLITE ? 'TEXT' : IS_PG ? 'character varying' : 'VARCHAR',
70
+ array_text: IS_MYSQL ? 'MEDIUMTEXT' : 'TEXT'
70
71
  };
@@ -9,8 +9,9 @@
9
9
  */
10
10
 
11
11
  import { Logger } from '../../utils/logger.js';
12
- import { parseRule } from '../../utils/tableHelper.js';
13
- import { IS_MYSQL, IS_PG, IS_SQLITE, SYSTEM_INDEX_FIELDS, typeMapping } from './constants.js';
12
+ import { parseRule, toSnakeCase } from '../../utils/helper.js';
13
+ import type { ParsedFieldRule, AnyObject } from '../../types/common.js';
14
+ import { IS_MYSQL, IS_PG, typeMapping } from './constants.js';
14
15
  import { quoteIdentifier, resolveDefaultValue, generateDefaultSql, getSqlType, escapeComment } from './helpers.js';
15
16
  import type { SQL } from 'bun';
16
17
 
@@ -78,6 +79,9 @@ export function buildBusinessColumnDefs(fields: Record<string, string>): string[
78
79
  const colDefs: string[] = [];
79
80
 
80
81
  for (const [fieldKey, fieldRule] of Object.entries(fields)) {
82
+ // 转换字段名为下划线格式
83
+ const dbFieldName = toSnakeCase(fieldKey);
84
+
81
85
  const parsed = parseRule(fieldRule);
82
86
  const { name: fieldName, type: fieldType, max: fieldMax, default: fieldDefault } = parsed;
83
87
  const sqlType = getSqlType(fieldType, fieldMax);
@@ -87,9 +91,9 @@ export function buildBusinessColumnDefs(fields: Record<string, string>): string[
87
91
  const defaultSql = generateDefaultSql(actualDefault, fieldType);
88
92
 
89
93
  if (IS_MYSQL) {
90
- colDefs.push(`\`${fieldKey}\` ${sqlType} NOT NULL${defaultSql} COMMENT "${escapeComment(fieldName)}"`);
94
+ colDefs.push(`\`${dbFieldName}\` ${sqlType} NOT NULL${defaultSql} COMMENT "${escapeComment(fieldName)}"`);
91
95
  } else {
92
- colDefs.push(`"${fieldKey}" ${sqlType} NOT NULL${defaultSql}`);
96
+ colDefs.push(`"${dbFieldName}" ${sqlType} NOT NULL${defaultSql}`);
93
97
  }
94
98
  }
95
99
 
@@ -105,6 +109,9 @@ export function buildBusinessColumnDefs(fields: Record<string, string>): string[
105
109
  * @returns DDL 子句
106
110
  */
107
111
  export function generateDDLClause(fieldKey: string, fieldRule: string, isAdd: boolean = false): string {
112
+ // 转换字段名为下划线格式
113
+ const dbFieldName = toSnakeCase(fieldKey);
114
+
108
115
  const parsed = parseRule(fieldRule);
109
116
  const { name: fieldName, type: fieldType, max: fieldMax, default: fieldDefault } = parsed;
110
117
  const sqlType = getSqlType(fieldType, fieldMax);
@@ -114,15 +121,15 @@ export function generateDDLClause(fieldKey: string, fieldRule: string, isAdd: bo
114
121
  const defaultSql = generateDefaultSql(actualDefault, fieldType);
115
122
 
116
123
  if (IS_MYSQL) {
117
- return `${isAdd ? 'ADD COLUMN' : 'MODIFY COLUMN'} \`${fieldKey}\` ${sqlType} NOT NULL${defaultSql} COMMENT "${escapeComment(fieldName)}"`;
124
+ return `${isAdd ? 'ADD COLUMN' : 'MODIFY COLUMN'} \`${dbFieldName}\` ${sqlType} NOT NULL${defaultSql} COMMENT "${escapeComment(fieldName)}"`;
118
125
  }
119
126
  if (IS_PG) {
120
- if (isAdd) return `ADD COLUMN IF NOT EXISTS "${fieldKey}" ${sqlType} NOT NULL${defaultSql}`;
127
+ if (isAdd) return `ADD COLUMN IF NOT EXISTS "${dbFieldName}" ${sqlType} NOT NULL${defaultSql}`;
121
128
  // PG 修改:类型与非空可分条执行,生成 TYPE 改变;非空另由上层统一控制
122
- return `ALTER COLUMN "${fieldKey}" TYPE ${sqlType}`;
129
+ return `ALTER COLUMN "${dbFieldName}" TYPE ${sqlType}`;
123
130
  }
124
131
  // SQLite 仅支持 ADD COLUMN(>=3.50.0:支持 IF NOT EXISTS)
125
- if (isAdd) return `ADD COLUMN IF NOT EXISTS "${fieldKey}" ${sqlType} NOT NULL${defaultSql}`;
132
+ if (isAdd) return `ADD COLUMN IF NOT EXISTS "${dbFieldName}" ${sqlType} NOT NULL${defaultSql}`;
126
133
  return '';
127
134
  }
128
135
 
@@ -9,7 +9,8 @@
9
9
  */
10
10
 
11
11
  import { IS_MYSQL, IS_PG, typeMapping } from './constants.js';
12
- import { isType } from '../../utils/typeHelper.js';
12
+ import { isType } from '../../utils/helper.js';
13
+ import { Logger } from '../../utils/logger.js';
13
14
 
14
15
  /**
15
16
  * 根据数据库类型引用标识符
@@ -158,7 +159,7 @@ export function escapeComment(str: string): string {
158
159
  * @param changeLabel - 变更类型的中文标签
159
160
  */
160
161
  export function logFieldChange(tableName: string, fieldName: string, changeType: string, oldValue: any, newValue: any, changeLabel: string): void {
161
- console.log(` 修改表 ${tableName} 的字段 ${fieldName} 的${changeLabel}: ${oldValue} -> ${newValue}`);
162
+ Logger.info(` 修改表 ${tableName} 的字段 ${fieldName} 的${changeLabel}: ${oldValue} -> ${newValue}`);
162
163
  }
163
164
 
164
165
  /**
@@ -10,10 +10,11 @@
10
10
  import path from 'node:path';
11
11
  import { Logger } from '../../utils/logger.js';
12
12
  import { Env } from '../../config/env.js';
13
- import { createSqlClient, toSnakeTableName } from '../../utils/dbHelper.js';
13
+ import { createSqlClient } from '../../utils/database.js';
14
+ import { toSnakeCase } from '../../utils/helper.js';
14
15
  import checkTable from '../../checks/table.js';
15
- import { __dirtables, getProjectDir } from '../../system.js';
16
- import { scanAddons, hasAddonDir, getAddonDir } from '../../utils/addonHelper.js';
16
+ import { paths } from '../../paths.js';
17
+ import { scanAddons, getAddonDir, addonDirExists } from '../../utils/helper.js';
17
18
 
18
19
  // 导入模块化的功能
19
20
  import { ensureDbVersion } from './version.js';
@@ -63,30 +64,27 @@ export const SyncDb = async (): Promise<void> => {
63
64
  }
64
65
 
65
66
  // 阶段1:验证表定义文件
66
- perfTracker.markPhase('validation');
67
+ perfTracker.markPhase('表定义验证');
67
68
  if (!(await checkTable())) {
68
69
  throw new Error('表定义验证失败');
69
70
  }
70
- Logger.info(`✓ 表定义验证完成,耗时: ${perfTracker.getPhaseTime('validation')}`);
71
+ Logger.info(`✓ 表定义验证完成,耗时: ${perfTracker.getPhaseTime('表定义验证')}`);
71
72
 
72
73
  // 阶段2:建立数据库连接并检查版本
73
- perfTracker.markPhase('connection');
74
+ perfTracker.markPhase('数据库连接');
74
75
  sql = await createSqlClient({ max: 1 });
75
76
  await ensureDbVersion(sql);
76
- Logger.info(`✓ 数据库连接建立,耗时: ${perfTracker.getPhaseTime('connection')}`);
77
+ Logger.info(`✓ 数据库连接建立,耗时: ${perfTracker.getPhaseTime('数据库连接')}`);
77
78
 
78
79
  // 阶段3:扫描表定义文件
79
- perfTracker.markPhase('scan');
80
+ perfTracker.markPhase('扫描表文件');
80
81
  const tablesGlob = new Bun.Glob('*.json');
81
- const directories: Array<{ path: string; isCore: boolean; addonName?: string }> = [
82
- { path: __dirtables, isCore: true },
83
- { path: getProjectDir('tables'), isCore: false }
84
- ];
82
+ const directories: Array<{ path: string; isCore: boolean; addonName?: string }> = [{ path: paths.projectTableDir, isCore: false }];
85
83
 
86
84
  // 添加所有 addon 的 tables 目录
87
85
  const addons = scanAddons();
88
86
  for (const addon of addons) {
89
- if (hasAddonDir(addon, 'tables')) {
87
+ if (addonDirExists(addon, 'tables')) {
90
88
  directories.push({
91
89
  path: getAddonDir(addon, 'tables'),
92
90
  isCore: false,
@@ -109,16 +107,16 @@ export const SyncDb = async (): Promise<void> => {
109
107
  }
110
108
  }
111
109
  }
112
- perfTracker.finishPhase('scan');
113
- Logger.info(`✓ 扫描完成,发现 ${totalTables} 个表定义文件,耗时: ${perfTracker.getPhaseTime('scan')}`);
110
+ perfTracker.finishPhase('扫描表文件');
111
+ Logger.info(`✓ 扫描完成,发现 ${totalTables} 个表定义文件,耗时: ${perfTracker.getPhaseTime('扫描表文件')}`);
114
112
 
115
113
  // 阶段4:处理表文件
116
- perfTracker.markPhase('process');
114
+ perfTracker.markPhase('同步处理');
117
115
  let processedCount = 0;
118
116
 
119
117
  for (const dirConfig of directories) {
120
118
  const { path: dir, isCore, addonName } = dirConfig;
121
- const dirType = isCore ? '内核' : addonName ? `组件[${addonName}]` : '项目';
119
+ const dirType = addonName ? `组件${addonName}` : '项目';
122
120
 
123
121
  for await (const file of tablesGlob.scan({
124
122
  cwd: dir,
@@ -134,14 +132,11 @@ export const SyncDb = async (): Promise<void> => {
134
132
  }
135
133
 
136
134
  // 确定表名前缀:
137
- // - 核心表:sys_ 前缀
138
- // - addon 表:{addonName}_ 前缀
135
+ // - addon 表:addon_{addonName}_ 前缀
139
136
  // - 项目表:无前缀
140
- let tableName = toSnakeTableName(fileName);
141
- if (isCore) {
142
- tableName = `sys_${tableName}`;
143
- } else if (addonName) {
144
- tableName = `${addonName}_${tableName}`;
137
+ let tableName = toSnakeCase(fileName);
138
+ if (addonName) {
139
+ tableName = `addon_${addonName}_${tableName}`;
145
140
  }
146
141
 
147
142
  processedCount++;
@@ -161,8 +156,8 @@ export const SyncDb = async (): Promise<void> => {
161
156
  }
162
157
  }
163
158
 
164
- perfTracker.finishPhase('process');
165
- Logger.info(`✓ 表处理完成,耗时: ${perfTracker.getPhaseTime('process')}`);
159
+ perfTracker.finishPhase('同步处理');
160
+ Logger.info(`✓ 表处理完成,耗时: ${perfTracker.getPhaseTime('同步处理')}`);
166
161
 
167
162
  // 阶段5:显示统计信息
168
163
  Logger.info('\n=== 同步统计信息 ===');
@@ -186,14 +181,7 @@ export const SyncDb = async (): Promise<void> => {
186
181
  // 输出性能统计
187
182
  perfTracker.logStats();
188
183
  } catch (error: any) {
189
- Logger.error(`数据库同步失败: ${error.message}`);
190
- Logger.error(`错误详情: ${error.stack}`);
191
- if (error.code) {
192
- Logger.error(`错误代码: ${error.code}`);
193
- }
194
- if (error.errno) {
195
- Logger.error(`错误编号: ${error.errno}`);
196
- }
184
+ Logger.error(`数据库同步失败`, error);
197
185
  process.exit(1);
198
186
  } finally {
199
187
  if (sql) {
@@ -209,7 +197,7 @@ export const SyncDb = async (): Promise<void> => {
209
197
  // 如果直接运行此脚本(Bun 支持 import.meta.main)
210
198
  if (import.meta.main) {
211
199
  SyncDb().catch((error) => {
212
- console.error('❌ 数据库同步失败:', error);
200
+ Logger.error('数据库同步失败', error);
213
201
  process.exit(1);
214
202
  });
215
203
  }
@@ -6,6 +6,8 @@
6
6
  * - 进度信息记录
7
7
  */
8
8
 
9
+ import { Logger } from '../../utils/logger.js';
10
+
9
11
  /**
10
12
  * 阶段统计信息
11
13
  */
@@ -67,13 +69,13 @@ export class PerformanceTracker {
67
69
  * 输出所有阶段统计
68
70
  */
69
71
  logStats(): void {
70
- console.log('\n⏱️ 性能统计:');
72
+ Logger.info('\n⏱️ 性能统计:');
71
73
  for (const [phase, stats] of this.phases) {
72
74
  const duration = stats.duration || Date.now() - stats.startTime;
73
75
  const timeStr = duration > 1000 ? `${(duration / 1000).toFixed(2)}s` : `${duration}ms`;
74
- console.log(` ${phase}: ${timeStr}`);
76
+ Logger.info(` ${phase}: ${timeStr}`);
75
77
  }
76
- console.log(` 总耗时: ${this.getTotalTime()}`);
78
+ Logger.info(` 总耗时: ${this.getTotalTime()}`);
77
79
  }
78
80
  }
79
81
 
@@ -85,20 +87,20 @@ export class ProgressLogger {
85
87
  * 记录表处理进度
86
88
  */
87
89
  logTableProgress(current: number, total: number, tableName: string): void {
88
- console.log(`\n[${current}/${total}] 处理表: ${tableName}`);
90
+ Logger.info(`\n[${current}/${total}] 处理表: ${tableName}`);
89
91
  }
90
92
 
91
93
  /**
92
94
  * 记录字段变更进度
93
95
  */
94
96
  logFieldChangeProgress(current: number, total: number, fieldName: string, changeType: string): void {
95
- console.log(` [${current}/${total}] 修改字段 ${fieldName} (${changeType})`);
97
+ Logger.info(` [${current}/${total}] 修改字段 ${fieldName} (${changeType})`);
96
98
  }
97
99
 
98
100
  /**
99
101
  * 记录索引创建进度
100
102
  */
101
103
  logIndexProgress(current: number, total: number, indexName: string): void {
102
- console.log(` [${current}/${total}] 创建索引: ${indexName}`);
104
+ Logger.info(` [${current}/${total}] 创建索引: ${indexName}`);
103
105
  }
104
106
  }