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.
Files changed (223) hide show
  1. package/README.md +83 -307
  2. package/dist/befly.config.d.ts +7 -0
  3. package/{befly.config.ts → dist/befly.config.js} +11 -36
  4. package/dist/befly.js +15621 -0
  5. package/dist/befly.min.js +21 -0
  6. package/dist/checks/checkApi.d.ts +1 -0
  7. package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
  8. package/dist/checks/checkHook.d.ts +1 -0
  9. package/dist/checks/checkHook.js +86 -0
  10. package/dist/checks/checkMenu.d.ts +7 -0
  11. package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
  12. package/dist/checks/checkPlugin.d.ts +1 -0
  13. package/dist/checks/checkPlugin.js +86 -0
  14. package/dist/checks/checkTable.d.ts +6 -0
  15. package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
  16. package/dist/configs/presetFields.d.ts +4 -0
  17. package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
  18. package/dist/configs/presetRegexp.d.ts +145 -0
  19. package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
  20. package/dist/hooks/auth.d.ts +7 -0
  21. package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
  22. package/dist/hooks/cors.d.ts +11 -0
  23. package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
  24. package/dist/hooks/parser.d.ts +14 -0
  25. package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
  26. package/dist/hooks/permission.d.ts +14 -0
  27. package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
  28. package/dist/hooks/validator.d.ts +11 -0
  29. package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
  30. package/dist/index.d.ts +26 -0
  31. package/{main.ts → dist/index.js} +61 -100
  32. package/dist/lib/asyncContext.d.ts +21 -0
  33. package/dist/lib/asyncContext.js +27 -0
  34. package/dist/lib/cacheHelper.d.ts +95 -0
  35. package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
  36. package/dist/lib/cacheKeys.d.ts +23 -0
  37. package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
  38. package/dist/lib/cipher.d.ts +153 -0
  39. package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
  40. package/dist/lib/connect.d.ts +91 -0
  41. package/{lib/connect.ts → dist/lib/connect.js} +47 -88
  42. package/dist/lib/dbDialect.d.ts +87 -0
  43. package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
  44. package/dist/lib/dbHelper.d.ts +204 -0
  45. package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
  46. package/dist/lib/dbUtils.d.ts +68 -0
  47. package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
  48. package/dist/lib/jwt.d.ts +13 -0
  49. package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
  50. package/dist/lib/logger.d.ts +42 -0
  51. package/dist/lib/logger.js +1144 -0
  52. package/dist/lib/redisHelper.d.ts +185 -0
  53. package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
  54. package/dist/lib/sqlBuilder.d.ts +160 -0
  55. package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
  56. package/dist/lib/sqlCheck.d.ts +23 -0
  57. package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
  58. package/dist/lib/validator.d.ts +45 -0
  59. package/{lib/validator.ts → dist/lib/validator.js} +44 -61
  60. package/dist/loader/loadApis.d.ts +12 -0
  61. package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
  62. package/dist/loader/loadHooks.d.ts +7 -0
  63. package/dist/loader/loadHooks.js +35 -0
  64. package/dist/loader/loadPlugins.d.ts +8 -0
  65. package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
  66. package/dist/paths.d.ts +93 -0
  67. package/{paths.ts → dist/paths.js} +6 -19
  68. package/dist/plugins/cache.d.ts +16 -0
  69. package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
  70. package/dist/plugins/cipher.d.ts +12 -0
  71. package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
  72. package/dist/plugins/config.d.ts +12 -0
  73. package/dist/plugins/config.js +8 -0
  74. package/dist/plugins/db.d.ts +16 -0
  75. package/{plugins/db.ts → dist/plugins/db.js} +11 -17
  76. package/dist/plugins/jwt.d.ts +12 -0
  77. package/dist/plugins/jwt.js +12 -0
  78. package/dist/plugins/logger.d.ts +32 -0
  79. package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
  80. package/dist/plugins/redis.d.ts +16 -0
  81. package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
  82. package/dist/plugins/tool.d.ts +81 -0
  83. package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
  84. package/dist/router/api.d.ts +14 -0
  85. package/dist/router/api.js +107 -0
  86. package/dist/router/static.d.ts +9 -0
  87. package/{router/static.ts → dist/router/static.js} +20 -34
  88. package/dist/scripts/ensureDist.d.ts +1 -0
  89. package/dist/scripts/ensureDist.js +296 -0
  90. package/dist/sync/syncApi.d.ts +3 -0
  91. package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
  92. package/dist/sync/syncCache.d.ts +2 -0
  93. package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
  94. package/dist/sync/syncDev.d.ts +6 -0
  95. package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
  96. package/dist/sync/syncMenu.d.ts +14 -0
  97. package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
  98. package/dist/sync/syncTable.d.ts +151 -0
  99. package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
  100. package/{types → dist/types}/api.d.ts +12 -51
  101. package/dist/types/api.js +4 -0
  102. package/{types → dist/types}/befly.d.ts +32 -227
  103. package/dist/types/befly.js +4 -0
  104. package/{types → dist/types}/cache.d.ts +7 -15
  105. package/dist/types/cache.js +4 -0
  106. package/dist/types/cipher.d.ts +27 -0
  107. package/dist/types/cipher.js +7 -0
  108. package/{types → dist/types}/common.d.ts +8 -33
  109. package/dist/types/common.js +5 -0
  110. package/{types → dist/types}/context.d.ts +3 -5
  111. package/dist/types/context.js +4 -0
  112. package/{types → dist/types}/crypto.d.ts +0 -3
  113. package/dist/types/crypto.js +4 -0
  114. package/dist/types/database.d.ts +138 -0
  115. package/dist/types/database.js +4 -0
  116. package/dist/types/hook.d.ts +17 -0
  117. package/dist/types/hook.js +6 -0
  118. package/dist/types/jwt.d.ts +75 -0
  119. package/dist/types/jwt.js +4 -0
  120. package/dist/types/logger.d.ts +59 -0
  121. package/dist/types/logger.js +6 -0
  122. package/dist/types/plugin.d.ts +16 -0
  123. package/dist/types/plugin.js +6 -0
  124. package/dist/types/redis.d.ts +71 -0
  125. package/dist/types/redis.js +4 -0
  126. package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
  127. package/dist/types/roleApisCache.js +8 -0
  128. package/dist/types/sync.d.ts +92 -0
  129. package/dist/types/sync.js +4 -0
  130. package/dist/types/table.d.ts +34 -0
  131. package/dist/types/table.js +4 -0
  132. package/dist/types/validate.d.ts +67 -0
  133. package/dist/types/validate.js +4 -0
  134. package/dist/utils/calcPerfTime.d.ts +4 -0
  135. package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
  136. package/dist/utils/convertBigIntFields.d.ts +11 -0
  137. package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
  138. package/dist/utils/cors.d.ts +8 -0
  139. package/{utils/cors.ts → dist/utils/cors.js} +1 -3
  140. package/dist/utils/disableMenusGlob.d.ts +13 -0
  141. package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
  142. package/dist/utils/fieldClear.d.ts +11 -0
  143. package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
  144. package/dist/utils/getClientIp.d.ts +6 -0
  145. package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
  146. package/dist/utils/importDefault.d.ts +1 -0
  147. package/dist/utils/importDefault.js +29 -0
  148. package/dist/utils/isDirentDirectory.d.ts +2 -0
  149. package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
  150. package/dist/utils/loadMenuConfigs.d.ts +29 -0
  151. package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
  152. package/dist/utils/mergeAndConcat.d.ts +7 -0
  153. package/dist/utils/mergeAndConcat.js +72 -0
  154. package/dist/utils/processAtSymbol.d.ts +4 -0
  155. package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
  156. package/dist/utils/processInfo.d.ts +24 -0
  157. package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
  158. package/dist/utils/response.d.ts +20 -0
  159. package/{utils/response.ts → dist/utils/response.js} +28 -49
  160. package/dist/utils/scanAddons.d.ts +17 -0
  161. package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
  162. package/dist/utils/scanConfig.d.ts +26 -0
  163. package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
  164. package/dist/utils/scanCoreBuiltins.d.ts +3 -0
  165. package/dist/utils/scanCoreBuiltins.js +65 -0
  166. package/dist/utils/scanFiles.d.ts +30 -0
  167. package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
  168. package/dist/utils/scanSources.d.ts +10 -0
  169. package/dist/utils/scanSources.js +46 -0
  170. package/dist/utils/sortModules.d.ts +28 -0
  171. package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
  172. package/dist/utils/util.d.ts +84 -0
  173. package/dist/utils/util.js +262 -0
  174. package/package.json +26 -34
  175. package/.gitignore +0 -0
  176. package/bunfig.toml +0 -3
  177. package/checks/checkHook.ts +0 -48
  178. package/checks/checkPlugin.ts +0 -48
  179. package/configs/presetRegexp.ts +0 -225
  180. package/docs/README.md +0 -98
  181. package/docs/api/api.md +0 -1921
  182. package/docs/guide/examples.md +0 -926
  183. package/docs/guide/quickstart.md +0 -354
  184. package/docs/hooks/auth.md +0 -38
  185. package/docs/hooks/cors.md +0 -28
  186. package/docs/hooks/hook.md +0 -838
  187. package/docs/hooks/parser.md +0 -19
  188. package/docs/hooks/rateLimit.md +0 -47
  189. package/docs/infra/redis.md +0 -628
  190. package/docs/plugins/cipher.md +0 -61
  191. package/docs/plugins/database.md +0 -189
  192. package/docs/plugins/plugin.md +0 -986
  193. package/docs/reference/addon.md +0 -510
  194. package/docs/reference/config.md +0 -573
  195. package/docs/reference/logger.md +0 -495
  196. package/docs/reference/sync.md +0 -478
  197. package/docs/reference/table.md +0 -763
  198. package/docs/reference/validator.md +0 -620
  199. package/lib/asyncContext.ts +0 -43
  200. package/lib/logger.ts +0 -811
  201. package/loader/loadHooks.ts +0 -51
  202. package/plugins/config.ts +0 -13
  203. package/plugins/jwt.ts +0 -15
  204. package/router/api.ts +0 -130
  205. package/tsconfig.json +0 -8
  206. package/types/database.d.ts +0 -541
  207. package/types/hook.d.ts +0 -25
  208. package/types/jwt.d.ts +0 -118
  209. package/types/logger.d.ts +0 -65
  210. package/types/plugin.d.ts +0 -19
  211. package/types/redis.d.ts +0 -83
  212. package/types/sync.d.ts +0 -398
  213. package/types/table.d.ts +0 -216
  214. package/types/validate.d.ts +0 -69
  215. package/utils/arrayKeysToCamel.ts +0 -18
  216. package/utils/configTypes.ts +0 -3
  217. package/utils/genShortId.ts +0 -12
  218. package/utils/importDefault.ts +0 -21
  219. package/utils/keysToCamel.ts +0 -22
  220. package/utils/keysToSnake.ts +0 -22
  221. package/utils/pickFields.ts +0 -19
  222. package/utils/scanSources.ts +0 -64
  223. package/utils/sqlLog.ts +0 -37
