befly 3.74.2 → 3.75.0

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 (65) hide show
  1. package/apis/dashboard/systemResources.js +3 -1
  2. package/apis/dict/_dict.js +1 -1
  3. package/apis/role/_role.js +2 -2
  4. package/apis/tongJi/dailyStats.js +2 -2
  5. package/apis/tongJi/dailyStatsDistribution.js +3 -3
  6. package/apis/tongJi/errorList.js +2 -2
  7. package/apis/tongJi/errorStats.js +6 -6
  8. package/checks/api.js +11 -6
  9. package/checks/items.js +56 -0
  10. package/checks/menu.js +1 -1
  11. package/checks/table.js +4 -5
  12. package/configs/beflyConfig.json +5 -10
  13. package/configs/constConfig.js +44 -3
  14. package/hooks/auth.js +3 -2
  15. package/hooks/parser.js +15 -4
  16. package/hooks/permission.js +3 -2
  17. package/hooks/rateLimit.js +16 -3
  18. package/hooks/validator.js +2 -1
  19. package/index.js +53 -24
  20. package/lib/cacheHelper.js +83 -229
  21. package/lib/connect.js +3 -13
  22. package/lib/dbHelper.js +57 -142
  23. package/lib/dbParse.js +65 -248
  24. package/lib/dbUtil.js +19 -63
  25. package/lib/logger.js +45 -22
  26. package/lib/redisHelper.js +186 -420
  27. package/lib/smtpText.js +7 -18
  28. package/lib/sqlBuilder.js +22 -51
  29. package/lib/xmlParse.js +15 -59
  30. package/package.json +1 -1
  31. package/plugins/cache.js +2 -3
  32. package/plugins/config.js +3 -1
  33. package/plugins/cron.js +2 -2
  34. package/plugins/email.js +3 -1
  35. package/plugins/logger.js +2 -2
  36. package/plugins/mysql.js +5 -10
  37. package/plugins/redis.js +4 -11
  38. package/plugins/tool.js +3 -3
  39. package/plugins/xmlParse.js +3 -1
  40. package/router/api.js +2 -6
  41. package/router/static.js +9 -10
  42. package/schemas/config.js +4 -4
  43. package/scripts/syncDb/context.js +4 -7
  44. package/scripts/syncDb/diff.js +2 -1
  45. package/scripts/syncDb/index.js +2 -6
  46. package/sync/api.js +7 -8
  47. package/sync/cache.js +3 -1
  48. package/sync/dev.js +28 -65
  49. package/sync/menu.js +5 -6
  50. package/sync/syncUtil.js +25 -0
  51. package/utils/calcPerfTime.js +2 -3
  52. package/utils/crypto.js +0 -40
  53. package/utils/deepMerge.js +2 -6
  54. package/utils/error.js +64 -0
  55. package/utils/fieldClear.js +13 -56
  56. package/utils/importDefault.js +11 -12
  57. package/utils/is.js +5 -182
  58. package/utils/loggerUtils.js +3 -1
  59. package/utils/response.js +12 -8
  60. package/utils/scanFiles.js +2 -53
  61. package/utils/scanSources.js +9 -0
  62. package/utils/util.js +25 -222
  63. package/checks/hook.js +0 -39
  64. package/checks/plugin.js +0 -39
  65. package/utils/processInfo.js +0 -39
package/lib/dbParse.js CHANGED
@@ -1,41 +1,30 @@
1
+ import { validationError } from "../utils/error.js";
1
2
  import { isNonEmptyString, isNullable, isPlainObject, isString } from "../utils/is.js";
2
3
  import { snakeCase } from "../utils/util.js";
3
4
  import { assertNoExprField, clearDeep, fieldsToSnake, normalizeQualifierField, orderByToSnake, parseFieldAliasParts, parseFlatWhereKey, parseTableRef, processJoinField, processJoinOn, processJoinOrderBy, processJoinWhere, whereKeysToSnake } from "./dbUtil.js";
4
5
 
5
6
  function assertNonEmptyString(value, label) {
6
7
  if (!isNonEmptyString(value)) {
7
- throw new Error(`${label} 必须是非空字符串`, {
8
- cause: null,
9
- code: "validation"
10
- });
8
+ throw validationError(`${label} 必须是非空字符串`);
11
9
  }
12
10
  }
13
11
 
