befly 3.10.18 → 3.11.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.
- package/README.md +83 -307
- package/dist/befly.config.d.ts +7 -0
- package/{befly.config.ts → dist/befly.config.js} +11 -36
- package/dist/befly.js +15621 -0
- package/dist/befly.min.js +21 -0
- package/dist/checks/checkApi.d.ts +1 -0
- package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
- package/dist/checks/checkHook.d.ts +1 -0
- package/dist/checks/checkHook.js +86 -0
- package/dist/checks/checkMenu.d.ts +7 -0
- package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
- package/dist/checks/checkPlugin.d.ts +1 -0
- package/dist/checks/checkPlugin.js +86 -0
- package/dist/checks/checkTable.d.ts +6 -0
- package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
- package/dist/configs/presetFields.d.ts +4 -0
- package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
- package/dist/configs/presetRegexp.d.ts +145 -0
- package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
- package/dist/hooks/auth.d.ts +7 -0
- package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
- package/dist/hooks/cors.d.ts +11 -0
- package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
- package/dist/hooks/parser.d.ts +14 -0
- package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
- package/dist/hooks/permission.d.ts +14 -0
- package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
- package/dist/hooks/validator.d.ts +11 -0
- package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
- package/dist/index.d.ts +26 -0
- package/{main.ts → dist/index.js} +61 -100
- package/dist/lib/asyncContext.d.ts +21 -0
- package/dist/lib/asyncContext.js +27 -0
- package/dist/lib/cacheHelper.d.ts +95 -0
- package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
- package/dist/lib/cacheKeys.d.ts +23 -0
- package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
- package/dist/lib/cipher.d.ts +153 -0
- package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
- package/dist/lib/connect.d.ts +91 -0
- package/{lib/connect.ts → dist/lib/connect.js} +47 -88
- package/dist/lib/dbDialect.d.ts +87 -0
- package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
- package/dist/lib/dbHelper.d.ts +204 -0
- package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
- package/dist/lib/dbUtils.d.ts +68 -0
- package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
- package/dist/lib/jwt.d.ts +13 -0
- package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
- package/dist/lib/logger.d.ts +42 -0
- package/dist/lib/logger.js +1144 -0
- package/dist/lib/redisHelper.d.ts +185 -0
- package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
- package/dist/lib/sqlBuilder.d.ts +160 -0
- package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
- package/dist/lib/sqlCheck.d.ts +23 -0
- package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
- package/dist/lib/validator.d.ts +45 -0
- package/{lib/validator.ts → dist/lib/validator.js} +44 -61
- package/dist/loader/loadApis.d.ts +12 -0
- package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
- package/dist/loader/loadHooks.d.ts +7 -0
- package/dist/loader/loadHooks.js +35 -0
- package/dist/loader/loadPlugins.d.ts +8 -0
- package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
- package/dist/paths.d.ts +93 -0
- package/{paths.ts → dist/paths.js} +6 -19
- package/dist/plugins/cache.d.ts +16 -0
- package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
- package/dist/plugins/cipher.d.ts +12 -0
- package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
- package/dist/plugins/config.d.ts +12 -0
- package/dist/plugins/config.js +8 -0
- package/dist/plugins/db.d.ts +16 -0
- package/{plugins/db.ts → dist/plugins/db.js} +11 -17
- package/dist/plugins/jwt.d.ts +12 -0
- package/dist/plugins/jwt.js +12 -0
- package/dist/plugins/logger.d.ts +32 -0
- package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
- package/dist/plugins/redis.d.ts +16 -0
- package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
- package/dist/plugins/tool.d.ts +81 -0
- package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
- package/dist/router/api.d.ts +14 -0
- package/dist/router/api.js +107 -0
- package/dist/router/static.d.ts +9 -0
- package/{router/static.ts → dist/router/static.js} +20 -34
- package/dist/scripts/ensureDist.d.ts +1 -0
- package/dist/scripts/ensureDist.js +296 -0
- package/dist/sync/syncApi.d.ts +3 -0
- package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
- package/dist/sync/syncCache.d.ts +2 -0
- package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
- package/dist/sync/syncDev.d.ts +6 -0
- package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
- package/dist/sync/syncMenu.d.ts +14 -0
- package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
- package/dist/sync/syncTable.d.ts +151 -0
- package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
- package/{types → dist/types}/api.d.ts +12 -51
- package/dist/types/api.js +4 -0
- package/{types → dist/types}/befly.d.ts +32 -227
- package/dist/types/befly.js +4 -0
- package/{types → dist/types}/cache.d.ts +7 -15
- package/dist/types/cache.js +4 -0
- package/dist/types/cipher.d.ts +27 -0
- package/dist/types/cipher.js +7 -0
- package/{types → dist/types}/common.d.ts +8 -33
- package/dist/types/common.js +5 -0
- package/{types → dist/types}/context.d.ts +3 -5
- package/dist/types/context.js +4 -0
- package/{types → dist/types}/crypto.d.ts +0 -3
- package/dist/types/crypto.js +4 -0
- package/dist/types/database.d.ts +138 -0
- package/dist/types/database.js +4 -0
- package/dist/types/hook.d.ts +17 -0
- package/dist/types/hook.js +6 -0
- package/dist/types/jwt.d.ts +75 -0
- package/dist/types/jwt.js +4 -0
- package/dist/types/logger.d.ts +59 -0
- package/dist/types/logger.js +6 -0
- package/dist/types/plugin.d.ts +16 -0
- package/dist/types/plugin.js +6 -0
- package/dist/types/redis.d.ts +71 -0
- package/dist/types/redis.js +4 -0
- package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
- package/dist/types/roleApisCache.js +8 -0
- package/dist/types/sync.d.ts +92 -0
- package/dist/types/sync.js +4 -0
- package/dist/types/table.d.ts +34 -0
- package/dist/types/table.js +4 -0
- package/dist/types/validate.d.ts +67 -0
- package/dist/types/validate.js +4 -0
- package/dist/utils/calcPerfTime.d.ts +4 -0
- package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
- package/dist/utils/convertBigIntFields.d.ts +11 -0
- package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
- package/dist/utils/cors.d.ts +8 -0
- package/{utils/cors.ts → dist/utils/cors.js} +1 -3
- package/dist/utils/disableMenusGlob.d.ts +13 -0
- package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
- package/dist/utils/fieldClear.d.ts +11 -0
- package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
- package/dist/utils/getClientIp.d.ts +6 -0
- package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
- package/dist/utils/importDefault.d.ts +1 -0
- package/dist/utils/importDefault.js +29 -0
- package/dist/utils/isDirentDirectory.d.ts +2 -0
- package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
- package/dist/utils/loadMenuConfigs.d.ts +29 -0
- package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
- package/dist/utils/mergeAndConcat.d.ts +7 -0
- package/dist/utils/mergeAndConcat.js +72 -0
- package/dist/utils/processAtSymbol.d.ts +4 -0
- package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
- package/dist/utils/processInfo.d.ts +24 -0
- package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
- package/dist/utils/response.d.ts +20 -0
- package/{utils/response.ts → dist/utils/response.js} +28 -49
- package/dist/utils/scanAddons.d.ts +17 -0
- package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
- package/dist/utils/scanConfig.d.ts +26 -0
- package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
- package/dist/utils/scanCoreBuiltins.d.ts +3 -0
- package/dist/utils/scanCoreBuiltins.js +65 -0
- package/dist/utils/scanFiles.d.ts +30 -0
- package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
- package/dist/utils/scanSources.d.ts +10 -0
- package/dist/utils/scanSources.js +46 -0
- package/dist/utils/sortModules.d.ts +28 -0
- package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
- package/dist/utils/util.d.ts +84 -0
- package/dist/utils/util.js +262 -0
- package/package.json +26 -34
- package/.gitignore +0 -0
- package/bunfig.toml +0 -3
- package/checks/checkHook.ts +0 -48
- package/checks/checkPlugin.ts +0 -48
- package/configs/presetRegexp.ts +0 -225
- package/docs/README.md +0 -98
- package/docs/api/api.md +0 -1921
- package/docs/guide/examples.md +0 -926
- package/docs/guide/quickstart.md +0 -354
- package/docs/hooks/auth.md +0 -38
- package/docs/hooks/cors.md +0 -28
- package/docs/hooks/hook.md +0 -838
- package/docs/hooks/parser.md +0 -19
- package/docs/hooks/rateLimit.md +0 -47
- package/docs/infra/redis.md +0 -628
- package/docs/plugins/cipher.md +0 -61
- package/docs/plugins/database.md +0 -189
- package/docs/plugins/plugin.md +0 -986
- package/docs/reference/addon.md +0 -510
- package/docs/reference/config.md +0 -573
- package/docs/reference/logger.md +0 -495
- package/docs/reference/sync.md +0 -478
- package/docs/reference/table.md +0 -763
- package/docs/reference/validator.md +0 -620
- package/lib/asyncContext.ts +0 -43
- package/lib/logger.ts +0 -811
- package/loader/loadHooks.ts +0 -51
- package/plugins/config.ts +0 -13
- package/plugins/jwt.ts +0 -15
- package/router/api.ts +0 -130
- package/tsconfig.json +0 -8
- package/types/database.d.ts +0 -541
- package/types/hook.d.ts +0 -25
- package/types/jwt.d.ts +0 -118
- package/types/logger.d.ts +0 -65
- package/types/plugin.d.ts +0 -19
- package/types/redis.d.ts +0 -83
- package/types/sync.d.ts +0 -398
- package/types/table.d.ts +0 -216
- package/types/validate.d.ts +0 -69
- package/utils/arrayKeysToCamel.ts +0 -18
- package/utils/configTypes.ts +0 -3
- package/utils/genShortId.ts +0 -12
- package/utils/importDefault.ts +0 -21
- package/utils/keysToCamel.ts +0 -22
- package/utils/keysToSnake.ts +0 -22
- package/utils/pickFields.ts +0 -19
- package/utils/scanSources.ts +0 -64
- package/utils/sqlLog.ts +0 -37
|
@@ -1,45 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
let DIALECT_CACHE: Map<DbDialectName, DbDialect> | null = null;
|
|
4
|
-
|
|
1
|
+
let DIALECT_CACHE = null;
|
|
5
2
|
/**
|
|
6
3
|
* 获取方言实例(内部缓存,避免到处 new)。
|
|
7
4
|
*
|
|
8
5
|
* 约束:dialect 实例应当是纯逻辑对象(无连接/无 IO/无状态副作用),可全局复用。
|
|
9
6
|
*/
|
|
10
|
-
export function getDialectByName(name
|
|
7
|
+
export function getDialectByName(name) {
|
|
11
8
|
const unknownDialectError = new Error(`未知数据库方言: ${String(name)}`);
|
|
12
9
|
if (name !== "mysql" && name !== "postgresql" && name !== "sqlite") {
|
|
13
10
|
throw unknownDialectError;
|
|
14
11
|
}
|
|
15
|
-
|
|
16
12
|
if (!DIALECT_CACHE) {
|
|
17
|
-
DIALECT_CACHE = new Map
|
|
13
|
+
DIALECT_CACHE = new Map();
|
|
18
14
|
DIALECT_CACHE.set("mysql", new MySqlDialect());
|
|
19
15
|
DIALECT_CACHE.set("postgresql", new PostgresDialect());
|
|
20
16
|
DIALECT_CACHE.set("sqlite", new SqliteDialect());
|
|
21
17
|
}
|
|
22
|
-
|
|
23
18
|
const dialect = DIALECT_CACHE.get(name);
|
|
24
19
|
if (!dialect) {
|
|
25
20
|
throw unknownDialectError;
|
|
26
21
|
}
|
|
27
|
-
|
|
28
22
|
return dialect;
|
|
29
23
|
}
|
|
30
|
-
|
|
31
|
-
export type SqlTextQuery = {
|
|
32
|
-
sql: string;
|
|
33
|
-
params: unknown[];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export type SyncTableColumnsInfoQuery = {
|
|
37
|
-
columns: SqlTextQuery;
|
|
38
|
-
comments?: SqlTextQuery;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export type SyncTableIndexesQuery = SqlTextQuery;
|
|
42
|
-
|
|
43
24
|
/**
|
|
44
25
|
* syncTable 专用:获取“列元信息”查询。
|
|
45
26
|
*
|
|
@@ -47,35 +28,30 @@ export type SyncTableIndexesQuery = SqlTextQuery;
|
|
|
47
28
|
* - 这里仅负责 SQL + 参数构造(方言差异);
|
|
48
29
|
* - 解析为 ColumnInfo 的逻辑仍放在 syncTable.ts(保持同步算法聚合)。
|
|
49
30
|
*/
|
|
50
|
-
export function getSyncTableColumnsInfoQuery(options
|
|
31
|
+
export function getSyncTableColumnsInfoQuery(options) {
|
|
51
32
|
if (options.dialect === "mysql") {
|
|
52
|
-
const columns
|
|
33
|
+
const columns = {
|
|
53
34
|
sql: "SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION",
|
|
54
35
|
params: [options.dbName, options.table]
|
|
55
36
|
};
|
|
56
37
|
return { columns: columns };
|
|
57
38
|
}
|
|
58
|
-
|
|
59
39
|
if (options.dialect === "postgresql") {
|
|
60
40
|
const schema = options.schema && options.schema.trim() !== "" ? options.schema : "public";
|
|
61
|
-
const columns
|
|
41
|
+
const columns = {
|
|
62
42
|
sql: "SELECT column_name, data_type, character_maximum_length, is_nullable, column_default FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position",
|
|
63
43
|
params: [schema, options.table]
|
|
64
44
|
};
|
|
65
|
-
|
|
66
|
-
const comments: SqlTextQuery = {
|
|
45
|
+
const comments = {
|
|
67
46
|
sql: "SELECT a.attname AS column_name, col_description(c.oid, a.attnum) AS column_comment FROM pg_class c JOIN pg_attribute a ON a.attrelid = c.oid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'r' AND n.nspname = ? AND c.relname = ? AND a.attnum > 0",
|
|
68
47
|
params: [schema, options.table]
|
|
69
48
|
};
|
|
70
|
-
|
|
71
49
|
return { columns: columns, comments: comments };
|
|
72
50
|
}
|
|
73
|
-
|
|
74
51
|
const sqlite = getDialectByName("sqlite");
|
|
75
52
|
const columns = sqlite.getTableColumnsQuery(options.table);
|
|
76
53
|
return { columns: columns };
|
|
77
54
|
}
|
|
78
|
-
|
|
79
55
|
/**
|
|
80
56
|
* syncTable 专用:获取“索引元信息”查询(只负责 SQL + 参数构造)。
|
|
81
57
|
*
|
|
@@ -83,14 +59,13 @@ export function getSyncTableColumnsInfoQuery(options: { dialect: DbDialectName;
|
|
|
83
59
|
* - 仅下沉 MySQL / PostgreSQL;SQLite 仍走 PRAGMA(需要多次查询)。
|
|
84
60
|
* - 解析(比如 PG indexdef 解析列名)仍留在 syncTable.ts。
|
|
85
61
|
*/
|
|
86
|
-
export function getSyncTableIndexesQuery(options
|
|
62
|
+
export function getSyncTableIndexesQuery(options) {
|
|
87
63
|
if (options.dialect === "mysql") {
|
|
88
64
|
return {
|
|
89
65
|
sql: "SELECT INDEX_NAME, COLUMN_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND INDEX_NAME != 'PRIMARY' ORDER BY INDEX_NAME",
|
|
90
66
|
params: [options.dbName, options.table]
|
|
91
67
|
};
|
|
92
68
|
}
|
|
93
|
-
|
|
94
69
|
if (options.dialect === "postgresql") {
|
|
95
70
|
const schema = options.schema && options.schema.trim() !== "" ? options.schema : "public";
|
|
96
71
|
return {
|
|
@@ -98,76 +73,39 @@ export function getSyncTableIndexesQuery(options: { dialect: DbDialectName; tabl
|
|
|
98
73
|
params: [schema, options.table]
|
|
99
74
|
};
|
|
100
75
|
}
|
|
101
|
-
|
|
102
76
|
throw new Error(`getSyncTableIndexesQuery 不支持方言: ${String(options.dialect)}`);
|
|
103
77
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 转义/引用 SQL 标识符(表名/字段名)。
|
|
110
|
-
* 注意:仅用于单个标识符片段,不支持包含空格、点号、函数等复杂表达式。
|
|
111
|
-
*/
|
|
112
|
-
quoteIdent(identifier: string): string;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 获取表字段列表的查询。
|
|
116
|
-
* 约定:返回结果应能通过 getTableColumnsFromResult 提取列名。
|
|
117
|
-
*/
|
|
118
|
-
getTableColumnsQuery(table: string, schema?: string): SqlTextQuery;
|
|
119
|
-
|
|
120
|
-
/** 从 getTableColumnsQuery 的结果中提取列名数组 */
|
|
121
|
-
getTableColumnsFromResult(result: any): string[];
|
|
122
|
-
|
|
123
|
-
/** 检查表是否存在的查询 */
|
|
124
|
-
tableExistsQuery(table: string, schema?: string): SqlTextQuery;
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* 是否支持 schema.table 形式(仅用于校验;具体 quoting 仍由 SqlBuilder 处理)。
|
|
128
|
-
*/
|
|
129
|
-
supportsSchema: boolean;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export class MySqlDialect implements DbDialect {
|
|
133
|
-
name: DbDialectName = "mysql";
|
|
134
|
-
supportsSchema: boolean = true;
|
|
135
|
-
|
|
136
|
-
quoteIdent(identifier: string): string {
|
|
78
|
+
export class MySqlDialect {
|
|
79
|
+
name = "mysql";
|
|
80
|
+
supportsSchema = true;
|
|
81
|
+
quoteIdent(identifier) {
|
|
137
82
|
if (typeof identifier !== "string") {
|
|
138
83
|
throw new Error(`quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`);
|
|
139
84
|
}
|
|
140
|
-
|
|
141
85
|
const trimmed = identifier.trim();
|
|
142
86
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
|
|
143
87
|
throw new Error(`无效的 SQL 标识符: ${trimmed}`);
|
|
144
88
|
}
|
|
145
|
-
|
|
146
89
|
return `\`${trimmed}\``;
|
|
147
90
|
}
|
|
148
|
-
|
|
149
|
-
getTableColumnsQuery(table: string, _schema?: string): SqlTextQuery {
|
|
91
|
+
getTableColumnsQuery(table, _schema) {
|
|
150
92
|
const quotedTable = this.quoteIdent(table);
|
|
151
93
|
return { sql: `SHOW COLUMNS FROM ${quotedTable}`, params: [] };
|
|
152
94
|
}
|
|
153
|
-
|
|
154
|
-
getTableColumnsFromResult(result: any): string[] {
|
|
95
|
+
getTableColumnsFromResult(result) {
|
|
155
96
|
if (!Array.isArray(result)) {
|
|
156
97
|
return [];
|
|
157
98
|
}
|
|
158
|
-
|
|
159
|
-
const columnNames: string[] = [];
|
|
99
|
+
const columnNames = [];
|
|
160
100
|
for (const row of result) {
|
|
161
101
|
const name = row?.Field;
|
|
162
102
|
if (typeof name === "string" && name.length > 0) {
|
|
163
103
|
columnNames.push(name);
|
|
164
104
|
}
|
|
165
105
|
}
|
|
166
|
-
|
|
167
106
|
return columnNames;
|
|
168
107
|
}
|
|
169
|
-
|
|
170
|
-
tableExistsQuery(table: string, schema?: string): SqlTextQuery {
|
|
108
|
+
tableExistsQuery(table, schema) {
|
|
171
109
|
if (typeof schema === "string" && schema.trim() !== "") {
|
|
172
110
|
return {
|
|
173
111
|
sql: "SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
|
|
@@ -177,26 +115,21 @@ export class MySqlDialect implements DbDialect {
|
|
|
177
115
|
return { sql: "SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?", params: [table] };
|
|
178
116
|
}
|
|
179
117
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
quoteIdent(identifier: string): string {
|
|
118
|
+
export class PostgresDialect {
|
|
119
|
+
name = "postgresql";
|
|
120
|
+
supportsSchema = true;
|
|
121
|
+
quoteIdent(identifier) {
|
|
186
122
|
if (typeof identifier !== "string") {
|
|
187
123
|
throw new Error(`quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`);
|
|
188
124
|
}
|
|
189
|
-
|
|
190
125
|
const trimmed = identifier.trim();
|
|
191
126
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
|
|
192
127
|
throw new Error(`无效的 SQL 标识符: ${trimmed}`);
|
|
193
128
|
}
|
|
194
|
-
|
|
195
129
|
// PostgreSQL 使用双引号引用标识符
|
|
196
130
|
return `"${trimmed}"`;
|
|
197
131
|
}
|
|
198
|
-
|
|
199
|
-
getTableColumnsQuery(table: string, schema?: string): SqlTextQuery {
|
|
132
|
+
getTableColumnsQuery(table, schema) {
|
|
200
133
|
if (typeof schema === "string" && schema.trim() !== "") {
|
|
201
134
|
return {
|
|
202
135
|
sql: "SELECT column_name FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position",
|
|
@@ -205,24 +138,20 @@ export class PostgresDialect implements DbDialect {
|
|
|
205
138
|
}
|
|
206
139
|
return { sql: "SELECT column_name FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = ? ORDER BY ordinal_position", params: [table] };
|
|
207
140
|
}
|
|
208
|
-
|
|
209
|
-
getTableColumnsFromResult(result: any): string[] {
|
|
141
|
+
getTableColumnsFromResult(result) {
|
|
210
142
|
if (!Array.isArray(result)) {
|
|
211
143
|
return [];
|
|
212
144
|
}
|
|
213
|
-
|
|
214
|
-
const columnNames: string[] = [];
|
|
145
|
+
const columnNames = [];
|
|
215
146
|
for (const row of result) {
|
|
216
147
|
const name = row?.column_name;
|
|
217
148
|
if (typeof name === "string" && name.length > 0) {
|
|
218
149
|
columnNames.push(name);
|
|
219
150
|
}
|
|
220
151
|
}
|
|
221
|
-
|
|
222
152
|
return columnNames;
|
|
223
153
|
}
|
|
224
|
-
|
|
225
|
-
tableExistsQuery(table: string, schema?: string): SqlTextQuery {
|
|
154
|
+
tableExistsQuery(table, schema) {
|
|
226
155
|
if (typeof schema === "string" && schema.trim() !== "") {
|
|
227
156
|
return {
|
|
228
157
|
sql: "SELECT COUNT(*)::int as count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
|
|
@@ -232,51 +161,42 @@ export class PostgresDialect implements DbDialect {
|
|
|
232
161
|
return { sql: "SELECT COUNT(*)::int as count FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = ?", params: [table] };
|
|
233
162
|
}
|
|
234
163
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
name: DbDialectName = "sqlite";
|
|
164
|
+
export class SqliteDialect {
|
|
165
|
+
name = "sqlite";
|
|
238
166
|
// SQLite 的“schema”语义与 MySQL/PG 不同:常规使用下没有独立 schema;
|
|
239
167
|
// 虽然存在 main/temp/attached db 的 database_name.table_name 形式,但这里
|
|
240
168
|
// 统一视为不支持传统 schema.table 校验语义。
|
|
241
|
-
supportsSchema
|
|
242
|
-
|
|
243
|
-
quoteIdent(identifier: string): string {
|
|
169
|
+
supportsSchema = false;
|
|
170
|
+
quoteIdent(identifier) {
|
|
244
171
|
if (typeof identifier !== "string") {
|
|
245
172
|
throw new Error(`quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`);
|
|
246
173
|
}
|
|
247
|
-
|
|
248
174
|
const trimmed = identifier.trim();
|
|
249
175
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
|
|
250
176
|
throw new Error(`无效的 SQL 标识符: ${trimmed}`);
|
|
251
177
|
}
|
|
252
|
-
|
|
253
178
|
// SQLite 也支持双引号引用标识符
|
|
254
179
|
return `"${trimmed}"`;
|
|
255
180
|
}
|
|
256
|
-
|
|
257
|
-
getTableColumnsQuery(table: string, _schema?: string): SqlTextQuery {
|
|
181
|
+
getTableColumnsQuery(table, _schema) {
|
|
258
182
|
// PRAGMA 不支持参数占位符;此处通过 quoteIdent 限制输入并安全拼接
|
|
259
183
|
const quotedTable = this.quoteIdent(table);
|
|
260
184
|
return { sql: `PRAGMA table_info(${quotedTable})`, params: [] };
|
|
261
185
|
}
|
|
262
|
-
|
|
263
|
-
getTableColumnsFromResult(result: any): string[] {
|
|
186
|
+
getTableColumnsFromResult(result) {
|
|
264
187
|
if (!Array.isArray(result)) {
|
|
265
188
|
return [];
|
|
266
189
|
}
|
|
267
|
-
|
|
268
|
-
const columnNames: string[] = [];
|
|
190
|
+
const columnNames = [];
|
|
269
191
|
for (const row of result) {
|
|
270
192
|
const name = row?.name;
|
|
271
193
|
if (typeof name === "string" && name.length > 0) {
|
|
272
194
|
columnNames.push(name);
|
|
273
195
|
}
|
|
274
196
|
}
|
|
275
|
-
|
|
276
197
|
return columnNames;
|
|
277
198
|
}
|
|
278
|
-
|
|
279
|
-
tableExistsQuery(table: string, _schema?: string): SqlTextQuery {
|
|
199
|
+
tableExistsQuery(table, _schema) {
|
|
280
200
|
return {
|
|
281
201
|
sql: "SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name = ?",
|
|
282
202
|
params: [table]
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据库助手 - TypeScript 版本
|
|
3
|
+
* 提供数据库 CRUD 操作的封装
|
|
4
|
+
*/
|
|
5
|
+
import type { WhereConditions } from "../types/common";
|
|
6
|
+
import type { QueryOptions, InsertOptions, UpdateOptions, DeleteOptions, ListResult, AllResult, TransactionCallback, DbResult, ListSql } from "../types/database";
|
|
7
|
+
import type { DbDialect } from "./dbDialect";
|
|
8
|
+
type RedisCacheLike = {
|
|
9
|
+
getObject<T = any>(key: string): Promise<T | null>;
|
|
10
|
+
setObject<T = any>(key: string, obj: T, ttl?: number | null): Promise<string | null>;
|
|
11
|
+
genTimeID(): Promise<number>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* 数据库助手类
|
|
15
|
+
*/
|
|
16
|
+
export declare class DbHelper {
|
|
17
|
+
private redis;
|
|
18
|
+
private dialect;
|
|
19
|
+
private sql;
|
|
20
|
+
private isTransaction;
|
|
21
|
+
/**
|
|
22
|
+
* 构造函数
|
|
23
|
+
* @param redis - Redis 实例
|
|
24
|
+
* @param sql - Bun SQL 客户端(可选,用于事务)
|
|
25
|
+
*/
|
|
26
|
+
constructor(options: {
|
|
27
|
+
redis: RedisCacheLike;
|
|
28
|
+
sql?: any | null;
|
|
29
|
+
dialect?: DbDialect;
|
|
30
|
+
});
|
|
31
|
+
private createSqlBuilder;
|
|
32
|
+
private getTableColumns;
|
|
33
|
+
private prepareQueryOptions;
|
|
34
|
+
/**
|
|
35
|
+
* 为 builder 添加 JOIN
|
|
36
|
+
*/
|
|
37
|
+
private applyJoins;
|
|
38
|
+
private executeWithConn;
|
|
39
|
+
/**
|
|
40
|
+
* 执行原生 SQL(内部工具/同步脚本专用)
|
|
41
|
+
*
|
|
42
|
+
* - 复用当前 DbHelper 持有的连接/事务
|
|
43
|
+
* - 统一走 executeWithConn,保持参数校验与错误行为一致
|
|
44
|
+
*/
|
|
45
|
+
unsafe(sqlStr: string, params?: any[]): Promise<DbResult<any>>;
|
|
46
|
+
/**
|
|
47
|
+
* 检查表是否存在
|
|
48
|
+
* @param tableName - 表名(支持小驼峰,会自动转换为下划线)
|
|
49
|
+
* @returns 表是否存在
|
|
50
|
+
*/
|
|
51
|
+
tableExists(tableName: string): Promise<DbResult<boolean>>;
|
|
52
|
+
/**
|
|
53
|
+
* 查询记录数
|
|
54
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
55
|
+
* @param options.where - 查询条件
|
|
56
|
+
* @param options.joins - 多表联查选项
|
|
57
|
+
* @example
|
|
58
|
+
* // 查询总数
|
|
59
|
+
* const count = await db.getCount({ table: 'user' });
|
|
60
|
+
* // 查询指定条件的记录数
|
|
61
|
+
* const activeCount = await db.getCount({ table: 'user', where: { state: 1 } });
|
|
62
|
+
* // 联查计数
|
|
63
|
+
* const count = await db.getCount({
|
|
64
|
+
* table: 'order o',
|
|
65
|
+
* joins: [{ table: 'user u', on: 'o.user_id = u.id' }],
|
|
66
|
+
* where: { 'o.state': 1 }
|
|
67
|
+
* });
|
|
68
|
+
*/
|
|
69
|
+
getCount(options: Omit<QueryOptions, "fields" | "page" | "limit" | "orderBy">): Promise<DbResult<number>>;
|
|
70
|
+
/**
|
|
71
|
+
* 查询单条数据
|
|
72
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换;联查时可带别名如 'order o')
|
|
73
|
+
* @param options.fields - 字段列表(联查时需带表别名,如 'o.id', 'u.username')
|
|
74
|
+
* @param options.joins - 多表联查选项
|
|
75
|
+
* @example
|
|
76
|
+
* // 单表查询
|
|
77
|
+
* getOne({ table: 'userProfile', fields: ['userId', 'userName'] })
|
|
78
|
+
* // 联查
|
|
79
|
+
* getOne({
|
|
80
|
+
* table: 'order o',
|
|
81
|
+
* joins: [{ table: 'user u', on: 'o.user_id = u.id' }],
|
|
82
|
+
* fields: ['o.id', 'o.totalAmount', 'u.username'],
|
|
83
|
+
* where: { 'o.id': 1 }
|
|
84
|
+
* })
|
|
85
|
+
*/
|
|
86
|
+
getOne<T extends Record<string, any> = Record<string, any>>(options: QueryOptions): Promise<DbResult<T | null>>;
|
|
87
|
+
/**
|
|
88
|
+
* 查询列表(带分页)
|
|
89
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换;联查时可带别名)
|
|
90
|
+
* @param options.fields - 字段列表(联查时需带表别名)
|
|
91
|
+
* @param options.joins - 多表联查选项
|
|
92
|
+
* @example
|
|
93
|
+
* // 单表分页
|
|
94
|
+
* getList({ table: 'userProfile', fields: ['userId', 'userName', 'createdAt'] })
|
|
95
|
+
* // 联查分页
|
|
96
|
+
* getList({
|
|
97
|
+
* table: 'order o',
|
|
98
|
+
* joins: [
|
|
99
|
+
* { table: 'user u', on: 'o.user_id = u.id' },
|
|
100
|
+
* { table: 'product p', on: 'o.product_id = p.id' }
|
|
101
|
+
* ],
|
|
102
|
+
* fields: ['o.id', 'o.totalAmount', 'u.username', 'p.name AS productName'],
|
|
103
|
+
* where: { 'o.status': 'paid' },
|
|
104
|
+
* orderBy: ['o.createdAt#DESC'],
|
|
105
|
+
* page: 1,
|
|
106
|
+
* limit: 10
|
|
107
|
+
* })
|
|
108
|
+
*/
|
|
109
|
+
getList<T extends Record<string, any> = Record<string, any>>(options: QueryOptions): Promise<DbResult<ListResult<T>, ListSql>>;
|
|
110
|
+
/**
|
|
111
|
+
* 查询所有数据(不分页,有上限保护)
|
|
112
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换;联查时可带别名)
|
|
113
|
+
* @param options.fields - 字段列表(联查时需带表别名)
|
|
114
|
+
* @param options.joins - 多表联查选项
|
|
115
|
+
* ⚠️ 警告:此方法会查询大量数据,建议使用 getList 分页查询
|
|
116
|
+
* @example
|
|
117
|
+
* // 单表查询
|
|
118
|
+
* getAll({ table: 'userProfile', fields: ['userId', 'userName'] })
|
|
119
|
+
* // 联查
|
|
120
|
+
* getAll({
|
|
121
|
+
* table: 'order o',
|
|
122
|
+
* joins: [{ table: 'user u', on: 'o.user_id = u.id' }],
|
|
123
|
+
* fields: ['o.id', 'u.username'],
|
|
124
|
+
* where: { 'o.state': 1 }
|
|
125
|
+
* })
|
|
126
|
+
*/
|
|
127
|
+
getAll<T extends Record<string, any> = Record<string, any>>(options: Omit<QueryOptions, "page" | "limit">): Promise<DbResult<AllResult<T>, ListSql>>;
|
|
128
|
+
/**
|
|
129
|
+
* 插入数据(自动生成 ID、时间戳、state)
|
|
130
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
131
|
+
*/
|
|
132
|
+
insData(options: InsertOptions): Promise<DbResult<number>>;
|
|
133
|
+
/**
|
|
134
|
+
* 批量插入数据(真正的批量操作)
|
|
135
|
+
* 使用 INSERT INTO ... VALUES (...), (...), (...) 语法
|
|
136
|
+
* 自动生成系统字段并包装在事务中
|
|
137
|
+
* @param table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
138
|
+
*/
|
|
139
|
+
insBatch(table: string, dataList: Record<string, any>[]): Promise<DbResult<number[]>>;
|
|
140
|
+
delForceBatch(table: string, ids: number[]): Promise<DbResult<number>>;
|
|
141
|
+
updBatch(table: string, dataList: Array<{
|
|
142
|
+
id: number;
|
|
143
|
+
data: Record<string, any>;
|
|
144
|
+
}>): Promise<DbResult<number>>;
|
|
145
|
+
/**
|
|
146
|
+
* 更新数据(强制更新时间戳,系统字段不可修改)
|
|
147
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
148
|
+
*/
|
|
149
|
+
updData(options: UpdateOptions): Promise<DbResult<number>>;
|
|
150
|
+
/**
|
|
151
|
+
* 软删除数据(deleted_at 设置为当前时间,state 设置为 0)
|
|
152
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
153
|
+
*/
|
|
154
|
+
delData(options: DeleteOptions): Promise<DbResult<number>>;
|
|
155
|
+
/**
|
|
156
|
+
* 硬删除数据(物理删除,不可恢复)
|
|
157
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
158
|
+
*/
|
|
159
|
+
delForce(options: Omit<DeleteOptions, "hard">): Promise<DbResult<number>>;
|
|
160
|
+
/**
|
|
161
|
+
* 禁用数据(设置 state=2)
|
|
162
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
163
|
+
*/
|
|
164
|
+
disableData(options: Omit<DeleteOptions, "hard">): Promise<DbResult<number>>;
|
|
165
|
+
/**
|
|
166
|
+
* 启用数据(设置 state=1)
|
|
167
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
168
|
+
*/
|
|
169
|
+
enableData(options: Omit<DeleteOptions, "hard">): Promise<DbResult<number>>;
|
|
170
|
+
/**
|
|
171
|
+
* 执行事务
|
|
172
|
+
* 使用 Bun SQL 的 begin 方法开启事务
|
|
173
|
+
*/
|
|
174
|
+
trans<T = any>(callback: TransactionCallback<T>): Promise<T>;
|
|
175
|
+
/**
|
|
176
|
+
* 执行原始 SQL
|
|
177
|
+
*/
|
|
178
|
+
query(sql: string, params?: any[]): Promise<DbResult<any>>;
|
|
179
|
+
/**
|
|
180
|
+
* 检查数据是否存在(优化性能)
|
|
181
|
+
* @param options.table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
182
|
+
*/
|
|
183
|
+
exists(options: Omit<QueryOptions, "fields" | "orderBy" | "page" | "limit">): Promise<DbResult<boolean>>;
|
|
184
|
+
/**
|
|
185
|
+
* 查询单个字段值(带字段名验证)
|
|
186
|
+
* @param field - 字段名(支持小驼峰或下划线格式)
|
|
187
|
+
*/
|
|
188
|
+
getFieldValue<T = any>(options: Omit<QueryOptions, "fields"> & {
|
|
189
|
+
field: string;
|
|
190
|
+
}): Promise<DbResult<T | null>>;
|
|
191
|
+
/**
|
|
192
|
+
* 自增字段(安全实现,防止 SQL 注入)
|
|
193
|
+
* @param table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
194
|
+
* @param field - 字段名(支持小驼峰或下划线格式,会自动转换)
|
|
195
|
+
*/
|
|
196
|
+
increment(table: string, field: string, where: WhereConditions, value?: number): Promise<DbResult<number>>;
|
|
197
|
+
/**
|
|
198
|
+
* 自减字段
|
|
199
|
+
* @param table - 表名(支持小驼峰或下划线格式,会自动转换)
|
|
200
|
+
* @param field - 字段名(支持小驼峰或下划线格式,会自动转换)
|
|
201
|
+
*/
|
|
202
|
+
decrement(table: string, field: string, where: WhereConditions, value?: number): Promise<DbResult<number>>;
|
|
203
|
+
}
|
|
204
|
+
export {};
|