@@ -2,115 +2,93 @@
2
2
  * SQL 构造器 - TypeScript 版本
3
3
  * 提供链式 API 构建 SQL 查询
4
4
  */
5
-
6
- import type { WhereConditions, WhereOperator, OrderDirection, SqlQuery, InsertData, UpdateData, SqlValue } from "../types/common.ts";
7
-
8
- import { SqlCheck } from "./sqlCheck.ts";
9
-
5
+ import { SqlCheck } from "./sqlCheck";
10
6
  const SqlBuilderError = {
11
- QUOTE_IDENT_NEED_STRING: (identifier: unknown) => `quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`,
7
+ QUOTE_IDENT_NEED_STRING: (identifier) => `quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`,
12
8
  IDENT_EMPTY: "SQL 标识符不能为空",
13
-
14
- FIELD_EXPR_NOT_ALLOWED: (field: string) => `字段包含函数/表达式,请使用 selectRaw/whereRaw (field: ${field})`,
15
-
9
+ FIELD_EXPR_NOT_ALLOWED: (field) => `字段包含函数/表达式,请使用 selectRaw/whereRaw (field: ${field})`,
16
10
  FROM_EMPTY: "FROM 表名不能为空",
17
- FROM_NEED_NON_EMPTY: (table: unknown) => `FROM 表名必须是非空字符串 (table: ${String(table)})`,
11
+ FROM_NEED_NON_EMPTY: (table) => `FROM 表名必须是非空字符串 (table: ${String(table)})`,
18
12
  FROM_REQUIRED: "FROM 表名是必需的",
19
13
  COUNT_NEED_FROM: "COUNT 需要 FROM 表名",
20
-
21
- TABLE_REF_TOO_MANY_PARTS: (table: string) => `不支持的表引用格式(包含过多片段)。请使用 fromRaw 显式传入复杂表达式 (table: ${table})`,
22
- TABLE_REF_SCHEMA_TOO_DEEP: (table: string) => `不支持的表引用格式(schema 层级过深)。请使用 fromRaw (table: ${table})`,
23
- SCHEMA_QUOTE_NOT_PAIRED: (schema: string) => `schema 标识符引用不完整,请使用成对的 \`...\` 或 "..." (schema: ${schema})`,
24
- TABLE_QUOTE_NOT_PAIRED: (tableName: string) => `table 标识符引用不完整,请使用成对的 \`...\` 或 "..." (table: ${tableName})`,
25
-
14
+ TABLE_REF_TOO_MANY_PARTS: (table) => `不支持的表引用格式(包含过多片段)。请使用 fromRaw 显式传入复杂表达式 (table: ${table})`,
15
+ TABLE_REF_SCHEMA_TOO_DEEP: (table) => `不支持的表引用格式(schema 层级过深)。请使用 fromRaw (table: ${table})`,
16
+ SCHEMA_QUOTE_NOT_PAIRED: (schema) => `schema 标识符引用不完整,请使用成对的 \`...\` 或 "..." (schema: ${schema})`,
17
+ TABLE_QUOTE_NOT_PAIRED: (tableName) => `table 标识符引用不完整,请使用成对的 \`...\` 或 "..." (table: ${tableName})`,
26
18
  SELECT_FIELDS_INVALID: "SELECT 字段必须是字符串或数组",
27
- SELECT_RAW_NEED_NON_EMPTY: (expr: unknown) => `selectRaw 需要非空字符串 (expr: ${String(expr)})`,
28
- FROM_RAW_NEED_NON_EMPTY: (tableExpr: unknown) => `fromRaw 需要非空字符串 (tableExpr: ${String(tableExpr)})`,
29
- WHERE_RAW_NEED_NON_EMPTY: (sql: unknown) => `whereRaw 需要非空字符串 (sql: ${String(sql)})`,
19
+ SELECT_RAW_NEED_NON_EMPTY: (expr) => `selectRaw 需要非空字符串 (expr: ${String(expr)})`,
20
+ FROM_RAW_NEED_NON_EMPTY: (tableExpr) => `fromRaw 需要非空字符串 (tableExpr: ${String(tableExpr)})`,
21
+ WHERE_RAW_NEED_NON_EMPTY: (sql) => `whereRaw 需要非空字符串 (sql: ${String(sql)})`,
30
22
  WHERE_VALUE_REQUIRED: "where(field, value) 不允许省略 value。若需传入原始 WHERE,请使用 whereRaw",
31
-
32
- JOIN_NEED_STRING: (table: unknown, on: unknown) => `JOIN 表名和条件必须是字符串 (table: ${String(table)}, on: ${String(on)})`,
33
-
23
+ JOIN_NEED_STRING: (table, on) => `JOIN 表名和条件必须是字符串 (table: ${String(table)}, on: ${String(on)})`,
34
24
  ORDER_BY_NEED_ARRAY: 'orderBy 必须是字符串数组,格式为 "字段#方向"',
35
- ORDER_BY_ITEM_NEED_HASH: (item: unknown) => `orderBy 字段必须是 "字段#方向" 格式的字符串(例如:"name#ASC", "id#DESC") (item: ${String(item)})`,
36
- ORDER_BY_FIELD_EMPTY: (item: string) => `orderBy 中字段名不能为空 (item: ${item})`,
37
- ORDER_BY_DIR_INVALID: (direction: string) => `ORDER BY 方向必须是 ASC 或 DESC (direction: ${direction})`,
38
-
39
- LIMIT_MUST_NON_NEGATIVE: (count: unknown) => `LIMIT 数量必须是非负数 (count: ${String(count)})`,
40
- OFFSET_MUST_NON_NEGATIVE: (offset: unknown) => `OFFSET 必须是非负数 (offset: ${String(offset)})`,
41
- OFFSET_COUNT_MUST_NON_NEGATIVE: (count: unknown) => `OFFSET 必须是非负数 (count: ${String(count)})`,
42
-
43
- INSERT_NEED_TABLE: (table: unknown) => `INSERT 需要表名 (table: ${String(table)})`,
44
- INSERT_NEED_DATA: (table: unknown, data: unknown) => `INSERT 需要数据 (table: ${String(table)}, data: ${JSON.stringify(data)})`,
45
- INSERT_NEED_AT_LEAST_ONE_FIELD: (table: string) => `插入数据必须至少有一个字段 (table: ${table})`,
46
-
25
+ ORDER_BY_ITEM_NEED_HASH: (item) => `orderBy 字段必须是 "字段#方向" 格式的字符串(例如:"name#ASC", "id#DESC") (item: ${String(item)})`,
26
+ ORDER_BY_FIELD_EMPTY: (item) => `orderBy 中字段名不能为空 (item: ${item})`,
27
+ ORDER_BY_DIR_INVALID: (direction) => `ORDER BY 方向必须是 ASC 或 DESC (direction: ${direction})`,
28
+ LIMIT_MUST_NON_NEGATIVE: (count) => `LIMIT 数量必须是非负数 (count: ${String(count)})`,
29
+ OFFSET_MUST_NON_NEGATIVE: (offset) => `OFFSET 必须是非负数 (offset: ${String(offset)})`,
30
+ OFFSET_COUNT_MUST_NON_NEGATIVE: (count) => `OFFSET 必须是非负数 (count: ${String(count)})`,
31
+ INSERT_NEED_TABLE: (table) => `INSERT 需要表名 (table: ${String(table)})`,
32
+ INSERT_NEED_DATA: (table, data) => `INSERT 需要数据 (table: ${String(table)}, data: ${JSON.stringify(data)})`,
33
+ INSERT_NEED_AT_LEAST_ONE_FIELD: (table) => `插入数据必须至少有一个字段 (table: ${table})`,
47
34
  UPDATE_NEED_TABLE: "UPDATE 需要表名",
48
35
  UPDATE_NEED_OBJECT: "UPDATE 需要数据对象",
49
36
  UPDATE_NEED_AT_LEAST_ONE_FIELD: "更新数据必须至少有一个字段",
50
37
  UPDATE_NEED_WHERE: "为安全起见,UPDATE 需要 WHERE 条件",
51
-
52
38
  DELETE_NEED_TABLE: "DELETE 需要表名",
53
39
  DELETE_NEED_WHERE: "为安全起见,DELETE 需要 WHERE 条件",
54
-
55
- TO_DELETE_IN_NEED_TABLE: (table: unknown) => `toDeleteInSql 需要非空表名 (table: ${String(table)})`,
56
- TO_DELETE_IN_NEED_ID_FIELD: (idField: unknown) => `toDeleteInSql 需要非空 idField (idField: ${String(idField)})`,
40
+ TO_DELETE_IN_NEED_TABLE: (table) => `toDeleteInSql 需要非空表名 (table: ${String(table)})`,
41
+ TO_DELETE_IN_NEED_ID_FIELD: (idField) => `toDeleteInSql 需要非空 idField (idField: ${String(idField)})`,
57
42
  TO_DELETE_IN_NEED_IDS: "toDeleteInSql 需要 ids 数组",
58
-
59
- TO_UPDATE_CASE_NEED_TABLE: (table: unknown) => `toUpdateCaseByIdSql 需要非空表名 (table: ${String(table)})`,
60
- TO_UPDATE_CASE_NEED_ID_FIELD: (idField: unknown) => `toUpdateCaseByIdSql 需要非空 idField (idField: ${String(idField)})`,
43
+ TO_UPDATE_CASE_NEED_TABLE: (table) => `toUpdateCaseByIdSql 需要非空表名 (table: ${String(table)})`,
44
+ TO_UPDATE_CASE_NEED_ID_FIELD: (idField) => `toUpdateCaseByIdSql 需要非空 idField (idField: ${String(idField)})`,
61
45
  TO_UPDATE_CASE_NEED_ROWS: "toUpdateCaseByIdSql 需要 rows 数组",
62
46
  TO_UPDATE_CASE_NEED_FIELDS: "toUpdateCaseByIdSql 需要 fields 数组"
63
- } as const;
64
-
47
+ };
65
48
  /**
66
49
  * SQL 构建器类
67
50
  */