14
12
  function assertPlainObjectValue(value, label) {
15
13
  if (!isPlainObject(value)) {
16
- throw new Error(`${label} 必须是对象`, {
17
- cause: null,
18
- code: "validation"
19
- });
14
+ throw validationError(`${label} 必须是对象`);
20
15
  }
21
16
  }
22
17
 
23
18
  function validateWhereObject(where, label, required = false) {
24
19
  if (isNullable(where)) {
25
20
  if (required) {
26
- throw new Error(`${label} 必须是对象`, {
27
- cause: null,
28
- code: "validation"
29
- });
21
+ throw validationError(`${label} 必须是对象`);
30
22
  }
31
23
  return;
32
24
  }
33
25
 
34
26
  if (!isPlainObject(where)) {
35
- throw new Error(`${label} 必须是对象`, {
36
- cause: null,
37
- code: "validation"
38
- });
27
+ throw validationError(`${label} 必须是对象`);
39
28
  }
40
29
 
41
30
  for (const [key, value] of Object.entries(where)) {
@@ -45,97 +34,64 @@ function validateWhereObject(where, label, required = false) {
45
34
  }
46
35
 
47
36
  if (!isPlainObject(value)) {
48
- throw new Error(`${label}.$exclude 必须是对象`, {
49
- cause: null,
50
- code: "validation"
51
- });
37
+ throw validationError(`${label}.$exclude 必须是对象`);
52
38
  }
53
39
 
54
40
  for (const [excludeKey, excludeValue] of Object.entries(value)) {
55
41
  assertNonEmptyString(excludeKey, `${label}.$exclude 的字段名`);
56
42
  if (!Array.isArray(excludeValue)) {
57
- throw new Error(`${label}.$exclude.${excludeKey} 必须是数组`, {
58
- cause: null,
59
- code: "validation"
60
- });
43
+ throw validationError(`${label}.$exclude.${excludeKey} 必须是数组`);
61
44
  }
62
45
  }
63
46
  continue;
64
47
  }
65
48
 
66
49
  if (key === "$or" || key === "$and") {
67
- throw new Error(`${label} 不再支持 ${key} 嵌套,请改用 field$op$or 扁平写法`, {
68
- cause: null,
69
- code: "validation"
70
- });
50
+ throw validationError(`${label} 不再支持 ${key} 嵌套,请改用 field$op$or 扁平写法`);
71
51
  }
72
52
 
73
53
  parseFlatWhereKey(key);
74
54
 
75
55
  if (isPlainObject(value)) {
76
- throw new Error(`${label}.${key} 不再支持嵌套对象操作符,请改用 field$op 扁平写法`, {
77
- cause: null,
78
- code: "validation"
79
- });
56
+ throw validationError(`${label}.${key} 不再支持嵌套对象操作符,请改用 field$op 扁平写法`);
80
57
  }
81
58
  }
82
59
  }
83
60
 
84
61
  function validateOrderByItems(orderBy, label) {
85
62
  if (!Array.isArray(orderBy)) {
86
- throw new Error(`${label} 必须是数组`, {
87
- cause: null,
88
- code: "validation"
89
- });
63
+ throw validationError(`${label} 必须是数组`);
90
64
  }
91
65
 
92
66
  for (const item of orderBy) {
93
67
  if (!isString(item) || !item.includes("#")) {
94
- throw new Error(`${label} 字段必须是 "字段#方向" 格式的字符串`, {
95
- cause: null,
96
- code: "validation"
97
- });
68
+ throw validationError(`${label} 字段必须是 "字段#方向" 格式的字符串`);
98
69
  }
99
70
 
100
71
  const parts = item.split("#");
101
72
  if (parts.length !== 2) {
102
- throw new Error(`${label} 字段必须是 "字段#方向" 格式的字符串`, {
103
- cause: null,
104
- code: "validation"
105
- });
73
+ throw validationError(`${label} 字段必须是 "字段#方向" 格式的字符串`);
106
74
  }
107
75
 
108
76
  const field = isString(parts[0]) ? parts[0].trim() : "";
109
77
  const direction = isString(parts[1]) ? parts[1].trim().toUpperCase() : "";
110
78
  if (!field) {
111
- throw new Error(`${label} 中字段名不能为空`, {
112
- cause: null,
113
- code: "validation"
114
- });
79
+ throw validationError(`${label} 中字段名不能为空`);
115
80
  }
116
81
  if (direction !== "ASC" && direction !== "DESC") {
117
- throw new Error(`${label} 方向必须是 ASC 或 DESC`, {
118
- cause: null,
119
- code: "validation"
120
- });
82
+ throw validationError(`${label} 方向必须是 ASC 或 DESC`);
121
83
  }
122
84
  }
123
85
  }
