befly 3.21.1 → 3.22.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.
package/lib/dbHelper.js CHANGED
@@ -2,8 +2,9 @@ import { fieldClear } from "../utils/fieldClear.js";
2
2
  import { isNonEmptyString, isNullable, isNumber, isPlainObject, isString } from "../utils/is.js";
3
3
  import { arrayKeysToCamel, canConvertToNumber, keysToCamel, keysToSnake, snakeCase } from "../utils/util.js";
4
4
  import { Logger } from "./logger.js";
5
+ import { DbParse } from "./dbParse.js";
5
6
  import { SqlBuilder } from "./sqlBuilder.js";
6
- import { addDefaultStateFilter, assertBatchInsertRowsConsistent, assertNoUndefinedInRecord, clearDeep, deserializeArrayFields, fieldsToSnake, orderByToSnake, parseLeftJoinItem, parseTableRef, processJoinField, processJoinOrderBy, processJoinWhere, serializeArrayFields, validateExplicitLeftJoinFields, validateExplicitReadFields, whereKeysToSnake } from "./dbUtil.js";
7
+ import { deserializeArrayFields, parseTableRef, serializeArrayFields } from "./dbUtil.js";
7
8
 
8
9
  function quoteIdentMySql(identifier) {
9
10
  if (!isString(identifier)) {
@@ -24,26 +25,6 @@ function quoteIdentMySql(identifier) {
24
25
  return `\`${trimmed}\``;
25
26
  }
26
27
 
27
- function normalizeTableRef(tableRef) {
28
- const parsed = parseTableRef(tableRef);
29
- const schemaPart = parsed.schema ? snakeCase(parsed.schema) : null;
30
- const tablePart = snakeCase(parsed.table);
31
- const aliasPart = parsed.alias ? snakeCase(parsed.alias) : null;
32
- let result = schemaPart ? `${schemaPart}.${tablePart}` : tablePart;
33
- if (aliasPart) {
34
- result = `${result} ${aliasPart}`;
35
- }
36
- return result;
37
- }
38
-
39
- function getJoinMainQualifier(tableRef) {
40
- const parsed = parseTableRef(tableRef);
41
- if (parsed.alias) {
42
- return snakeCase(parsed.alias);
43
- }
44
- return snakeCase(parsed.table);
45
- }
46
-
47
28
  function normalizeSqlMetaNumber(value) {
48
29
  if (isNullable(value)) {
49
30
  return 0;
@@ -147,503 +128,129 @@ function getExecuteErrorMessage(error) {
147
128
  return detailText;
148
129
  }
149
130
 
150
- function assertNonEmptyString(value, label) {
151
- if (!isNonEmptyString(value)) {
152
- throw new Error(`${label} 必须是非空字符串`, {
131
+ function assertGeneratedBatchId(id, table, index) {
132
+ if (typeof id !== "number") {
133
+ throw new Error(`批量插入生成 ID 失败:ids[${index}] 不是 number (table: ${table})`, {
153
134
  cause: null,
154
- code: "validation"
135
+ code: "runtime"
155
136
  });
156
137
  }
157
138
  }
158
139
 
159
- function assertPlainObjectValue(value, label) {
160
- if (!isPlainObject(value)) {
161
- throw new Error(`${label} 必须是对象`, {
140
+ function assertTimeIdValue(id) {
141
+ if (!Number.isFinite(id) || id <= 0) {
142
+ throw new Error(`buildInsertRow(timeId) 失败:id 必须为 > 0 的有限 number (id: ${String(id)})`, {
162
143
  cause: null,
163
144
  code: "validation"
164
145
  });
165
146
  }
166
147
  }
167
148
 
168
- function validateExcludeRules(excludeRules, label) {
169
- if (isNullable(excludeRules)) {
170
- return;
171
- }
172
-
173
- if (!isPlainObject(excludeRules)) {
174
- throw new Error(`${label} 必须是对象`, {
149
+ function assertInsertBatchSize(count, maxBatchSize) {
150
+ if (count > maxBatchSize) {
151
+ throw new Error(`批量插入数量 ${count} 超过最大限制 ${maxBatchSize}`, {
175
152
  cause: null,
176
153
  code: "validation"
177
154
  });
178
155
  }
179
-
180
- for (const [key, value] of Object.entries(excludeRules)) {
181
- assertNonEmptyString(key, `${label} 的字段名`);
182
- if (!Array.isArray(value)) {
183
- throw new Error(`${label}.${key} 必须是数组`, {
184
- cause: null,
185
- code: "validation"
186
- });
187
- }
188
- }
189
156
  }
190
157
 
191
- function validateWhereMeta(where, label) {
192
- if (!isPlainObject(where)) {
158
+ function assertWriteDataHasFields(data, message, table) {
159
+ if (Object.keys(data).length > 0) {
193
160
  return;
194
161
  }
195
162
 
196
- for (const [key, value] of Object.entries(where)) {
197
- if (key === "$exclude") {
198
- validateExcludeRules(value, `${label}.$exclude`);
199
- continue;
200
- }
201
-
202
- if ((key === "$or" || key === "$and") && Array.isArray(value)) {
203
- for (let i = 0; i < value.length; i++) {
204
- if (isPlainObject(value[i])) {
205
- validateWhereMeta(value[i], `${label}.${key}[${i}]`);
206
- }
207
- }
208
- continue;
209
- }
210
-
211
- if (isPlainObject(value)) {
212
- validateWhereMeta(value, `${label}.${key}`);
213
- }
214
- }
163
+ throw new Error(`${message} (table: ${table})`, {
164
+ cause: null,
165
+ code: "validation"
166
+ });
215
167
  }
216
168
 
217
- function validateWhereObject(where, label, required = false) {
218
- if (isNullable(where)) {
219
- if (required) {
220
- throw new Error(`${label} 必须是对象`, {
169
+ function assertNoUndefinedInRecord(row, label) {
170
+ for (const [key, value] of Object.entries(row)) {
171
+ if (value === undefined) {
172
+ throw new Error(`${label} 存在 undefined 字段值 (field: ${key})`, {
221
173
  cause: null,
222
174
  code: "validation"
223
175
  });
224
176
  }
225
- return;
226
177
  }
227
-
228
- if (!isPlainObject(where)) {
229
- throw new Error(`${label} 必须是对象`, {
230
- cause: null,
231
- code: "validation"
232
- });
233
- }
234
-
235
- validateWhereMeta(where, label);
236
178
  }
237
179
 
238
- function validateOrderByItems(orderBy, label) {
239
- if (!Array.isArray(orderBy)) {
240
- throw new Error(`${label} 必须是数组`, {
180
+ function assertBatchInsertRowsConsistent(rows, options) {
181
+ if (!Array.isArray(rows)) {
182
+ throw new Error("批量插入 rows 必须是数组", {
241
183
  cause: null,
242
184
  code: "validation"
243
185
  });
244
186
  }
245
-
246
- for (const item of orderBy) {
247
- if (!isString(item) || !item.includes("#")) {
248
- throw new Error(`${label} 字段必须是 "字段#方向" 格式的字符串`, {
249
- cause: null,
250
- code: "validation"
251
- });
252
- }
253
-
254
- const parts = item.split("#");
255
- if (parts.length !== 2) {
256
- throw new Error(`${label} 字段必须是 "字段#方向" 格式的字符串`, {
257
- cause: null,
258
- code: "validation"
259
- });
260
- }
261
-
262
- const field = isString(parts[0]) ? parts[0].trim() : "";
263
- const direction = isString(parts[1]) ? parts[1].trim().toUpperCase() : "";
264
- if (!field) {
265
- throw new Error(`${label} 中字段名不能为空`, {
266
- cause: null,
267
- code: "validation"
268
- });
269
- }
270
- if (direction !== "ASC" && direction !== "DESC") {
271
- throw new Error(`${label} 方向必须是 ASC 或 DESC`, {
272
- cause: null,
273
- code: "validation"
274
- });
275
- }
276
- }
277
- }
278
-
279
- function validateJoinFieldRef(value, label) {
280
- if (!isNonEmptyString(value)) {
281
- throw new Error(`${label} 不能为空`, {
187
+ if (rows.length === 0) {
188
+ throw new Error(`插入数据不能为空 (table: ${options.table})`, {
282
189
  cause: null,
283
190
  code: "validation"
284
191
  });
285
192
  }
286
193
 
287
- const parts = value.split(".").map((item) => item.trim());
288
- if (parts.length !== 2 || !isNonEmptyString(parts[0]) || !isNonEmptyString(parts[1])) {
289
- throw new Error(`${label} 必须是 alias.field 格式`, {
194
+ const first = rows[0];
195
+ if (!first || typeof first !== "object" || Array.isArray(first)) {
196
+ throw new Error(`批量插入的每一行必须是对象 (table: ${options.table}, rowIndex: 0)`, {
290
197
  cause: null,
291
198
  code: "validation"
292
199
  });
293
200
  }
294
- }
295
-
296
- function parseLeftJoinEquality(joinItem) {
297
- if (!isNonEmptyString(joinItem)) {
298
- return null;
299
- }
300
-
301
- const match = joinItem.match(/^([a-zA-Z_][a-zA-Z0-9_]*\.[a-zA-Z_][a-zA-Z0-9_]*) ([a-zA-Z_][a-zA-Z0-9_]*\.[a-zA-Z_][a-zA-Z0-9_]*)$/);
302
- if (!match) {
303
- return null;
304
- }
305
201
 
306
- return {
307
- left: match[1],
308
- right: match[2]
309
- };
310
- }
311
-
312
- function validateLeftJoinItems(leftJoin, label) {
313
- if (!Array.isArray(leftJoin)) {
314
- throw new Error(`${label} 必须是数组`, {
202
+ const fields = Object.keys(first);
203
+ if (fields.length === 0) {
204
+ throw new Error(`插入数据必须至少有一个字段 (table: ${options.table})`, {
315
205
  cause: null,
316
206
  code: "validation"
317
207
  });
318
208
  }
319
209
 
320
- for (let i = 0; i < leftJoin.length; i++) {
321
- const joinItem = leftJoin[i];
322
- if (!isNonEmptyString(joinItem)) {
323
- throw new Error(`${label}[${i}] 必须是非空字符串`, {
210
+ const fieldSet = new Set(fields);
211
+ for (let i = 0; i < rows.length; i++) {
212
+ const row = rows[i];
213
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
214
+ throw new Error(`批量插入的每一行必须是对象 (table: ${options.table}, rowIndex: ${i})`, {
324
215
  cause: null,
325
216
  code: "validation"
326
217
  });
327
218
  }
328
219
 
329
- const parsed = parseLeftJoinEquality(joinItem);
330
- if (!parsed) {
331
- throw new Error(`${label}[${i}] 必须是 "left.field right.field" 格式(空格表示等于)`, {
220
+ const rowKeys = Object.keys(row);
221
+ if (rowKeys.length !== fields.length) {
222
+ throw new Error(`批量插入每行字段必须一致 (table: ${options.table}, rowIndex: ${i})`, {
332
223
  cause: null,
333
224
  code: "validation"
334
225
  });
335
226
  }
336
227
 
337
- validateJoinFieldRef(parsed.left, `${label}[${i}] 左侧字段`);
338
- validateJoinFieldRef(parsed.right, `${label}[${i}] 右侧字段`);
339
- }
340
- }
341
-
342
- function validateLegacyJoinOptions(options, label) {
343
- const legacyJoinKeys = ["joins", "innerJoin", "rightJoin"];
344
- for (const key of legacyJoinKeys) {
345
- if (!isNullable(options[key])) {
346
- throw new Error(`${label} 仅支持 leftJoin,${label}.${key} 已废弃`, {
347
- cause: null,
348
- code: "validation"
349
- });
350
- }
351
- }
352
- }
353
-
354
- function validateQueryTableOption(table, leftJoin, label) {
355
- if (Array.isArray(table)) {
356
- if (table.length === 0) {
357
- throw new Error(`${label}.table 不能为空数组`, {
358
- cause: null,
359
- code: "validation"
360
- });
361
- }
362
-
363
- for (let i = 0; i < table.length; i++) {
364
- assertNonEmptyString(table[i], `${label}.table[${i}]`);
365
- parseTableRef(table[i]);
366
- }
367
-
368
- if (Array.isArray(leftJoin) && leftJoin.length > 0) {
369
- if (table.length !== leftJoin.length + 1) {
370
- throw new Error(`${label}.table 与 ${label}.leftJoin 数量不匹配,要求 table.length = leftJoin.length + 1`, {
228
+ for (const key of rowKeys) {
229
+ if (!fieldSet.has(key)) {
230
+ throw new Error(`批量插入每行字段必须一致 (table: ${options.table}, rowIndex: ${i}, extraField: ${key})`, {
371
231
  cause: null,
372
232
  code: "validation"
373
233
  });
374
234
  }
375
- return;
376
235
  }
377
236
 
378
- if (table.length > 1) {
379
- throw new Error(`${label}.table 为数组时必须配合 leftJoin 使用`, {
380
- cause: null,
381
- code: "validation"
382
- });
383
- }
384
- return;
385
- }
386
-
387
- assertNonEmptyString(table, `${label}.table`);
388
- parseTableRef(table);
389
-
390
- if (Array.isArray(leftJoin) && leftJoin.length > 0) {
391
- throw new Error(`${label}.leftJoin 启用时,${label}.table 必须是数组`, {
392
- cause: null,
393
- code: "validation"
394
- });
395
- }
396
- }
397
-
398
- function validateQueryOptions(options, label) {
399
- if (!isPlainObject(options)) {
400
- throw new Error(`${label} 必须是对象`, {
401
- cause: null,
402
- code: "validation"
403
- });
404
- }
405
-
406
- validateQueryTableOption(options.table, options.leftJoin, label);
407
-
408
- if (!isNullable(options.where)) {
409
- validateWhereObject(options.where, `${label}.where`);
410
- }
411
-
412
- if (!isNullable(options.fields) && !Array.isArray(options.fields)) {
413
- throw new Error(`${label}.fields 必须是数组`, {
414
- cause: null,
415
- code: "validation"
416
- });
417
- }
418
-
419
- if (!isNullable(options.orderBy)) {
420
- validateOrderByItems(options.orderBy, `${label}.orderBy`);
421
- }
422
-
423
- validateLegacyJoinOptions(options, label);
424
-
425
- if (!isNullable(options.leftJoin)) {
426
- validateLeftJoinItems(options.leftJoin, `${label}.leftJoin`);
427
- }
428
-
429
- if (!isNullable(options.page) && (!Number.isFinite(options.page) || Math.floor(options.page) !== options.page)) {
430
- throw new Error(`${label}.page 必须是整数`, {
431
- cause: null,
432
- code: "validation"
433
- });
434
- }
435
-
436
- if (!isNullable(options.limit) && (!Number.isFinite(options.limit) || Math.floor(options.limit) !== options.limit)) {
437
- throw new Error(`${label}.limit 必须是整数`, {
438
- cause: null,
439
- code: "validation"
440
- });
441
- }
442
- }
443
-
444
- function validateTableName(table, label) {
445
- assertNonEmptyString(table, label);
446
- }
447
-
448
- function validateBatchDataList(dataList, label) {
449
- if (!Array.isArray(dataList)) {
450
- throw new Error(`${label} 必须是数组`, {
451
- cause: null,
452
- code: "validation"
453
- });
454
- }
455
-
456
- for (let i = 0; i < dataList.length; i++) {
457
- if (!isPlainObject(dataList[i])) {
458
- throw new Error(`${label}[${i}] 必须是对象`, {
459
- cause: null,
460
- code: "validation"
461
- });
237
+ for (const field of fields) {
238
+ if (!(field in row)) {
239
+ throw new Error(`批量插入缺少字段 (table: ${options.table}, rowIndex: ${i}, field: ${field})`, {
240
+ cause: null,
241
+ code: "validation"
242
+ });
243
+ }
244
+ if (row[field] === undefined) {
245
+ throw new Error(`批量插入字段值不能为 undefined (table: ${options.table}, rowIndex: ${i}, field: ${field})`, {
246
+ cause: null,
247
+ code: "validation"
248
+ });
249
+ }
462
250
  }
463
251
  }
464
- }
465
-
466
- function validateTableWhereOptions(options, label, required = false) {
467
- validateTableName(options.table, `${label}.table`);
468
- validateWhereObject(options.where, `${label}.where`, required);
469
- }
470
-
471
- function validateTableDataOptions(options, label) {
472
- validateTableName(options.table, `${label}.table`);
473
- assertPlainObjectValue(options.data, `${label}.data`);
474
- }
475
-
476
- function validateTableBatchDataOptions(table, dataList, label) {
477
- validateTableName(table, `${label}.table`);
478
- validateBatchDataList(dataList, `${label}.dataList`);
479
- }
480
-
481
- function validateSimpleTableName(rawTable, label) {
482
- if (Array.isArray(rawTable)) {
483
- throw new Error(`${label}.table 不支持数组`, {
484
- cause: null,
485
- code: "validation"
486
- });
487
- }
488
-
489
- const table = isString(rawTable) ? rawTable.trim() : "";
490
- if (!table) {
491
- throw new Error(`${label}.table 不能为空`, {
492
- cause: null,
493
- code: "validation"
494
- });
495
- }
496
- if (table.includes(" ")) {
497
- throw new Error(`${label} 不支持别名表写法(table: ${table})`, {
498
- cause: null,
499
- code: "validation"
500
- });
501
- }
502
- if (table.includes(".")) {
503
- throw new Error(`${label} 不支持 schema.table 写法(table: ${table})`, {
504
- cause: null,
505
- code: "validation"
506
- });
507
- }
508
- }
509
-
510
- function validateNoLeftJoin(leftJoin, message) {
511
- if (Array.isArray(leftJoin) && leftJoin.length > 0) {
512
- throw new Error(message, {
513
- cause: null,
514
- code: "validation"
515
- });
516
- }
517
- }
518
-
519
- function validateNoLeftJoinReadOptions(options, label, joinErrorMessage) {
520
- validateTableName(options.table, `${label}.table`);
521
- validateWhereObject(options.where, `${label}.where`, false);
522
- validateNoLeftJoin(options.leftJoin, joinErrorMessage);
523
- validateLegacyJoinOptions(options, label);
524
- validateSimpleTableName(options.table, label);
525
- }
526
-
527
- function validateSafeFieldName(field) {
528
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(field)) {
529
- throw new Error(`无效的字段名: ${field},只允许字母、数字和下划线`, {
530
- cause: null,
531
- code: "validation"
532
- });
533
- }
534
- }
535
-
536
- function validatePageLimitRange(prepared, rawTable) {
537
- if (prepared.page < 1 || prepared.page > 10000) {
538
- throw new Error(`页码必须在 1 到 10000 之间 (table: ${rawTable}, page: ${prepared.page}, limit: ${prepared.limit})`, {
539
- cause: null,
540
- code: "validation"
541
- });
542
- }
543
- if (prepared.limit < 1 || prepared.limit > 100000) {
544
- throw new Error(`每页数量必须在 1 到 100000 之间 (table: ${rawTable}, page: ${prepared.page}, limit: ${prepared.limit})`, {
545
- cause: null,
546
- code: "validation"
547
- });
548
- }
549
- }
550
-
551
- function validateGeneratedBatchId(id, table, index) {
552
- if (typeof id !== "number") {
553
- throw new Error(`批量插入生成 ID 失败:ids[${index}] 不是 number (table: ${table})`, {
554
- cause: null,
555
- code: "runtime"
556
- });
557
- }
558
- }
559
-
560
- function validateTimeIdValue(id) {
561
- if (!Number.isFinite(id) || id <= 0) {
562
- throw new Error(`buildInsertRow(timeId) 失败:id 必须为 > 0 的有限 number (id: ${String(id)})`, {
563
- cause: null,
564
- code: "validation"
565
- });
566
- }
567
- }
568
-
569
- function validateInsertBatchSize(count, maxBatchSize) {
570
- if (count > maxBatchSize) {
571
- throw new Error(`批量插入数量 ${count} 超过最大限制 ${maxBatchSize}`, {
572
- cause: null,
573
- code: "validation"
574
- });
575
- }
576
- }
577
-
578
- function validateIncrementValue(table, field, value) {
579
- if (typeof value !== "number" || Number.isNaN(value)) {
580
- throw new Error(`自增值必须是有效的数字 (table: ${table}, field: ${field}, value: ${value})`, {
581
- cause: null,
582
- code: "validation"
583
- });
584
- }
585
- }
586
-
587
- function validateIncrementOptions(table, field, where, value, label) {
588
- validateTableName(table, `${label}.table`);
589
- validateWhereObject(where, `${label}.where`, true);
590
- validateSafeFieldName(snakeCase(table));
591
- validateSafeFieldName(snakeCase(field));
592
- validateIncrementValue(table, field, value);
593
- }
594
-
595
- function validateIdList(ids, label) {
596
- if (!Array.isArray(ids)) {
597
- throw new Error(`${label} 必须是数组`, {
598
- cause: null,
599
- code: "validation"
600
- });
601
- }
602
- }
603
-
604
- function validateEffectiveWhere(snakeWhere, label) {
605
- if (!snakeWhere || Object.keys(snakeWhere).length === 0) {
606
- throw new Error(`${label} 需要有效的 WHERE 条件`, {
607
- cause: null,
608
- code: "validation"
609
- });
610
- }
611
- }
612
-
613
- function validateDataReadOptions(options, label) {
614
- validateQueryOptions(options, label);
615
-
616
- const hasLeftJoin = Array.isArray(options.leftJoin) && options.leftJoin.length > 0;
617
- const classifiedFields = hasLeftJoin ? validateExplicitLeftJoinFields(options.fields) : validateExplicitReadFields(options.fields);
618
-
619
- return {
620
- hasLeftJoin: hasLeftJoin,
621
- classifiedFields: classifiedFields
622
- };
623
- }
624
-
625
- function validateCountReadOptions(options, label) {
626
- validateQueryOptions(options, label);
627
252
 
628
- return {
629
- hasLeftJoin: Array.isArray(options.leftJoin) && options.leftJoin.length > 0
630
- };
631
- }
632
-
633
- function validateGetCountOptions(options) {
634
- return validateCountReadOptions(options, "getCount.options");
635
- }
636
-
637
- function validateGetOneOptions(options) {
638
- return validateDataReadOptions(options, "getOne.options");
639
- }
640
-
641
- function validateGetListOptions(options) {
642
- return validateDataReadOptions(options, "getList.options");
643
- }
644
-
645
- function validateGetAllOptions(options) {
646
- return validateDataReadOptions(options, "getAll.options");
253
+ return fields;
647
254
  }
648
255
 
649
256
  class DbHelper {
@@ -661,6 +268,30 @@ class DbHelper {
661
268
  this.sql = options.sql || null;
662
269
  this.isTransaction = options.isTransaction === true;
663
270
  this.beflyMode = options.beflyMode === "manual" ? "manual" : "auto";
271
+ this.tables = isPlainObject(options.tables) ? options.tables : {};
272
+ }
273
+
274
+ getTableDef(table) {
275
+ const tableName = parseTableRef(table).table;
276
+
277
+ if (!/^_?[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/.test(tableName)) {
278
+ throw new Error(`表名必须使用小驼峰写法 (table: ${tableName})`, {
279
+ cause: null,
280
+ code: "validation"
281
+ });
282
+ }
283
+
284
+ const tableInfo = this.tables[tableName];
285
+
286
+ if (!isPlainObject(tableInfo)) {
287
+ const sourceLabel = tableName.startsWith("befly") ? "packages/core/tables" : "app tables";
288
+ throw new Error(`表 ${tableName} 缺少本地 tables 定义,请检查 ${sourceLabel}`, {
289
+ cause: null,
290
+ code: "runtime"
291
+ });
292
+ }
293
+
294
+ return tableInfo;
664
295
  }
665
296
 
666
297
  async execute(sql, params) {
@@ -764,20 +395,16 @@ class DbHelper {
764
395
  return new SqlBuilder({ quoteIdent: quoteIdentMySql });
765
396
  }
766
397
 
767
- createListBuilder(prepared, whereFiltered, limit, offset) {
768
- const builder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(whereFiltered).limit(limit);
769
- this.applyLeftJoins(builder, prepared.leftJoins);
770
- if (!isNullable(offset)) {
771
- builder.offset(offset);
772
- }
773
- if (prepared.orderBy && prepared.orderBy.length > 0) {
774
- builder.orderBy(prepared.orderBy);
775
- }
776
- return builder;
398
+ createDbParse() {
399
+ return new DbParse({
400
+ tables: this.tables,
401
+ beflyMode: this.beflyMode,
402
+ getTableColumns: this.getTableColumns.bind(this)
403
+ });
777
404
  }
778
405
 
779
- async fetchCount(prepared, whereFiltered, alias) {
780
- const builder = this.createSqlBuilder().selectRaw(alias).from(prepared.table).where(whereFiltered);
406
+ async fetchCount(prepared, alias) {
407
+ const builder = this.createSqlBuilder().selectRaw(alias).from(prepared.table).where(prepared.where);
781
408
  this.applyLeftJoins(builder, prepared.leftJoins);
782
409
  const result = builder.toSelectSql();
783
410
  const executeRes = await this.execute(result.sql, result.params);
@@ -815,22 +442,11 @@ class DbHelper {
815
442
  return normalizeBigIntValues(deserializedList);
816
443
  }
817
444
 
818
- _joinFieldsToSnake(classifiedFields) {
819
- if (classifiedFields.type === "include") {
820
- return classifiedFields.fields.map((field) => processJoinField(field));
821
- }
822
-
823
- return [];
824
- }
825
-
826
- _cleanAndSnakeAndSerializeWriteData(data, excludeValues = [null, undefined]) {
827
- const cleanData = fieldClear(data, { excludeValues: excludeValues });
828
- return serializeArrayFields(keysToSnake(cleanData));
829
- }
830
-
831
- _stripSystemFieldsForWrite(data, allowState) {
445
+ _prepareWriteUserData(data, allowState) {
446
+ const cleanData = fieldClear(data, { excludeValues: [null, undefined] });
832
447
  const result = {};
833
- for (const [key, value] of Object.entries(data)) {
448
+
449
+ for (const [key, value] of Object.entries(serializeArrayFields(keysToSnake(cleanData)))) {
834
450
  if (key === "id") continue;
835
451
  if (key === "created_at") continue;
836
452
  if (key === "updated_at") continue;
@@ -838,18 +454,15 @@ class DbHelper {
838
454
  if (!allowState && key === "state") continue;
839
455
  result[key] = value;
840
456
  }
841
- return result;
842
- }
843
457
 
844
- _prepareWriteUserData(data, allowState) {
845
- return this._stripSystemFieldsForWrite(this._cleanAndSnakeAndSerializeWriteData(data), allowState);
458
+ return result;
846
459
  }
847
460
 
848
461
  _buildInsertRow(options) {
849
462
  const result = { ...this._prepareWriteUserData(options.data, false) };
850
463
 
851
464
  if (options.beflyMode === "auto") {
852
- validateTimeIdValue(options.id);
465
+ assertTimeIdValue(options.id);
853
466
  result.id = options.id;
854
467
  }
855
468
 
@@ -870,126 +483,18 @@ class DbHelper {
870
483
  return result;
871
484
  }
872
485
 
873
- _buildPartialUpdateData(options) {
874
- return this._prepareWriteUserData(options.data, options.allowState);
875
- }
876
-
877
- normalizeLeftJoinOptions(options, cleanWhere, classifiedFields) {
878
- const processedFields = this._joinFieldsToSnake(classifiedFields);
879
- const mainTableRef = options.table[0];
880
- const joinTableRefs = options.table.slice(1);
881
- const normalizedTableRef = normalizeTableRef(mainTableRef);
882
- const mainQualifier = getJoinMainQualifier(mainTableRef);
883
- const leftJoins = options.leftJoin.map((joinItem, index) => parseLeftJoinItem(joinTableRefs[index], joinItem));
884
-
885
- return {
886
- table: normalizedTableRef,
887
- tableQualifier: mainQualifier,
888
- fields: processedFields,
889
- where: processJoinWhere(cleanWhere),
890
- leftJoins: leftJoins,
891
- orderBy: processJoinOrderBy(options.orderBy || []),
892
- page: options.page || 1,
893
- limit: options.limit || 10
894
- };
895
- }
896
-
897
- normalizeLeftJoinCountOptions(options, cleanWhere) {
898
- const mainTableRef = options.table[0];
899
- const joinTableRefs = options.table.slice(1);
900
- const normalizedTableRef = normalizeTableRef(mainTableRef);
901
- const mainQualifier = getJoinMainQualifier(mainTableRef);
902
- const leftJoins = options.leftJoin.map((joinItem, index) => parseLeftJoinItem(joinTableRefs[index], joinItem));
903
-
904
- return {
905
- table: normalizedTableRef,
906
- tableQualifier: mainQualifier,
907
- fields: [],
908
- where: processJoinWhere(cleanWhere),
909
- leftJoins: leftJoins,
910
- orderBy: [],
911
- page: options.page || 1,
912
- limit: options.limit || 10
913
- };
914
- }
915
-
916
- async normalizeSingleTableOptions(options, cleanWhere, classifiedFields) {
917
- const tableRef = Array.isArray(options.table) ? options.table[0] : options.table;
918
- const snakeTable = snakeCase(tableRef);
919
- const processedFields = await fieldsToSnake(snakeTable, classifiedFields, this.getTableColumns.bind(this));
920
-
921
- return {
922
- table: snakeTable,
923
- tableQualifier: snakeTable,
924
- fields: processedFields,
925
- where: whereKeysToSnake(cleanWhere),
926
- leftJoins: [],
927
- orderBy: orderByToSnake(options.orderBy || []),
928
- page: options.page || 1,
929
- limit: options.limit || 10
930
- };
931
- }
932
-
933
- buildQueryOptions(options, defaults) {
934
- const output = {
935
- table: options.table,
936
- page: defaults.page,
937
- limit: defaults.limit
938
- };
939
-
940
- if (options.fields !== undefined) output.fields = options.fields;
941
- if (options.where !== undefined) output.where = options.where;
942
- if (options.leftJoin !== undefined) output.leftJoin = options.leftJoin;
943
- if (options.orderBy !== undefined) output.orderBy = options.orderBy;
944
- return output;
945
- }
946
-
947
- resolveFieldValue(record, field) {
948
- if (!isPlainObject(record)) {
949
- return null;
950
- }
951
-
952
- if (Object.hasOwn(record, field)) {
953
- return record[field];
954
- }
955
-
956
- const camelField = field.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
957
- if (camelField !== field && Object.hasOwn(record, camelField)) {
958
- return record[camelField];
959
- }
960
-
961
- const snakeField = field.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
962
- if (snakeField !== field && Object.hasOwn(record, snakeField)) {
963
- return record[snakeField];
964
- }
965
-
966
- return null;
967
- }
968
-
969
486
  async getTableColumns(table) {
970
- const parsed = parseTableRef(table);
971
- const schemaPart = parsed.schema ? `${quoteIdentMySql(snakeCase(parsed.schema))}.` : "";
972
- const quotedTable = quoteIdentMySql(snakeCase(parsed.table));
973
- const executeRes = await this.execute(`SHOW COLUMNS FROM ${schemaPart}${quotedTable}`, []);
974
- const result = executeRes.data;
487
+ const tableInfo = this.getTableDef(table);
488
+ const fieldNames = Object.keys(tableInfo);
975
489
 
976
- if (!result || result.length === 0) {
977
- throw new Error(`表 ${table} 不存在或没有字段`, {
490
+ if (fieldNames.length === 0) {
491
+ throw new Error(`表 ${table} 的本地 tables 定义为空或没有字段`, {
978
492
  cause: null,
979
493
  code: "runtime"
980
494
  });
981
495
  }
982
496
 
983
- const columnNames = [];
984
- for (const row of result) {
985
- const record = row;
986
- const name = record.Field;
987
- if (isNonEmptyString(name)) {
988
- columnNames.push(name);
989
- }
990
- }
991
-
992
- return columnNames;
497
+ return fieldNames.map((fieldName) => snakeCase(fieldName));
993
498
  }
994
499
 
995
500
  applyLeftJoins(builder, leftJoins) {
@@ -1002,71 +507,6 @@ class DbHelper {
1002
507
  }
1003
508
  }
1004
509
 
1005
- async prepareBaseReadContext(options, validation, buildPrepared) {
1006
- const cleanWhere = clearDeep(options.where || {});
1007
- const prepared = await buildPrepared.call(this, options, cleanWhere, validation);
1008
-
1009
- return {
1010
- prepared: prepared,
1011
- whereFiltered: addDefaultStateFilter(prepared.where, prepared.tableQualifier, prepared.leftJoins.length > 0, this.beflyMode)
1012
- };
1013
- }
1014
-
1015
- async buildPreparedReadOptions(options, cleanWhere, validation) {
1016
- if (validation.hasLeftJoin) {
1017
- return this.normalizeLeftJoinOptions(options, cleanWhere, validation.classifiedFields);
1018
- }
1019
-
1020
- return await this.normalizeSingleTableOptions(options, cleanWhere, validation.classifiedFields);
1021
- }
1022
-
1023
- buildPreparedCountOptions(options, cleanWhere, validation) {
1024
- if (validation.hasLeftJoin) {
1025
- return this.normalizeLeftJoinCountOptions(options, cleanWhere);
1026
- }
1027
-
1028
- const snakeTable = snakeCase(Array.isArray(options.table) ? options.table[0] : options.table);
1029
- return {
1030
- table: snakeTable,
1031
- tableQualifier: snakeTable,
1032
- fields: [],
1033
- where: whereKeysToSnake(cleanWhere),
1034
- leftJoins: [],
1035
- orderBy: [],
1036
- page: options.page || 1,
1037
- limit: options.limit || 10
1038
- };
1039
- }
1040
-
1041
- async prepareReadContext(options, validation) {
1042
- return await this.prepareBaseReadContext(options, validation, this.buildPreparedReadOptions);
1043
- }
1044
-
1045
- async prepareCountContext(options, validation) {
1046
- return await this.prepareBaseReadContext(options, validation, this.buildPreparedCountOptions);
1047
- }
1048
-
1049
- prepareSingleTableWhere(table, where, useDefaultStateFilter = true) {
1050
- const snakeTable = snakeCase(table);
1051
- const snakeWhere = whereKeysToSnake(clearDeep(where || {}));
1052
-
1053
- return {
1054
- snakeTable: snakeTable,
1055
- whereFiltered: useDefaultStateFilter ? addDefaultStateFilter(snakeWhere, snakeTable, false, this.beflyMode) : snakeWhere
1056
- };
1057
- }
1058
-
1059
- prepareRequiredSingleTableWhere(table, where, useDefaultStateFilter, label) {
1060
- const snakeTable = snakeCase(table);
1061
- const snakeWhere = whereKeysToSnake(clearDeep(where || {}));
1062
- validateEffectiveWhere(snakeWhere, label);
1063
-
1064
- return {
1065
- snakeTable: snakeTable,
1066
- whereFiltered: useDefaultStateFilter ? addDefaultStateFilter(snakeWhere, snakeTable, false, this.beflyMode) : snakeWhere
1067
- };
1068
- }
1069
-
1070
510
  async createInsertRows(table, snakeTable, dataList, now) {
1071
511
  if (this.beflyMode === "manual") {
1072
512
  return {
@@ -1102,7 +542,7 @@ class DbHelper {
1102
542
  const processedList = dataList.map((data, index) => {
1103
543
  const id = ids[index];
1104
544
  if (dataList.length > 1) {
1105
- validateGeneratedBatchId(id, snakeTable, index);
545
+ assertGeneratedBatchId(id, snakeTable, index);
1106
546
  }
1107
547
 
1108
548
  return this._buildInsertRow({
@@ -1150,9 +590,8 @@ class DbHelper {
1150
590
  }
1151
591
 
1152
592
  async getCount(options) {
1153
- const validation = validateGetCountOptions(options);
1154
- const { prepared, whereFiltered } = await this.prepareCountContext(options, validation);
1155
- const result = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as count");
593
+ const prepared = this.createDbParse().parseCount(options);
594
+ const result = await this.fetchCount(prepared, "COUNT(*) as count");
1156
595
 
1157
596
  return {
1158
597
  data: result.total,
@@ -1161,9 +600,8 @@ class DbHelper {
1161
600
  }
1162
601
 
1163
602
  async getOne(options) {
1164
- const validation = validateGetOneOptions(options);
1165
- const { prepared, whereFiltered } = await this.prepareReadContext(options, validation);
1166
- const builder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(whereFiltered);
603
+ const prepared = await this.createDbParse().parseRead(options, "one");
604
+ const builder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(prepared.where);
1167
605
  this.applyLeftJoins(builder, prepared.leftJoins);
1168
606
 
1169
607
  const { sql, params } = builder.toSelectSql();
@@ -1178,14 +616,16 @@ class DbHelper {
1178
616
  }
1179
617
 
1180
618
  async getList(options) {
1181
- const validation = validateGetListOptions(options);
1182
- const { prepared, whereFiltered } = await this.prepareReadContext(options, validation);
1183
- validatePageLimitRange(prepared, options.table);
1184
- const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
619
+ const prepared = await this.createDbParse().parseRead(options, "list");
620
+ const countResult = await this.fetchCount(prepared, "COUNT(*) as total");
1185
621
  const total = countResult.total;
1186
622
 
1187
623
  const offset = (prepared.page - 1) * prepared.limit;
1188
- const dataBuilder = this.createListBuilder(prepared, whereFiltered, prepared.limit, offset);
624
+ const dataBuilder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(prepared.where).limit(prepared.limit).offset(offset);
625
+ this.applyLeftJoins(dataBuilder, prepared.leftJoins);
626
+ if (prepared.orderBy && prepared.orderBy.length > 0) {
627
+ dataBuilder.orderBy(prepared.orderBy);
628
+ }
1189
629
  const { sql: dataSql, params: dataParams } = dataBuilder.toSelectSql();
1190
630
 
1191
631
  if (total === 0) {
@@ -1228,10 +668,19 @@ class DbHelper {
1228
668
  async getAll(options) {
1229
669
  const MAX_LIMIT = 100000;
1230
670
  const WARNING_LIMIT = 10000;
1231
- const prepareOptions = this.buildQueryOptions(options, { page: 1, limit: 10 });
1232
- const validation = validateGetAllOptions(prepareOptions);
1233
- const { prepared, whereFiltered } = await this.prepareReadContext(prepareOptions, validation);
1234
- const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
671
+ const prepareOptions = {
672
+ table: options.table,
673
+ page: 1,
674
+ limit: 10
675
+ };
676
+
677
+ if (options.fields !== undefined) prepareOptions.fields = options.fields;
678
+ if (options.where !== undefined) prepareOptions.where = options.where;
679
+ if (options.leftJoin !== undefined) prepareOptions.leftJoin = options.leftJoin;
680
+ if (options.orderBy !== undefined) prepareOptions.orderBy = options.orderBy;
681
+
682
+ const prepared = await this.createDbParse().parseRead(prepareOptions, "all");
683
+ const countResult = await this.fetchCount(prepared, "COUNT(*) as total");
1235
684
  const total = countResult.total;
1236
685
 
1237
686
  if (total === 0) {
@@ -1246,7 +695,11 @@ class DbHelper {
1246
695
  };
1247
696
  }
1248
697
 
1249
- const dataBuilder = this.createListBuilder(prepared, whereFiltered, MAX_LIMIT, null);
698
+ const dataBuilder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(prepared.where).limit(MAX_LIMIT);
699
+ this.applyLeftJoins(dataBuilder, prepared.leftJoins);
700
+ if (prepared.orderBy && prepared.orderBy.length > 0) {
701
+ dataBuilder.orderBy(prepared.orderBy);
702
+ }
1250
703
 
1251
704
  const { sql: dataSql, params: dataParams } = dataBuilder.toSelectSql();
1252
705
  const listExecuteRes = await this.execute(dataSql, dataParams);
@@ -1275,10 +728,9 @@ class DbHelper {
1275
728
  }
1276
729
 
1277
730
  async exists(options) {
1278
- validateNoLeftJoinReadOptions(options, "exists", "exists 不支持 leftJoin(请使用显式 query 或拆分查询)");
1279
- const prepared = this.prepareSingleTableWhere(options.table, options.where, true);
731
+ const prepared = this.createDbParse().parseExists(options);
1280
732
 
1281
- const builder = this.createSqlBuilder().selectRaw("COUNT(1) as cnt").from(prepared.snakeTable).where(prepared.whereFiltered).limit(1);
733
+ const builder = this.createSqlBuilder().selectRaw("COUNT(1) as cnt").from(prepared.snakeTable).where(prepared.where).limit(1);
1282
734
  const { sql, params } = builder.toSelectSql();
1283
735
  const executeRes = await this.execute(sql, params);
1284
736
  const exists = (executeRes.data?.[0]?.cnt || 0) > 0;
@@ -1286,38 +738,55 @@ class DbHelper {
1286
738
  }
1287
739
 
1288
740
  async getFieldValue(options) {
1289
- validateNoLeftJoinReadOptions(options, "getFieldValue", "getFieldValue 不支持 leftJoin(请使用 getOne/getList 并自行取字段)");
1290
- const field = options.field;
1291
- validateSafeFieldName(field);
741
+ const parsed = await this.createDbParse().parseFieldValue(options);
742
+ const builder = this.createSqlBuilder().select(parsed.prepared.fields).from(parsed.prepared.table).where(parsed.prepared.where);
743
+ this.applyLeftJoins(builder, parsed.prepared.leftJoins);
744
+ if (parsed.prepared.orderBy && parsed.prepared.orderBy.length > 0) {
745
+ builder.orderBy(parsed.prepared.orderBy);
746
+ }
747
+ const { sql, params } = builder.toSelectSql();
748
+ const executeRes = await this.execute(sql, params);
749
+ const result = this.normalizeRowData(executeRes.data?.[0] || null);
750
+ let value = null;
751
+ let hasValue = false;
752
+
753
+ if (isPlainObject(result)) {
754
+ if (Object.hasOwn(result, parsed.field)) {
755
+ value = result[parsed.field];
756
+ hasValue = true;
757
+ }
1292
758
 
1293
- const oneOptions = {
1294
- table: options.table
1295
- };
1296
- if (options.where !== undefined) oneOptions.where = options.where;
1297
- if (options.leftJoin !== undefined) oneOptions.leftJoin = options.leftJoin;
1298
- if (options.orderBy !== undefined) oneOptions.orderBy = options.orderBy;
1299
- if (options.page !== undefined) oneOptions.page = options.page;
1300
- if (options.limit !== undefined) oneOptions.limit = options.limit;
1301
- oneOptions.fields = [field];
759
+ if (!hasValue) {
760
+ const camelField = parsed.field.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
761
+ if (camelField !== parsed.field && Object.hasOwn(result, camelField)) {
762
+ value = result[camelField];
763
+ hasValue = true;
764
+ }
765
+ }
1302
766
 
1303
- const oneRes = await this.getOne(oneOptions);
767
+ if (!hasValue) {
768
+ const snakeField = parsed.field.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
769
+ if (snakeField !== parsed.field && Object.hasOwn(result, snakeField)) {
770
+ value = result[snakeField];
771
+ }
772
+ }
773
+ }
1304
774
 
1305
- const result = oneRes.data;
1306
- const value = this.resolveFieldValue(result, field);
1307
775
  return {
1308
776
  data: value,
1309
- sql: oneRes.sql
777
+ sql: executeRes.sql
1310
778
  };
1311
779
  }
1312
780
 
1313
781
  async insData(options) {
1314
- validateTableDataOptions(options, "insData");
1315
- const { table, data } = options;
1316
- const snakeTable = snakeCase(table);
782
+ const parsed = this.createDbParse().parseInsert(options);
783
+ const { table, snakeTable, data } = parsed;
1317
784
  const now = Date.now();
1318
785
  const insertRows = await this.createInsertRows(table, snakeTable, [data], now);
1319
786
  const processed = insertRows.processedList[0];
1320
787
 
788
+ assertWriteDataHasFields(processed, "插入数据必须至少有一个字段", snakeTable);
789
+
1321
790
  assertNoUndefinedInRecord(processed, `insData 插入数据 (table: ${snakeTable})`);
1322
791
 
1323
792
  const builder = this.createSqlBuilder();
@@ -1331,7 +800,9 @@ class DbHelper {
1331
800
  }
1332
801
 
1333
802
  async insBatch(table, dataList) {
1334
- validateTableBatchDataOptions(table, dataList, "insBatch");
803
+ const parsed = this.createDbParse().parseInsertBatch(table, dataList);
804
+ table = parsed.table;
805
+ dataList = parsed.dataList;
1335
806
  if (dataList.length === 0) {
1336
807
  return {
1337
808
  data: [],
@@ -1340,9 +811,9 @@ class DbHelper {
1340
811
  }
1341
812
 
1342
813
  const MAX_BATCH_SIZE = 1000;
1343
- validateInsertBatchSize(dataList.length, MAX_BATCH_SIZE);
814
+ assertInsertBatchSize(dataList.length, MAX_BATCH_SIZE);
1344
815
 
1345
- const snakeTable = snakeCase(table);
816
+ const snakeTable = parsed.snakeTable;
1346
817
  const now = Date.now();
1347
818
  const insertRows = await this.createInsertRows(table, snakeTable, dataList, now);
1348
819
  const processedList = insertRows.processedList;
@@ -1378,8 +849,9 @@ class DbHelper {
1378
849
  }
1379
850
 
1380
851
  async delForceBatch(table, ids) {
1381
- validateTableName(table, "delForceBatch.table");
1382
- validateIdList(ids, "delForceBatch.ids");
852
+ const parsed = this.createDbParse().parseDelForceBatch(table, ids);
853
+ table = parsed.table;
854
+ ids = parsed.ids;
1383
855
  if (ids.length === 0) {
1384
856
  return {
1385
857
  data: 0,
@@ -1387,7 +859,7 @@ class DbHelper {
1387
859
  };
1388
860
  }
1389
861
 
1390
- const snakeTable = snakeCase(table);
862
+ const snakeTable = parsed.snakeTable;
1391
863
  const query = SqlBuilder.toDeleteInSql({
1392
864
  table: snakeTable,
1393
865
  idField: "id",
@@ -1403,7 +875,9 @@ class DbHelper {
1403
875
  }
1404
876
 
1405
877
  async updBatch(table, dataList) {
1406
- validateTableBatchDataOptions(table, dataList, "updBatch");
878
+ const parsed = this.createDbParse().parseUpdateBatch(table, dataList);
879
+ table = parsed.table;
880
+ dataList = parsed.dataList;
1407
881
  if (dataList.length === 0) {
1408
882
  return {
1409
883
  data: 0,
@@ -1411,14 +885,14 @@ class DbHelper {
1411
885
  };
1412
886
  }
1413
887
 
1414
- const snakeTable = snakeCase(table);
888
+ const snakeTable = parsed.snakeTable;
1415
889
  const now = Date.now();
1416
890
 
1417
891
  const processedList = [];
1418
892
  const fieldSet = new Set();
1419
893
 
1420
894
  for (const item of dataList) {
1421
- const userData = this._buildPartialUpdateData({ data: item.data, allowState: true });
895
+ const userData = this._prepareWriteUserData(item.data, true);
1422
896
 
1423
897
  for (const key of Object.keys(userData)) {
1424
898
  fieldSet.add(key);
@@ -1456,14 +930,12 @@ class DbHelper {
1456
930
  }
1457
931
 
1458
932
  async updData(options) {
1459
- validateTableDataOptions(options, "updData");
1460
- validateTableWhereOptions(options, "updData", true);
1461
- const { table, data, where } = options;
1462
- const prepared = this.prepareRequiredSingleTableWhere(table, where, true, "updData");
933
+ const parsed = this.createDbParse().parseUpdate(options);
1463
934
 
1464
- const processed = this._buildUpdateRow({ data: data, now: Date.now(), allowState: true, beflyMode: this.beflyMode });
1465
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
1466
- const { sql, params } = builder.toUpdateSql(prepared.snakeTable, processed);
935
+ const processed = this._buildUpdateRow({ data: parsed.data, now: Date.now(), allowState: true, beflyMode: this.beflyMode });
936
+ assertWriteDataHasFields(processed, "更新数据必须至少有一个字段", parsed.snakeTable);
937
+ const builder = this.createSqlBuilder().where(parsed.where);
938
+ const { sql, params } = builder.toUpdateSql(parsed.snakeTable, processed);
1467
939
 
1468
940
  const executeRes = await this.execute(sql, params);
1469
941
  const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
@@ -1474,9 +946,7 @@ class DbHelper {
1474
946
  }
1475
947
 
1476
948
  async delData(options) {
1477
- validateTableWhereOptions(options, "delData", true);
1478
- const { table, where } = options;
1479
- const prepared = this.prepareRequiredSingleTableWhere(table, where, true, "delData");
949
+ const parsed = this.createDbParse().parseDelete(options, false);
1480
950
  const now = Date.now();
1481
951
  const processed = {
1482
952
  state: 0,
@@ -1487,8 +957,8 @@ class DbHelper {
1487
957
  processed.updated_at = now;
1488
958
  }
1489
959
 
1490
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
1491
- const { sql, params } = builder.toUpdateSql(prepared.snakeTable, processed);
960
+ const builder = this.createSqlBuilder().where(parsed.where);
961
+ const { sql, params } = builder.toUpdateSql(parsed.snakeTable, processed);
1492
962
  const executeRes = await this.execute(sql, params);
1493
963
  const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
1494
964
 
@@ -1499,12 +969,10 @@ class DbHelper {
1499
969
  }
1500
970
 
1501
971
  async delForce(options) {
1502
- validateTableWhereOptions(options, "delForce", true);
1503
- const { table, where } = options;
1504
- const prepared = this.prepareRequiredSingleTableWhere(table, where, false, "delForce");
972
+ const parsed = this.createDbParse().parseDelete(options, true);
1505
973
 
1506
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
1507
- const { sql, params } = builder.toDeleteSql(prepared.snakeTable);
974
+ const builder = this.createSqlBuilder().where(parsed.where);
975
+ const { sql, params } = builder.toDeleteSql(parsed.snakeTable);
1508
976
 
1509
977
  const executeRes = await this.execute(sql, params);
1510
978
  const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
@@ -1515,7 +983,6 @@ class DbHelper {
1515
983
  }
1516
984
 
1517
985
  async disableData(options) {
1518
- validateTableWhereOptions(options, "disableData", true);
1519
986
  const { table, where } = options;
1520
987
 
1521
988
  return await this.updData({
@@ -1528,7 +995,6 @@ class DbHelper {
1528
995
  }
1529
996
 
1530
997
  async enableData(options) {
1531
- validateTableWhereOptions(options, "enableData", true);
1532
998
  const { table, where } = options;
1533
999
 
1534
1000
  return await this.updData({
@@ -1541,18 +1007,16 @@ class DbHelper {
1541
1007
  }
1542
1008
 
1543
1009
  async increment(table, field, where, value = 1) {
1544
- validateIncrementOptions(table, field, where, value, "increment");
1545
- const prepared = this.prepareRequiredSingleTableWhere(table, where, true, "increment");
1546
- const snakeField = snakeCase(field);
1010
+ const parsed = this.createDbParse().parseIncrement(table, field, where, value, "increment");
1547
1011
 
1548
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
1012
+ const builder = this.createSqlBuilder().where(parsed.where);
1549
1013
  const { sql: whereClause, params: whereParams } = builder.getWhereConditions();
1550
1014
 
1551
- const quotedTable = quoteIdentMySql(prepared.snakeTable);
1552
- const quotedField = quoteIdentMySql(snakeField);
1015
+ const quotedTable = quoteIdentMySql(parsed.snakeTable);
1016
+ const quotedField = quoteIdentMySql(parsed.snakeField);
1553
1017
  const sql = whereClause ? `UPDATE ${quotedTable} SET ${quotedField} = ${quotedField} + ? WHERE ${whereClause}` : `UPDATE ${quotedTable} SET ${quotedField} = ${quotedField} + ?`;
1554
1018
 
1555
- const params = [value];
1019
+ const params = [parsed.value];
1556
1020
  for (const param of whereParams) {
1557
1021
  params.push(param);
1558
1022
  }
@@ -1600,7 +1064,8 @@ class DbHelper {
1600
1064
  dbName: this.dbName,
1601
1065
  sql: tx,
1602
1066
  isTransaction: true,
1603
- beflyMode: this.beflyMode
1067
+ beflyMode: this.beflyMode,
1068
+ tables: this.tables
1604
1069
  });
1605
1070
  const result = await callback(trans);
1606
1071
  if (result?.code !== undefined && result.code !== 0) {