68
51
  export class SqlBuilder {
69
- private _select: string[] = [];
70
- private _from: string = "";
71
- private _where: string[] = [];
72
- private _joins: string[] = [];
73
- private _orderBy: string[] = [];
74
- private _groupBy: string[] = [];
75
- private _having: string[] = [];
76
- private _limit: number | null = null;
77
- private _offset: number | null = null;
78
- private _params: SqlValue[] = [];
79
- private _quoteIdent: (identifier: string) => string;
80
-
81
- constructor(options?: { quoteIdent?: (identifier: string) => string }) {
52
+ _select = [];
53
+ _from = "";
54
+ _where = [];
55
+ _joins = [];
56
+ _orderBy = [];
57
+ _groupBy = [];
58
+ _having = [];
59
+ _limit = null;
60
+ _offset = null;
61
+ _params = [];
62
+ _quoteIdent;
63
+ constructor(options) {
82
64
  if (options && options.quoteIdent) {
83
65
  this._quoteIdent = options.quoteIdent;
84
- } else {
85
- this._quoteIdent = (identifier: string) => {
66
+ }
67
+ else {
68
+ this._quoteIdent = (identifier) => {
86
69
  if (typeof identifier !== "string") {
87
70
  throw new Error(SqlBuilderError.QUOTE_IDENT_NEED_STRING(identifier));
88
71
  }
89
-
90
72
  const trimmed = identifier.trim();
91
73
  if (!trimmed) {
92
74
  throw new Error(SqlBuilderError.IDENT_EMPTY);
93
75
  }
94
-
95
76
  // 默认行为(MySQL 风格):允许特殊字符,但对反引号进行转义
96
77
  const escaped = trimmed.replace(/`/g, "``");
97
78
  return `\`${escaped}\``;
98
79
  };
99
80
  }
100
81
  }
101
-
102
- private _isQuotedIdent(value: string): boolean {
82
+ _isQuotedIdent(value) {
103
83
  return SqlCheck.isQuotedIdentPaired(value);
104
84
  }
105
-
106
- private _startsWithQuote(value: string): boolean {
85
+ _startsWithQuote(value) {
107
86
  return SqlCheck.startsWithQuote(value);
108
87
  }
109
-
110
88
  /**
111
89
  * 重置构建器状态
112
90
  */
113
- reset(): this {
91
+ reset() {
114
92
  this._select = [];
115
93
  this._from = "";
116
94
  this._where = [];
@@ -123,32 +101,27 @@ export class SqlBuilder {
123
101
  this._params = [];
124
102
  return this;
125
103
  }
126
-
127
104
  /**
128
105
  * 转义字段名
129
106
  */
130
- private _escapeField(field: string): string {
107
+ _escapeField(field) {
131
108
  if (typeof field !== "string") {
132
109
  return field;
133
110
  }
134
-
135
111
  field = field.trim();
136
-
137
112
  // 防止不完整引用被误认为“已安全引用”
138
113
  SqlCheck.assertPairedQuotedIdentIfStartsWithQuote(field, "字段标识符");
139
-
140
114
  // 如果是 * 或已经被引用,直接返回
141
115
  if (field === "*" || this._isQuotedIdent(field)) {
142
116
  return field;
143
117
  }
144
-
145
118
  try {
146
119
  SqlCheck.assertNoExprField(field);
147
- } catch {
120
+ }
121
+ catch {
148
122
  // 保持 SqlBuilder 报错文案统一
149
123
  throw new Error(SqlBuilderError.FIELD_EXPR_NOT_ALLOWED(field));
150
124
  }
151
-
152
125
  // 处理别名(AS关键字)
153
126
  if (field.toUpperCase().includes(" AS ")) {
154
127
  const parts = field.split(/\s+AS\s+/i);
@@ -158,99 +131,85 @@ export class SqlBuilder {
158
131
  SqlCheck.assertSafeAlias(aliasPart);
159
132
  return `${this._escapeField(fieldPart)} AS ${aliasPart}`;
160
133
  }
161
-
162
134
  // 处理表名.字段名的情况(多表联查)
163
135
  if (field.includes(".")) {
164
136
  const parts = field.split(".");
165
137
  return parts
166
138
  .map((part) => {
167
- part = part.trim();
168
- if (part === "*" || this._isQuotedIdent(part)) {
169
- return part;
170
- }
171
- SqlCheck.assertPairedQuotedIdentIfStartsWithQuote(part, "字段标识符");
172
- return this._quoteIdent(part);
173
- })
139
+ part = part.trim();
140
+ if (part === "*" || this._isQuotedIdent(part)) {
141
+ return part;
142
+ }
143
+ SqlCheck.assertPairedQuotedIdentIfStartsWithQuote(part, "字段标识符");
144
+ return this._quoteIdent(part);
145
+ })
174
146
  .join(".");
175
147
  }
176
-
177
148
  // 处理单个字段名
178
149
  return this._quoteIdent(field);
179
150
  }
180
-
181
- private _validateIdentifierPart(part: string, kind: "table" | "schema" | "alias" | "field"): void {
151
+ _validateIdentifierPart(part, kind) {
182
152
  SqlCheck.assertSafeIdentifierPart(part, kind);
183
153
  }
184
-
185
154
  /**
186
155
  * 转义表名
187
156
  */
188
- private _escapeTable(table: string): string {
157
+ _escapeTable(table) {
189
158
  if (typeof table !== "string") {
190
159
  return table;
191
160
  }
192
-
193
161
  table = table.trim();
194
-
195
162
  // 防止不完整引用被误认为“已安全引用”
196
163
  if (this._startsWithQuote(table) && !this._isQuotedIdent(table)) {
197
164
  // 注意:这里可能是 `table` alias 的形式,整体不成对,但 namePart 可能成对。
198
165
  // 因此这里只做“整体是单段引用”的判断,具体在后续 namePart 分支里校验。
199
166
  }
200
-
201
167
  if (this._isQuotedIdent(table)) {
202
168
  return table;
203
169
  }
204
-
205
170
  const parts = table.split(/\s+/).filter((p) => p.length > 0);
206
171
  if (parts.length === 0) {
207
172
  throw new Error(SqlBuilderError.FROM_EMPTY);
208
173
  }
209
-
210
174
  if (parts.length > 2) {
211
175
  throw new Error(SqlBuilderError.TABLE_REF_TOO_MANY_PARTS(table));
212
176
  }
213
-
214
177
  const namePart = parts[0].trim();
215
178
  const aliasPart = parts.length === 2 ? parts[1].trim() : null;
216
-
217
179
  const nameSegments = namePart.split(".");
218
180
  if (nameSegments.length > 2) {
219
181
  throw new Error(SqlBuilderError.TABLE_REF_SCHEMA_TOO_DEEP(table));
220
182
  }
221
-
222
183
  let escapedName = "";
223
184
  if (nameSegments.length === 2) {
224
185
  const schema = nameSegments[0].trim();
225
186
  const tableName = nameSegments[1].trim();
226
-
227
187
  const escapedSchema = this._isQuotedIdent(schema)
228
188
  ? schema
229
189
  : (() => {
230
- if (this._startsWithQuote(schema) && !this._isQuotedIdent(schema)) {
231
- throw new Error(SqlBuilderError.SCHEMA_QUOTE_NOT_PAIRED(schema));
232
- }
233
- this._validateIdentifierPart(schema, "schema");
234
- return this._quoteIdent(schema);
235
- })();
236
-
190
+ if (this._startsWithQuote(schema) && !this._isQuotedIdent(schema)) {
191
+ throw new Error(SqlBuilderError.SCHEMA_QUOTE_NOT_PAIRED(schema));
192
+ }
193
+ this._validateIdentifierPart(schema, "schema");
194
+ return this._quoteIdent(schema);
195
+ })();
237
196
  const escapedTableName = this._isQuotedIdent(tableName)
238
197
  ? tableName
239
198
  : (() => {
240
- if (this._startsWithQuote(tableName) && !this._isQuotedIdent(tableName)) {
241
- throw new Error(SqlBuilderError.TABLE_QUOTE_NOT_PAIRED(tableName));
242
- }
243
- this._validateIdentifierPart(tableName, "table");
244
- return this._quoteIdent(tableName);
245
- })();
246
-
199
+ if (this._startsWithQuote(tableName) && !this._isQuotedIdent(tableName)) {
200
+ throw new Error(SqlBuilderError.TABLE_QUOTE_NOT_PAIRED(tableName));
201
+ }
202
+ this._validateIdentifierPart(tableName, "table");
203
+ return this._quoteIdent(tableName);
204
+ })();
247
205
  escapedName = `${escapedSchema}.${escapedTableName}`;
248
- } else {
206
+ }
207
+ else {
249
208
  const tableName = nameSegments[0].trim();
250
-
251
209
  if (this._isQuotedIdent(tableName)) {
252
210
  escapedName = tableName;
253
- } else {
211
+ }
212
+ else {
254
213
  if (this._startsWithQuote(tableName) && !this._isQuotedIdent(tableName)) {
255
214
  throw new Error(SqlBuilderError.TABLE_QUOTE_NOT_PAIRED(tableName));
256
215
  }
@@ -258,28 +217,23 @@ export class SqlBuilder {
258
217
  escapedName = this._quoteIdent(tableName);
259
218
  }
260
219
  }
261
-
262
220
  if (aliasPart) {
263
221
  this._validateIdentifierPart(aliasPart, "alias");
264
222
  return `${escapedName} ${aliasPart}`;
265
223
  }
266
-
267
224
  return escapedName;
268
225
  }
269
-
270
226
  /**
271
227
  * 验证参数
272
228
  */
273
- private _validateParam(value: any): void {
229
+ _validateParam(value) {
274
230
  SqlCheck.assertNoUndefinedParam(value, "SQL 参数值");
275
231
  }
276
-
277
232
  /**
278
233
  * 处理单个操作符条件
279
234
  */
280
- private _applyOperator(fieldName: string, operator: WhereOperator, value: any): void {
235
+ _applyOperator(fieldName, operator, value) {
281
236
  const escapedField = this._escapeField(fieldName);
282
-
283
237
  switch (operator) {
284
238
  case "$ne":
285
239
  case "$not":
@@ -287,7 +241,6 @@ export class SqlBuilder {
287
241
  this._where.push(`${escapedField} != ?`);
288
242
  this._params.push(value);
289
243
  break;
290
-
291
244
  case "$in":
292
245
  if (!Array.isArray(value)) {
293
246
  throw new Error(`$in 操作符的值必须是数组 (operator: ${operator})`);
@@ -299,7 +252,6 @@ export class SqlBuilder {
299
252
  this._where.push(`${escapedField} IN (${placeholders})`);
300
253
  this._params.push(...value);
301
254
  break;
302
-
303
255
  case "$nin":
304
256
  case "$notIn":
305
257
  if (!Array.isArray(value)) {
@@ -312,43 +264,36 @@ export class SqlBuilder {
312
264
  this._where.push(`${escapedField} NOT IN (${placeholders2})`);
313
265
  this._params.push(...value);
314
266
  break;
315
-
316
267
  case "$like":
317
268
  this._validateParam(value);
318
269
  this._where.push(`${escapedField} LIKE ?`);
319
270
  this._params.push(value);
320
271
  break;
321
-
322
272
  case "$notLike":
323
273
  this._validateParam(value);
324
274
  this._where.push(`${escapedField} NOT LIKE ?`);
325
275
  this._params.push(value);
326
276
  break;
327
-
328
277
  case "$gt":
329
278
  this._validateParam(value);
330
279
  this._where.push(`${escapedField} > ?`);
331
280
  this._params.push(value);
332
281
  break;
333
-
334
282
  case "$gte":
335
283
  this._validateParam(value);
336
284
  this._where.push(`${escapedField} >= ?`);
337
285
  this._params.push(value);
338
286
  break;
339
-
340
287
  case "$lt":
341
288
  this._validateParam(value);
342
289
  this._where.push(`${escapedField} < ?`);
343
290
  this._params.push(value);
344
291
  break;
345
-
346
292
  case "$lte":
347
293
  this._validateParam(value);
348
294
  this._where.push(`${escapedField} <= ?`);
349
295
  this._params.push(value);
350
296
  break;
351
-
352
297
  case "$between":
353
298
  if (Array.isArray(value) && value.length === 2) {
354
299
  this._validateParam(value[0]);
@@ -357,7 +302,6 @@ export class SqlBuilder {
357
302
  this._params.push(value[0], value[1]);
358
303
  }
359
304
  break;
360
-
361
305
  case "$notBetween":
362
306
  if (Array.isArray(value) && value.length === 2) {
363
307
  this._validateParam(value[0]);
@@ -366,19 +310,16 @@ export class SqlBuilder {
366
310
  this._params.push(value[0], value[1]);
367
311
  }
368
312
  break;
369
-
370
313
  case "$null":
371
314
  if (value === true) {
372
315
  this._where.push(`${escapedField} IS NULL`);
373
316
  }
374
317
  break;
375
-
376
318
  case "$notNull":
377
319
  if (value === true) {
378
320
  this._where.push(`${escapedField} IS NOT NULL`);
379
321
  }
380
322
  break;
381
-
382
323
  default:
383
324
  // 等于条件
384
325
  this._validateParam(value);
@@ -386,30 +327,27 @@ export class SqlBuilder {
386
327
  this._params.push(value);
387
328
  }
388
329
  }
389
-
390
330
  /**
391
331
  * 处理复杂的 WHERE 条件对象
392
332
  */
393
- private _processWhereConditions(whereObj: WhereConditions): void {
333
+ _processWhereConditions(whereObj) {
394
334
  if (!whereObj || typeof whereObj !== "object") {
395
335
  return;
396
336
  }
397
-
398
337
  Object.entries(whereObj).forEach(([key, value]) => {
399
338
  // 跳过undefined值
400
339
  if (value === undefined) {
401
340
  return;
402
341
  }
403
-
404
342
  if (key === "$and") {
405
343
  if (Array.isArray(value)) {
406
344
  value.forEach((condition) => this._processWhereConditions(condition));
407
345
  }
408
- } else if (key === "$or") {
346
+ }
347
+ else if (key === "$or") {
409
348
  if (Array.isArray(value)) {
410
- const orConditions: string[] = [];
411
- const tempParams: SqlValue[] = [];
412
-
349
+ const orConditions = [];
350
+ const tempParams = [];
413
351
  value.forEach((condition) => {
414
352
  const tempBuilder = new SqlBuilder({ quoteIdent: this._quoteIdent });
415
353
  tempBuilder._processWhereConditions(condition);
@@ -418,26 +356,28 @@ export class SqlBuilder {
418
356
  tempParams.push(...tempBuilder._params);
419
357
  }
420
358
  });
421
-
422
359
  if (orConditions.length > 0) {
423
360
  this._where.push(`(${orConditions.join(" OR ")})`);
424
361
  this._params.push(...tempParams);
425
362
  }
426
363
  }
427
- } else if (key.includes("$")) {
364
+ }
365
+ else if (key.includes("$")) {
428
366
  // 一级属性格式:age$gt, role$in 等
429
367
  const lastDollarIndex = key.lastIndexOf("$");
430
368
  const fieldName = key.substring(0, lastDollarIndex);
431
- const operator = ("$" + key.substring(lastDollarIndex + 1)) as WhereOperator;
369
+ const operator = ("$" + key.substring(lastDollarIndex + 1));
432
370
  this._applyOperator(fieldName, operator, value);
433
- } else {
371
+ }
372
+ else {
434
373
  // 检查值是否为对象(嵌套条件)
435
374
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
436
375
  // 嵌套条件:如 { age: { $gt: 18 } }
437
376
  for (const [op, val] of Object.entries(value)) {
438
- this._applyOperator(key, op as WhereOperator, val);
377
+ this._applyOperator(key, op, val);
439
378
  }
440
- } else {
379
+ }
380
+ else {
441
381
  // 简单的等于条件
442
382
  this._validateParam(value);
443
383
  const escapedKey = this._escapeField(key);
@@ -447,75 +387,65 @@ export class SqlBuilder {
447
387
  }
448
388
  });
449
389
  }
450
-
451
390
  /**
452
391
  * 获取 WHERE 条件(供 DbHelper 使用)
453
392
  */
454
- getWhereConditions(): { sql: string; params: SqlValue[] } {
393
+ getWhereConditions() {
455
394
  return {
456
395
  sql: this._where.length > 0 ? this._where.join(" AND ") : "",
457
396
  params: [...this._params]
458
397
  };
459
398
  }
460
-
461
399
  /**
462
400
  * SELECT 字段
463
401
  */
464
- select(fields: string | string[] = "*"): this {
402
+ select(fields = "*") {
465
403
  if (Array.isArray(fields)) {
466
404
  this._select = [...this._select, ...fields.map((field) => this._escapeField(field))];
467
- } else if (typeof fields === "string") {
405
+ }
406
+ else if (typeof fields === "string") {
468
407
  this._select.push(this._escapeField(fields));
469
- } else {
408
+ }
409
+ else {
470
410
  throw new Error(SqlBuilderError.SELECT_FIELDS_INVALID);
471
411
  }
472
412
  return this;
473
413
  }
474
-
475
414
  /**
476
415
  * SELECT 原始表达式(不做转义)
477
416
  */
478
- selectRaw(expr: string): this {
417
+ selectRaw(expr) {
479
418
  if (typeof expr !== "string" || !expr.trim()) {
480
419
  throw new Error(SqlBuilderError.SELECT_RAW_NEED_NON_EMPTY(expr));
481
420
  }
482
421
  this._select.push(expr);
483
422
  return this;
484
423
  }
485
-
486
424
  /**
487
425
  * FROM 表名
488
426
  */
489
- from(table: string): this {
427
+ from(table) {
490
428
  if (typeof table !== "string" || !table.trim()) {
491
429
  throw new Error(SqlBuilderError.FROM_NEED_NON_EMPTY(table));
492
430
  }
493
431
  this._from = this._escapeTable(table.trim());
494
432
  return this;
495
433
  }
496
-
497
434
  /**
498
435
  * FROM 原始表达式(不做转义)
499
436
  */
500
- fromRaw(tableExpr: string): this {
437
+ fromRaw(tableExpr) {
501
438
  if (typeof tableExpr !== "string" || !tableExpr.trim()) {
502
439
  throw new Error(SqlBuilderError.FROM_RAW_NEED_NON_EMPTY(tableExpr));
503
440
  }
504
441
  this._from = tableExpr;
505
442
  return this;
506
443
  }
507
-
508
- /**
509
- * WHERE 条件
510
- */
511
- where(condition: WhereConditions): this;
512
- where(field: string, value: SqlValue): this;
513
- where(conditionOrField: WhereConditions | string, value?: SqlValue): this {
444
+ where(conditionOrField, value) {
514
445
  if (typeof conditionOrField === "object" && conditionOrField !== null) {
515
446
  this._processWhereConditions(conditionOrField);
516
447
  return this;
517
448
  }
518
-
519
449
  if (typeof conditionOrField === "string") {
520
450
  if (value === undefined) {
521
451
  throw new Error(SqlBuilderError.WHERE_VALUE_REQUIRED);
@@ -526,30 +456,25 @@ export class SqlBuilder {
526
456
  this._params.push(value);
527
457
  return this;
528
458
  }
529
-
530
459
  return this;
531
460
  }
532
-
533
461
  /**
534
462
  * WHERE 原始片段(不做转义),可附带参数。
535
463
  */
536
- whereRaw(sql: string, params?: SqlValue[]): this {
464
+ whereRaw(sql, params) {
537
465
  if (typeof sql !== "string" || !sql.trim()) {
538
466
  throw new Error(SqlBuilderError.WHERE_RAW_NEED_NON_EMPTY(sql));
539
467
  }
540
-
541
468
  this._where.push(sql);
542
469
  if (params && params.length > 0) {
543
470
  this._params.push(...params);
544
471
  }
545
-
546
472
  return this;
547
473
  }
548
-
549
474
  /**
550
475
  * LEFT JOIN
551
476
  */
552
- leftJoin(table: string, on: string): this {
477
+ leftJoin(table, on) {
553
478
  if (typeof table !== "string" || typeof on !== "string") {
554
479
  throw new Error(SqlBuilderError.JOIN_NEED_STRING(table, on));
555
480
  }
@@ -557,11 +482,10 @@ export class SqlBuilder {
557
482
  this._joins.push(`LEFT JOIN ${escapedTable} ON ${on}`);
558
483
  return this;
559
484
  }
560
-
561
485
  /**
562
486
  * RIGHT JOIN
563
487
  */
564
- rightJoin(table: string, on: string): this {
488
+ rightJoin(table, on) {
565
489
  if (typeof table !== "string" || typeof on !== "string") {
566
490
  throw new Error(SqlBuilderError.JOIN_NEED_STRING(table, on));
567
491
  }
@@ -569,11 +493,10 @@ export class SqlBuilder {
569
493
  this._joins.push(`RIGHT JOIN ${escapedTable} ON ${on}`);
570
494
  return this;
571
495
  }
572
-
573
496
  /**
574
497
  * INNER JOIN
575
498
  */
576
- innerJoin(table: string, on: string): this {
499
+ innerJoin(table, on) {
577
500
  if (typeof table !== "string" || typeof on !== "string") {
578
501
  throw new Error(SqlBuilderError.JOIN_NEED_STRING(table, on));
579
502
  }
@@ -581,67 +504,58 @@ export class SqlBuilder {
581
504
  this._joins.push(`INNER JOIN ${escapedTable} ON ${on}`);
582
505
  return this;
583
506
  }
584
-
585
507
  /**
586
508
  * ORDER BY
587
509
  * @param fields - 格式为 ["field#ASC", "field2#DESC"]
588
510
  */
589
- orderBy(fields: string[]): this {
511
+ orderBy(fields) {
590
512
  if (!Array.isArray(fields)) {
591
513
  throw new Error(SqlBuilderError.ORDER_BY_NEED_ARRAY);
592
514
  }
593
-
594
515
  fields.forEach((item) => {
595
516
  if (typeof item !== "string" || !item.includes("#")) {
596
517
  throw new Error(SqlBuilderError.ORDER_BY_ITEM_NEED_HASH(item));
597
518
  }
598
-
599
519
  const [fieldName, direction] = item.split("#");
600
520
  const cleanField = fieldName.trim();
601
- const cleanDir = direction.trim().toUpperCase() as OrderDirection;
602
-
521
+ const cleanDir = direction.trim().toUpperCase();
603
522
  if (!cleanField) {
604
523
  throw new Error(SqlBuilderError.ORDER_BY_FIELD_EMPTY(item));
605
524
  }
606
-
607
525
  if (!["ASC", "DESC"].includes(cleanDir)) {
608
526
  throw new Error(SqlBuilderError.ORDER_BY_DIR_INVALID(cleanDir));
609
527
  }
610
-
611
528
  const escapedField = this._escapeField(cleanField);
612
529
  this._orderBy.push(`${escapedField} ${cleanDir}`);
613
530
  });
614
-
615
531
  return this;
616
532
  }
617
-
618
533
  /**
619
534
  * GROUP BY
620
535
  */
621
- groupBy(field: string | string[]): this {
536
+ groupBy(field) {
622
537
  if (Array.isArray(field)) {
623
538
  const escapedFields = field.filter((f) => typeof f === "string").map((f) => this._escapeField(f));
624
539
  this._groupBy = [...this._groupBy, ...escapedFields];
625
- } else if (typeof field === "string") {
540
+ }
541
+ else if (typeof field === "string") {
626
542
  this._groupBy.push(this._escapeField(field));
627
543
  }
628
544
  return this;
629
545
  }
630
-
631
546
  /**
632
547
  * HAVING
633
548
  */
634
- having(condition: string): this {
549
+ having(condition) {
635
550
  if (typeof condition === "string") {
636
551
  this._having.push(condition);
637
552
  }
638
553
  return this;
639
554
  }
640
-
641
555
  /**
642
556
  * LIMIT
643
557
  */
644
- limit(count: number, offset?: number): this {
558
+ limit(count, offset) {
645
559
  if (typeof count !== "number" || count < 0) {
646
560
  throw new Error(SqlBuilderError.LIMIT_MUST_NON_NEGATIVE(count));
647
561
  }
@@ -654,186 +568,151 @@ export class SqlBuilder {
654
568
  }
655
569
  return this;
656
570
  }
657
-
658
571
  /**
659
572
  * OFFSET
660
573
  */
661
- offset(count: number): this {
574
+ offset(count) {
662
575
  if (typeof count !== "number" || count < 0) {
663
576
  throw new Error(SqlBuilderError.OFFSET_COUNT_MUST_NON_NEGATIVE(count));
664
577
  }
665
578
  this._offset = Math.floor(count);
666
579
  return this;
667
580
  }
668
-
669
581
  /**
670
582
  * 构建 SELECT 查询
671
583
  */
672
- toSelectSql(): SqlQuery {
584
+ toSelectSql() {
673
585
  let sql = "SELECT ";
674
-
675
586
  sql += this._select.length > 0 ? this._select.join(", ") : "*";
676
-
677
587
  if (!this._from) {
678
588
  throw new Error(SqlBuilderError.FROM_REQUIRED);
679
589
  }
680
590
  sql += ` FROM ${this._from}`;
681
-
682
591
  if (this._joins.length > 0) {
683
592
  sql += " " + this._joins.join(" ");
684
593
  }
685
-
686
594
  if (this._where.length > 0) {
687
595
  sql += " WHERE " + this._where.join(" AND ");
688
596
  }
689
-
690
597
  if (this._groupBy.length > 0) {
691
598
  sql += " GROUP BY " + this._groupBy.join(", ");
692
599
  }
693
-
694
600
  if (this._having.length > 0) {
695
601
  sql += " HAVING " + this._having.join(" AND ");
696
602
  }
697
-
698
603
  if (this._orderBy.length > 0) {
699
604
  sql += " ORDER BY " + this._orderBy.join(", ");
700
605
  }
701
-
702
606
  if (this._limit !== null) {
703
607
  sql += ` LIMIT ${this._limit}`;
704
608
  if (this._offset !== null) {
705
609
  sql += ` OFFSET ${this._offset}`;
706
610
  }
707
611
  }
708
-
709
612
  return { sql, params: [...this._params] };
710
613
  }
711
-
712
614
  /**
713
615
  * 构建 INSERT 查询
714
616
  */
715
- toInsertSql(table: string, data: InsertData): SqlQuery {
617
+ toInsertSql(table, data) {
716
618
  if (!table || typeof table !== "string") {
717
619
  throw new Error(SqlBuilderError.INSERT_NEED_TABLE(table));
718
620
  }
719
-
720
621
  if (!data || typeof data !== "object") {
721
622
  throw new Error(SqlBuilderError.INSERT_NEED_DATA(table, data));
722
623
  }
723
-
724
624
  const escapedTable = this._escapeTable(table);
725
-
726
625
  if (Array.isArray(data)) {
727
- const fields = SqlCheck.assertBatchInsertRowsConsistent(data as Array<Record<string, unknown>>, { table: table });
728
-
626
+ const fields = SqlCheck.assertBatchInsertRowsConsistent(data, { table: table });
729
627
  const escapedFields = fields.map((field) => this._escapeField(field));
730
628
  const placeholders = fields.map(() => "?").join(", ");
731
629
  const values = data.map(() => `(${placeholders})`).join(", ");
732
-
733
630
  const sql = `INSERT INTO ${escapedTable} (${escapedFields.join(", ")}) VALUES ${values}`;
734
- const params: SqlValue[] = [];
631
+ const params = [];
735
632
  for (let i = 0; i < data.length; i++) {
736
- const row = data[i] as Record<string, SqlValue>;
633
+ const row = data[i];
737
634
  for (const field of fields) {
738
635
  params.push(row[field]);
739
636
  }
740
637
  }
741
-
742
638
  return { sql, params };
743
- } else {
639
+ }
640
+ else {
744
641
  const fields = Object.keys(data);
745
642
  if (fields.length === 0) {
746
643
  throw new Error(SqlBuilderError.INSERT_NEED_AT_LEAST_ONE_FIELD(table));
747
644
  }
748
-
749
645
  for (const field of fields) {
750
- this._validateParam((data as any)[field]);
646
+ this._validateParam(data[field]);
751
647
  }
752
-
753
648
  const escapedFields = fields.map((field) => this._escapeField(field));
754
649
  const placeholders = fields.map(() => "?").join(", ");
755
650
  const sql = `INSERT INTO ${escapedTable} (${escapedFields.join(", ")}) VALUES (${placeholders})`;
756
651
  const params = fields.map((field) => data[field]);
757
-
758
652
  return { sql, params };
759
653
  }
760
654
  }
761
-
762
655
  /**
763
656
  * 构建 UPDATE 查询
764
657
  */
765
- toUpdateSql(table: string, data: UpdateData): SqlQuery {
658
+ toUpdateSql(table, data) {
766
659
  if (!table || typeof table !== "string") {
767
660
  throw new Error(SqlBuilderError.UPDATE_NEED_TABLE);
768
661
  }
769
-
770
662
  if (!data || typeof data !== "object" || Array.isArray(data)) {
771
663
  throw new Error(SqlBuilderError.UPDATE_NEED_OBJECT);
772
664
  }
773
-
774
665
  const fields = Object.keys(data);
775
666
  if (fields.length === 0) {
776
667
  throw new Error(SqlBuilderError.UPDATE_NEED_AT_LEAST_ONE_FIELD);
777
668
  }
778
-
779
669
  const escapedTable = this._escapeTable(table);
780
670
  const setFields = fields.map((field) => `${this._escapeField(field)} = ?`);
781
- const params: SqlValue[] = [...Object.values(data), ...this._params];
782
-
671
+ const params = [...Object.values(data), ...this._params];
783
672
  let sql = `UPDATE ${escapedTable} SET ${setFields.join(", ")}`;
784
-
785
673
  if (this._where.length > 0) {
786
674
  sql += " WHERE " + this._where.join(" AND ");
787
- } else {
675
+ }
676
+ else {
788
677
  throw new Error(SqlBuilderError.UPDATE_NEED_WHERE);
789
678
  }
790
-
791
679
  return { sql, params };
792
680
  }
793
-
794
681
  /**
795
682
  * 构建 DELETE 查询
796
683
  */
797
- toDeleteSql(table: string): SqlQuery {
684
+ toDeleteSql(table) {
798
685
  if (!table || typeof table !== "string") {
799
686
  throw new Error(SqlBuilderError.DELETE_NEED_TABLE);
800
687
  }
801
-
802
688
  const escapedTable = this._escapeTable(table);
803
689
  let sql = `DELETE FROM ${escapedTable}`;
804
-
805
690
  if (this._where.length > 0) {
806
691
  sql += " WHERE " + this._where.join(" AND ");
807
- } else {
692
+ }
693
+ else {
808
694
  throw new Error(SqlBuilderError.DELETE_NEED_WHERE);
809
695
  }
810
-
811
696
  return { sql, params: [...this._params] };
812
697
  }
813
-
814
698
  /**
815
699
  * 构建 COUNT 查询
816
700
  */
817
- toCountSql(): SqlQuery {
701
+ toCountSql() {
818
702
  let sql = "SELECT COUNT(*) as total";
819
-
820
703
  if (!this._from) {
821
704
  throw new Error(SqlBuilderError.COUNT_NEED_FROM);
822
705
  }
823
706
  sql += ` FROM ${this._from}`;
824
-
825
707
  if (this._joins.length > 0) {
826
708
  sql += " " + this._joins.join(" ");
827
709
  }
828
-
829
710
  if (this._where.length > 0) {
830
711
  sql += " WHERE " + this._where.join(" AND ");
831
712
  }
832
-
833
713
  return { sql, params: [...this._params] };
834
714
  }
835
-
836
- static toDeleteInSql(options: { table: string; idField: string; ids: SqlValue[]; quoteIdent: (identifier: string) => string }): SqlQuery {
715
+ static toDeleteInSql(options) {
837
716
  if (typeof options.table !== "string" || !options.table.trim()) {
838
717
  throw new Error(SqlBuilderError.TO_DELETE_IN_NEED_TABLE(options.table));
839
718
  }
@@ -846,23 +725,11 @@ export class SqlBuilder {
846
725
  if (options.ids.length === 0) {
847
726
  return { sql: "", params: [] };
848
727
  }
849
-
850
728
  const placeholders = options.ids.map(() => "?").join(",");
851
729
  const sql = `DELETE FROM ${options.quoteIdent(options.table)} WHERE ${options.quoteIdent(options.idField)} IN (${placeholders})`;
852
730
  return { sql: sql, params: [...options.ids] };
853
731
  }
854
-
855
- static toUpdateCaseByIdSql(options: {
856
- table: string;
857
- idField: string;
858
- rows: Array<{ id: SqlValue; data: Record<string, SqlValue> }>;
859
- fields: string[];
860
- quoteIdent: (identifier: string) => string;
861
- updatedAtField: string;
862
- updatedAtValue: SqlValue;
863
- stateField?: string;
864
- stateGtZero?: boolean;
865
- }): SqlQuery {
732
+ static toUpdateCaseByIdSql(options) {
866
733
  if (typeof options.table !== "string" || !options.table.trim()) {
867
734
  throw new Error(SqlBuilderError.TO_UPDATE_CASE_NEED_TABLE(options.table));
868
735
  }
@@ -881,55 +748,42 @@ export class SqlBuilder {
881
748
  if (options.fields.length === 0) {
882
749
  return { sql: "", params: [] };
883
750
  }
884
-
885
- const ids: SqlValue[] = options.rows.map((r) => r.id);
751
+ const ids = options.rows.map((r) => r.id);
886
752
  const placeholders = ids.map(() => "?").join(",");
887
-
888
- const setSqlList: string[] = [];
889
- const args: SqlValue[] = [];
890
-
753
+ const setSqlList = [];
754
+ const args = [];
891
755
  const quotedId = options.quoteIdent(options.idField);
892
-
893
756
  for (const field of options.fields) {
894
- const whenList: string[] = [];
895
-
757
+ const whenList = [];
896
758
  for (const row of options.rows) {
897
759
  if (!(field in row.data)) {
898
760
  continue;
899
761
  }
900
-
901
762
  whenList.push("WHEN ? THEN ?");
902
763
  args.push(row.id);
903
764
  args.push(row.data[field]);
904
765
  }
905
-
906
766
  if (whenList.length === 0) {
907
767
  continue;
908
768
  }
909
-
910
769
  const quotedField = options.quoteIdent(field);
911
770
  setSqlList.push(`${quotedField} = CASE ${quotedId} ${whenList.join(" ")} ELSE ${quotedField} END`);
912
771
  }
913
-
914
772
  setSqlList.push(`${options.quoteIdent(options.updatedAtField)} = ?`);
915
773
  args.push(options.updatedAtValue);
916
-
917
774
  for (const id of ids) {
918
775
  args.push(id);
919
776
  }
920
-
921
777
  let sql = `UPDATE ${options.quoteIdent(options.table)} SET ${setSqlList.join(", ")} WHERE ${quotedId} IN (${placeholders})`;
922
778
  if (options.stateGtZero && options.stateField) {
923
779
  sql += ` AND ${options.quoteIdent(options.stateField)} > 0`;
924
780
  }
925
-
926
781
  return { sql: sql, params: args };
927
782
  }
928
783
  }
929
-
930
784
  /**
931
785
  * 创建新的 SQL 构建器实例
932
786
  */
933
- export function createQueryBuilder(): SqlBuilder {
787
+ export function createQueryBuilder() {
934
788
  return new SqlBuilder();
935
789
  }