124
86
 
125
87
  function assertJoinFieldRef(value, label) {
126
88
  if (!isNonEmptyString(value)) {
127
- throw new Error(`${label} 不能为空`, {
128
- cause: null,
129
- code: "validation"
130
- });
89
+ throw validationError(`${label} 不能为空`);
131
90
  }
132
91
 
133
92
  const parts = value.split(".").map((item) => item.trim());
134
93
  if (parts.length !== 2 || !isNonEmptyString(parts[0]) || !isNonEmptyString(parts[1])) {
135
- throw new Error(`${label} 必须是 alias.field 格式`, {
136
- cause: null,
137
- code: "validation"
138
- });
94
+ throw validationError(`${label} 必须是 alias.field 格式`);
139
95
  }
140
96
  }
141
97
 
@@ -157,27 +113,18 @@ function parseLeftJoinEquality(joinItem) {
157
113
 
158
114
  function validateLeftJoinItems(leftJoin, label) {
159
115
  if (!Array.isArray(leftJoin)) {
160
- throw new Error(`${label} 必须是数组`, {
161
- cause: null,
162
- code: "validation"
163
- });
116
+ throw validationError(`${label} 必须是数组`);
164
117
  }
165
118
 
166
119
  for (let i = 0; i < leftJoin.length; i++) {
167
120
  const joinItem = leftJoin[i];
168
121
  if (!isNonEmptyString(joinItem)) {
169
- throw new Error(`${label}[${i}] 必须是非空字符串`, {
170
- cause: null,
171
- code: "validation"
172
- });
122
+ throw validationError(`${label}[${i}] 必须是非空字符串`);
173
123
  }
174
124
 
175
125
  const parsed = parseLeftJoinEquality(joinItem);
176
126
  if (!parsed) {
177
- throw new Error(`${label}[${i}] 必须是 "left.field right.field" 格式(空格表示等于)`, {
178
- cause: null,
179
- code: "validation"
180
- });
127
+ throw validationError(`${label}[${i}] 必须是 "left.field right.field" 格式(空格表示等于)`);
181
128
  }
182
129
 
183
130
  assertJoinFieldRef(parsed.left, `${label}[${i}] 左侧字段`);
@@ -187,19 +134,13 @@ function validateLeftJoinItems(leftJoin, label) {
187
134
 
188
135
  function validateQueryOptions(options, label) {
189
136
  if (!isPlainObject(options)) {
190
- throw new Error(`${label} 必须是对象`, {
191
- cause: null,
192
- code: "validation"
193
- });
137
+ throw validationError(`${label} 必须是对象`);
194
138
  }
195
139
 
196
140
  const tableList = Array.isArray(options.table) ? options.table : [options.table];
197
141
 
198
142
  if (Array.isArray(options.table) && tableList.length === 0) {
199
- throw new Error(`${label}.table 不能为空数组`, {
200
- cause: null,
201
- code: "validation"
202
- });
143
+ throw validationError(`${label}.table 不能为空数组`);
203
144
  }
204
145
 
205
146
  for (let i = 0; i < tableList.length; i++) {
@@ -215,22 +156,13 @@ function validateQueryOptions(options, label) {
215
156
  if (Array.isArray(options.table)) {
216
157
  if (Array.isArray(options.leftJoin) && options.leftJoin.length > 0) {
217
158
  if (tableList.length !== options.leftJoin.length + 1) {
218
- throw new Error(`${label}.table 与 ${label}.leftJoin 数量不匹配,要求 table.length = leftJoin.length + 1`, {
219
- cause: null,
220
- code: "validation"
221
- });
159
+ throw validationError(`${label}.table 与 ${label}.leftJoin 数量不匹配,要求 table.length = leftJoin.length + 1`);
222
160
  }
223
161
  } else if (tableList.length > 1) {
224
- throw new Error(`${label}.table 为数组时必须配合 leftJoin 使用`, {
225
- cause: null,
226
- code: "validation"
227
- });
162
+ throw validationError(`${label}.table 为数组时必须配合 leftJoin 使用`);
228
163
  }
229
164
  } else if (Array.isArray(options.leftJoin) && options.leftJoin.length > 0) {
230
- throw new Error(`${label}.leftJoin 启用时,${label}.table 必须是数组`, {
231
- cause: null,
232
- code: "validation"
233
- });
165
+ throw validationError(`${label}.leftJoin 启用时,${label}.table 必须是数组`);
234
166
  }
235
167
 
236
168
  if (!isNullable(options.where)) {
@@ -238,10 +170,7 @@ function validateQueryOptions(options, label) {
238
170
  }
239
171
 
240
172
  if (!isNullable(options.fields) && !Array.isArray(options.fields)) {
241
- throw new Error(`${label}.fields 必须是数组`, {
242
- cause: null,
243
- code: "validation"
244
- });
173
+ throw validationError(`${label}.fields 必须是数组`);
245
174
  }
246
175
 
247
176
  if (!isNullable(options.orderBy)) {
@@ -253,34 +182,22 @@ function validateQueryOptions(options, label) {
253
182
  }
254
183
 
255
184
  if (!isNullable(options.page) && (!Number.isFinite(options.page) || Math.floor(options.page) !== options.page)) {
256
- throw new Error(`${label}.page 必须是整数`, {
257
- cause: null,
258
- code: "validation"
259
- });
185
+ throw validationError(`${label}.page 必须是整数`);
260
186
  }
261
187
 
262
188
  if (!isNullable(options.limit) && (!Number.isFinite(options.limit) || Math.floor(options.limit) !== options.limit)) {
263
- throw new Error(`${label}.limit 必须是整数`, {
264
- cause: null,
265
- code: "validation"
266
- });
189
+ throw validationError(`${label}.limit 必须是整数`);
267
190
  }
268
191
  }
269
192
 
270
193
  function assertBatchDataList(dataList, label) {
271
194
  if (!Array.isArray(dataList)) {
272
- throw new Error(`${label} 必须是数组`, {
273
- cause: null,
274
- code: "validation"
275
- });
195
+ throw validationError(`${label} 必须是数组`);
276
196
  }
277
197
 
278
198
  for (let i = 0; i < dataList.length; i++) {
279
199
  if (!isPlainObject(dataList[i])) {
280
- throw new Error(`${label}[${i}] 必须是对象`, {
281
- cause: null,
282
- code: "validation"
283
- });
200
+ throw validationError(`${label}[${i}] 必须是对象`);
284
201
  }
285
202
  }
286
203
  }
@@ -289,77 +206,47 @@ function validateNoLeftJoinReadOptions(options, label, joinErrorMessage) {
289
206
  assertNonEmptyString(options.table, `${label}.table`);
290
207
  validateWhereObject(options.where, `${label}.where`, false);
291
208
  if (Array.isArray(options.leftJoin) && options.leftJoin.length > 0) {
292
- throw new Error(joinErrorMessage, {
293
- cause: null,
294
- code: "validation"
295
- });
209
+ throw validationError(joinErrorMessage);
296
210
  }
297
211
 
298
212
  if (Array.isArray(options.table)) {
299
- throw new Error(`${label}.table 不支持数组`, {
300
- cause: null,
301
- code: "validation"
302
- });
213
+ throw validationError(`${label}.table 不支持数组`);
303
214
  }
304
215
 
305
216
  const table = isString(options.table) ? options.table.trim() : "";
306
217
  if (!table) {
307
- throw new Error(`${label}.table 不能为空`, {
308
- cause: null,
309
- code: "validation"
310
- });
218
+ throw validationError(`${label}.table 不能为空`);
311
219
  }
312
220
  if (table.includes(" ")) {
313
- throw new Error(`${label} 不支持别名表写法(table: ${table})`, {
314
- cause: null,
315
- code: "validation"
316
- });
221
+ throw validationError(`${label} 不支持别名表写法(table: ${table})`);
317
222
  }
318
223
  if (table.includes(".")) {
319
- throw new Error(`${label} 不支持 schema.table 写法(table: ${table})`, {
320
- cause: null,
321
- code: "validation"
322
- });
224
+ throw validationError(`${label} 不支持 schema.table 写法(table: ${table})`);
323
225
  }
324
226
 
325
227
  if (!isNullable(options.orderBy)) {
326
228
  validateOrderByItems(options.orderBy, `${label}.orderBy`);
327
229
  }
328
230
  if (!isNullable(options.page) && (!Number.isFinite(options.page) || Math.floor(options.page) !== options.page)) {
329
- throw new Error(`${label}.page 必须是整数`, {
330
- cause: null,
331
- code: "validation"
332
- });
231
+ throw validationError(`${label}.page 必须是整数`);
333
232
  }
334
233
  if (!isNullable(options.limit) && (!Number.isFinite(options.limit) || Math.floor(options.limit) !== options.limit)) {
335
- throw new Error(`${label}.limit 必须是整数`, {
336
- cause: null,
337
- code: "validation"
338
- });
234
+ throw validationError(`${label}.limit 必须是整数`);
339
235
  }
340
236
  }
341
237
 
342
238
  function assertSafeFieldName(field) {
343
239
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(field)) {
344
- throw new Error(`无效的字段名: ${field},只允许字母、数字和下划线`, {
345
- cause: null,
346
- code: "validation"
347
- });
240
+ throw validationError(`无效的字段名: ${field},只允许字母、数字和下划线`);
348
241
  }
349
242
  }
350
243
 
351
244
  function assertPageLimitRange(page, limit, rawTable) {
352
245
  if (page < 1 || page > 10000) {
353
- throw new Error(`页码必须在 1 到 10000 之间 (table: ${rawTable}, page: ${page}, limit: ${limit})`, {
354
- cause: null,
355
- code: "validation"
356
- });
246
+ throw validationError(`页码必须在 1 到 10000 之间 (table: ${rawTable}, page: ${page}, limit: ${limit})`);
357
247
  }
358
248
  if (limit < 1 || limit > 100000) {
359
- throw new Error(`每页数量必须在 1 到 100000 之间 (table: ${rawTable}, page: ${page}, limit: ${limit})`, {
360
- cause: null,
361
- code: "validation"
362
- });
249
+ throw validationError(`每页数量必须在 1 到 100000 之间 (table: ${rawTable}, page: ${page}, limit: ${limit})`);
363
250
  }
364
251
  }
365
252
 
@@ -369,28 +256,19 @@ function validateIncrementOptions(table, field, where, value, label) {
369
256
  assertSafeFieldName(snakeCase(table));
370
257
  assertSafeFieldName(snakeCase(field));
371
258
  if (typeof value !== "number" || Number.isNaN(value)) {
372
- throw new Error(`自增值必须是有效的数字 (table: ${table}, field: ${field}, value: ${value})`, {
373
- cause: null,
374
- code: "validation"
375
- });
259
+ throw validationError(`自增值必须是有效的数字 (table: ${table}, field: ${field}, value: ${value})`);
376
260
  }
377
261
  }
378
262
 
379
263
  function assertEffectiveWhere(where, label) {
380
264
  if (!where || Object.keys(where).length === 0) {
381
- throw new Error(`${label} 需要有效的 WHERE 条件`, {
382
- cause: null,
383
- code: "validation"
384
- });
265
+ throw validationError(`${label} 需要有效的 WHERE 条件`);
385
266
  }
386
267
  }
387
268
 
388
269
  function assertDefinedParamValue(value) {
389
270
  if (value === undefined) {
390
- throw new Error("SQL 参数值 不能为 undefined", {
391
- cause: null,
392
- code: "validation"
393
- });
271
+ throw validationError("SQL 参数值 不能为 undefined");
394
272
  }
395
273
  }
396
274
 
@@ -412,21 +290,15 @@ function parseFieldSelection(fields) {
412
290
  }
413
291
 
414
292
  if (fields.some((field) => field === "*")) {
415
- throw new Error("fields 不支持 * 星号,请使用空数组 [] 或不传参数表示查询所有字段", {
416
- cause: null,
417
- code: "validation"
418
- });
293
+ throw validationError("fields 不支持 * 星号,请使用空数组 [] 或不传参数表示查询所有字段");
419
294
  }
420
295
 
421
296
  if (fields.some((field) => !isNonEmptyString(field))) {
422
- throw new Error("fields 不能包含空字符串或无效值", {
423
- cause: null,
424
- code: "validation"
425
- });
297
+ throw validationError("fields 不能包含空字符串或无效值");
426
298
  }
427
299
 
428
300
  for (const rawField of fields) {
429
- const checkField = rawField.startsWith("!") ? rawField.substring(1) : rawField;
301
+ const checkField = rawField.startsWith("!") ? rawField.slice(1) : rawField;
430
302
  assertNoExprField(checkField);
431
303
  }
432
304
 
@@ -437,13 +309,10 @@ function parseFieldSelection(fields) {
437
309
  return { type: "include", fields: includeFields };
438
310
  }
439
311
  if (excludeFields.length > 0 && includeFields.length === 0) {
440
- return { type: "exclude", fields: excludeFields.map((field) => field.substring(1)) };
312
+ return { type: "exclude", fields: excludeFields.map((field) => field.slice(1)) };
441
313
  }
442
314
 
443
- throw new Error('fields 不能同时包含普通字段和排除字段(! 开头)。只能使用以下3种方式之一:\n1. 空数组 [] 或不传(查询所有)\n2. 全部指定字段 ["id", "name"]\n3. 全部排除字段 ["!password", "!token"]', {
444
- cause: null,
445
- code: "validation"
446
- });
315
+ throw validationError('fields 不能同时包含普通字段和排除字段(! 开头)。只能使用以下3种方式之一:\n1. 空数组 [] 或不传(查询所有)\n2. 全部指定字段 ["id", "name"]\n3. 全部排除字段 ["!password", "!token"]');
447
316
  }
448
317
 
449
318
  function getTableQualifier(tableRef) {
@@ -457,10 +326,7 @@ function getTableQualifier(tableRef) {
457
326
 
458
327
  function parseJoinFieldSelection(fields, mainTableRef) {
459
328
  if (!fields || fields.length === 0) {
460
- throw new Error("leftJoin 查询必须显式传 fields,不支持空 fields 或查询全部字段", {
461
- cause: null,
462
- code: "validation"
463
- });
329
+ throw validationError("leftJoin 查询必须显式传 fields,不支持空 fields 或查询全部字段");
464
330
  }
465
331
 
466
332
  const mainQualifier = getTableQualifier(mainTableRef);
@@ -471,51 +337,33 @@ function parseJoinFieldSelection(fields, mainTableRef) {
471
337
 
472
338
  for (const rawField of fields) {
473
339
  if (!isNonEmptyString(rawField)) {
474
- throw new Error("fields 不能包含空字符串或无效值", {
475
- cause: null,
476
- code: "validation"
477
- });
340
+ throw validationError("fields 不能包含空字符串或无效值");
478
341
  }
479
342
 
480
343
  const trimmed = rawField.trim();
481
344
  if (trimmed === "*") {
482
- throw new Error("fields 不支持 * 星号,请使用空数组 [] 或不传参数表示查询所有字段", {
483
- cause: null,
484
- code: "validation"
485
- });
345
+ throw validationError("fields 不支持 * 星号,请使用空数组 [] 或不传参数表示查询所有字段");
486
346
  }
487
347
 
488
348
  if (trimmed.startsWith("!")) {
489
- const fieldPart = trimmed.substring(1).trim();
349
+ const fieldPart = trimmed.slice(1).trim();
490
350
  if (!isNonEmptyString(fieldPart)) {
491
- throw new Error("fields 不能包含空字符串或无效值", {
492
- cause: null,
493
- code: "validation"
494
- });
351
+ throw validationError("fields 不能包含空字符串或无效值");
495
352
  }
496
353
  if (/\s/.test(fieldPart)) {
497
- throw new Error("leftJoin 排除字段不支持别名", {
498
- cause: null,
499
- code: "validation"
500
- });
354
+ throw validationError("leftJoin 排除字段不支持别名");
501
355
  }
502
356
 
503
357
  assertNoExprField(fieldPart);
504
358
  const normalizedField = normalizeQualifierField(fieldPart);
505
359
  if (normalizedField === mainWildcard) {
506
- throw new Error("leftJoin 主表排除字段不能使用 *", {
507
- cause: null,
508
- code: "validation"
509
- });
360
+ throw validationError("leftJoin 主表排除字段不能使用 *");
510
361
  }
511
362
  if (!normalizedField.startsWith(`${mainQualifier}.`)) {
512
- throw new Error("leftJoin 排除字段仅支持主表字段,请使用主表限定符", {
513
- cause: null,
514
- code: "validation"
515
- });
363
+ throw validationError("leftJoin 排除字段仅支持主表字段,请使用主表限定符");
516
364
  }
517
365
 
518
- mainExcludeFields.push(normalizedField.substring(mainQualifier.length + 1));
366
+ mainExcludeFields.push(normalizedField.slice(mainQualifier.length + 1));
519
367
  continue;
520
368
  }
521
369
 
@@ -524,10 +372,7 @@ function parseJoinFieldSelection(fields, mainTableRef) {
524
372
  const normalizedField = normalizeQualifierField(aliasParts ? aliasParts.fieldPart.trim() : trimmed);
525
373
  if (normalizedField === mainWildcard) {
526
374
  if (aliasParts) {
527
- throw new Error("leftJoin 主表全字段不支持别名", {
528
- cause: null,
529
- code: "validation"
530
- });
375
+ throw validationError("leftJoin 主表全字段不支持别名");
531
376
  }
532
377
  mainAllIncluded = true;
533
378
  continue;
@@ -537,10 +382,7 @@ function parseJoinFieldSelection(fields, mainTableRef) {
537
382
  }
538
383
 
539
384
  if (mainExcludeFields.length > 0 && !mainAllIncluded) {
540
- throw new Error("leftJoin 使用主表排除字段时,必须同时传主表限定符.*", {
541
- cause: null,
542
- code: "validation"
543
- });
385
+ throw validationError("leftJoin 使用主表排除字段时,必须同时传主表限定符.*");
544
386
  }
545
387
 
546
388
  return {
@@ -572,16 +414,10 @@ function normalizeOrGroupKey(orGroup) {
572
414
 
573
415
  function buildArrayOperatorNode(fieldName, operator, value, errorFactory, emptyMessage) {
574
416
  if (!Array.isArray(value)) {
575
- throw new Error(errorFactory(operator), {
576
- cause: null,
577
- code: "validation"
578
- });
417
+ throw validationError(errorFactory(operator));
579
418
  }
580
419
  if (value.length === 0) {
581
- throw new Error(emptyMessage, {
582
- cause: null,
583
- code: "validation"
584
- });
420
+ throw validationError(emptyMessage);
585
421
  }
586
422
 
587
423
  return { type: "op", field: fieldName, operator: operator, value: value };
@@ -741,22 +577,6 @@ export class DbParse {
741
577
  this.getTableColumns = options.getTableColumns;
742
578
  }
743
579
 
744
- classifyFields(fields) {
745
- return parseFieldSelection(fields);
746
- }
747
-
748
- classifyJoinFields(fields, mainTableRef) {
749
- return parseJoinFieldSelection(fields, mainTableRef);
750
- }
751
-
752
- parseLeftJoinItem(joinTable, joinItem) {
753
- return parseLeftJoinItem(joinTable, joinItem);
754
- }
755
-
756
- applyDefaultStateFilter(where, table, hasLeftJoin = false) {
757
- return applyDefaultStateFilter(where, table, hasLeftJoin, this.beflyMode);
758
- }
759
-
760
580
  async parseRead(options, mode) {
761
581
  const label = mode === "one" ? "getOne.options" : mode === "list" ? "getList.options" : "getAll.options";
762
582
  validateQueryOptions(options, label);
@@ -861,10 +681,7 @@ export class DbParse {
861
681
  parseDelForceBatch(table, ids) {
862
682
  assertNonEmptyString(table, "delForceBatch.table");
863
683
  if (!Array.isArray(ids)) {
864
- throw new Error("delForceBatch.ids 必须是数组", {
865
- cause: null,
866
- code: "validation"
867
- });
684
+ throw validationError("delForceBatch.ids 必须是数组");
868
685
  }
869
686
  return {
870
687
  table: table,
@@ -996,12 +813,12 @@ export class DbParse {
996
813
  }
997
814
 
998
815
  if (processedFields.length === 0) {
999
- throw new Error(`排除字段后没有剩余字段可查询。表: ${mainTableRef}, 排除: ${classifiedFields.mainExcludeFields.join(", ")}`, {
1000
- cause: null,
1001
- code: "validation"
1002
- });
816
+ throw validationError(`排除字段后没有剩余字段可查询。表: ${mainTableRef}, 排除: ${classifiedFields.mainExcludeFields.join(", ")}`);
1003
817
  }
1004
818
 
1005
819
  return processedFields;
1006
820
  }
1007
821
  }
822
+
823
+ // 供内部/测试直接使用的字段分类工具(类方法套壳已移除)
824
+ export { applyDefaultStateFilter, parseFieldSelection, parseJoinFieldSelection, parseLeftJoinItem };