mm_sqlite 1.0.5 → 1.0.7

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/sql.js CHANGED
@@ -3,7 +3,16 @@
3
3
  * @author <a href="http://qww.elins.cn">邱文武</a>
4
4
  * @version 1.2
5
5
  */
6
- require('mm_expand');
6
+ require('mm_logs');
7
+ const SqlString = require('sqlstring');
8
+
9
+ function escape(value, stringifyObjects, timeZone) {
10
+ return SqlString.escape(value, stringifyObjects, timeZone);
11
+ }
12
+
13
+ function escapeId(value, forbidQualified) {
14
+ return SqlString.escapeId(value, forbidQualified);
15
+ }
7
16
 
8
17
  /**
9
18
  * @class 数据库语法通用类
@@ -24,6 +33,23 @@ class Sql {
24
33
  * 更改函数 用于增删改
25
34
  */
26
35
  this.exec = exec;
36
+ /**
37
+ * 规避SQL注入函数
38
+ * @param {Object} value 值
39
+ * @return {String} 返回执行结果
40
+ */
41
+ this.escape = function(value) {
42
+ return escape(value);
43
+ };
44
+
45
+ /**
46
+ * 规避排序、SQL注入函数
47
+ * @param {String} key 键
48
+ * @return {String} 返回执行结果
49
+ */
50
+ this.escapeId = function(key) {
51
+ return escapeId(key);
52
+ };
27
53
 
28
54
  /**
29
55
  * sql语句
@@ -65,6 +91,11 @@ class Sql {
65
91
  */
66
92
  this.orderby = "";
67
93
 
94
+ /**
95
+ * 查询分组
96
+ */
97
+ this.groupby = "";
98
+
68
99
  /**
69
100
  * 是否统计查询结果数
70
101
  */
@@ -112,25 +143,30 @@ class Sql {
112
143
  "count_ret": "count_ret"
113
144
  }
114
145
  };
146
+
147
+ this.like = false;
115
148
  }
116
149
  }
117
150
 
118
151
  /**
119
- * @description 清理存储的数据
152
+ * @description 清空查询条件
153
+ * @return {Object} 返回当前对象
120
154
  */
121
- Sql.prototype.clear = async function() {
122
- this.run = run;
123
- this.exec = exec;
155
+ Sql.prototype.clear = function() {
156
+ this.where = "";
157
+ this.set = "";
158
+ this.order = "";
159
+ this.view = "";
124
160
  this.sql = "";
125
- this.error;
126
- this.results = [];
127
- this.table = "";
161
+ this.error = "";
162
+ this.like = true;
163
+ this.config = {};
164
+ this.param = {};
128
165
  this.page = 0;
129
- this.size = 30;
130
- this.method = "";
131
- this.field = "";
132
- this.orderby = "";
133
- this.count_ret = "false";
166
+ this.size = 0;
167
+ this.run = null;
168
+ this.exec = null;
169
+ return this;
134
170
  };
135
171
 
136
172
  /**
@@ -164,7 +200,7 @@ Sql.prototype.toQuery = function(where, sort, view) {
164
200
  sql += " WHERE " + where;
165
201
  }
166
202
  if (sort) {
167
- sql += " ORDER BY " + sort;
203
+ sql += " ORDER BY " + sort.replace(/;/, '');
168
204
  }
169
205
  sql = sql.replace("{0}", this.table).replace("{1}", view);
170
206
  if (this.size && this.page) {
@@ -224,7 +260,7 @@ Sql.prototype.getSql = function(where, sort, view) {
224
260
  * @param {String} set 修改的键值
225
261
  * @return {Promise|Object} 执行结果
226
262
  */
227
- Sql.prototype.addOrSetSql = async function(where, set) {
263
+ Sql.prototype.addOrSetSql = async function(where, set, like) {
228
264
  if (!where || !set) {
229
265
  return -1;
230
266
  }
@@ -233,13 +269,14 @@ Sql.prototype.addOrSetSql = async function(where, set) {
233
269
  var arr = set.split(',');
234
270
  var key = "";
235
271
  var value = "";
236
- arr.map(function(o) {
272
+ for (var i = 0; i < arr.length; i++) {
273
+ var o = arr[i];
237
274
  var ar = o.split('=');
238
275
  if (ar.length === 2) {
239
276
  key += "," + ar[0];
240
277
  value += "," + ar[1];
241
278
  }
242
- });
279
+ }
243
280
  return await this.addSql(key.replace(',', ''), value.replace(',', ''));
244
281
  }
245
282
  return await this.setSql(where, set);
@@ -262,6 +299,7 @@ Sql.prototype.countSql = async function(where) {
262
299
  }
263
300
  return n;
264
301
  };
302
+
265
303
  /**
266
304
  * @description 查询数据并返回符合条件总数
267
305
  * @param {String} where 查询条件
@@ -281,20 +319,197 @@ Sql.prototype.getCountSql = async function(where, sort, view) {
281
319
  };
282
320
  return ret;
283
321
  };
322
+
323
+ /**
324
+ * @description 统计学
325
+ * @param {String} where 查询条件
326
+ * @param {String} groupby 分组的字段
327
+ * @param {String} view 返回的字段
328
+ * @param {String} sort 排序方式
329
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
330
+ */
331
+ Sql.prototype.groupMathSql = async function(where, groupby, view, sort, method) {
332
+ if (!view) {
333
+ view = "*"
334
+ }
335
+ var viewStr = "";
336
+ if (view.indexOf(",") !== -1) {
337
+ var arr = view.split(",");
338
+ for (var i = 0; i < arr.length; i++) {
339
+ var str = escapeId(arr[i]);
340
+ viewStr += "," + method.toUpperCase() + "(" + str + ") " + method.toLowerCase() + "_" + str.replace(
341
+ /`/g, "")
342
+ }
343
+ } else {
344
+ viewStr = "," + method.toUpperCase() + "(" + escapeId(view) + ") " + method.toLowerCase() + "_" +
345
+ view.replace(/`/g, "")
346
+ }
347
+ var sql = "SELECT " + (groupby ? escapeId(groupby) : "") + viewStr + " FROM `" + this.table + "`";
348
+ if (where) {
349
+ sql += ' WHERE ' + where;
350
+ }
351
+ if (groupby) {
352
+ sql += " GROUP BY " + escapeId(groupby);
353
+ }
354
+ if (sort) {
355
+ sql += " ORDER BY " + sort;
356
+ }
357
+ if (this.size && this.page) {
358
+ var start = this.size * (this.page - 1);
359
+ sql += " limit " + start + ',' + this.size;
360
+ }
361
+ return await this.run(sql);
362
+ }
363
+
364
+
365
+ /**
366
+ * @description 分组求平均值
367
+ * @param {String} where 查询条件
368
+ * @param {String} groupby 分组的字段
369
+ * @param {String} view 返回的字段
370
+ * @param {String} sort 排序方式
371
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
372
+ */
373
+ Sql.prototype.groupAvgSql = async function(where, groupby, view, sort = "") {
374
+ return await this.groupMathSql(where, groupby, view, sort, "AVG");
375
+ };
376
+
377
+ /**
378
+ * @description 分组合计数值
379
+ * @param {String} where 查询条件
380
+ * @param {String} groupby 分组的字段
381
+ * @param {String} view 返回的字段
382
+ * @param {String} sort 排序方式
383
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
384
+ */
385
+ Sql.prototype.groupSumSql = async function(where, groupby, view, sort = "") {
386
+ return await this.groupMathSql(where, groupby, view, sort, "SUM");
387
+ };
388
+
389
+ /**
390
+ * @description 分组合计不同条数
391
+ * @param {String} where 查询条件
392
+ * @param {String} groupby 分组的字段
393
+ * @param {String} view 返回的字段
394
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
395
+ */
396
+ Sql.prototype.groupCountSql = async function(where, groupby, view, sort = "") {
397
+ return await this.groupMathSql(where, groupby, view, sort, "COUNT");
398
+ };
399
+
400
+
401
+ /**
402
+ * @description 统计学
403
+ * @param {Object} query 查询条件
404
+ * @param {String} groupby 分组的字段
405
+ * @param {String} view 返回的字段
406
+ * @param {String} sort 排序方式
407
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
408
+ */
409
+ Sql.prototype.groupMath = async function(query, groupby, view, sort, method) {
410
+ var where = this.toWhere(query, this.like);
411
+ return await this.groupMathSql(where, groupby, view, sort, method);
412
+ };
413
+
414
+ /**
415
+ * @description 分组求平均值
416
+ * @param {Object} query 查询条件
417
+ * @param {String} groupby 分组的字段
418
+ * @param {String} view 返回的字段
419
+ * @param {String} sort 排序方式
420
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
421
+ */
422
+ Sql.prototype.groupAvg = async function(query, groupby, view, sort) {
423
+ return await this.groupMath(query, groupby, view, sort, "AVG");
424
+ };
425
+
426
+ /**
427
+ * @description 分组合计数值
428
+ * @param {Object} query 查询条件
429
+ * @param {String} groupby 分组的字段
430
+ * @param {String} view 返回的字段
431
+ * @param {String} sort 排序方式
432
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
433
+ */
434
+ Sql.prototype.groupSum = async function(query, groupby, view, sort) {
435
+ return await this.groupMath(query, groupby, view, sort, "SUM");
436
+ };
437
+
438
+ /**
439
+ * @description 分组合计不同条数
440
+ * @param {Object} query 查询条件
441
+ * @param {String} groupby 分组的字段
442
+ * @param {String} view 返回的字段
443
+ * @return {Promise|Object} 查询到的内容列表和符合条件总数
444
+ */
445
+ Sql.prototype.groupCount = async function(query, groupby, view, sort) {
446
+ return await this.groupMath(query, groupby, view, sort, "COUNT");
447
+ };
448
+
284
449
  /* === sql语句拼接函数 === */
285
- ///
286
450
  /**
287
451
  * @description 转为where语句
288
452
  * @param {Object} obj 用作拼接的对象
453
+ * @param {Boolean} like 是否使用like匹配, 默认不使用
289
454
  * @return {String} where格式sql语句字符串
290
455
  */
291
- Sql.prototype.toWhere = function(obj) {
456
+ Sql.prototype.toWhere = function(obj, like) {
292
457
  var where = "";
293
- for (var k in obj) {
294
- where += " and `" + k + "`='" + obj[k] + "'";
458
+ if (like === undefined) {
459
+ like = this.like;
460
+ }
461
+ if (like) {
462
+ for (var k in obj) {
463
+ var val = obj[k];
464
+ if (val && typeof(val) === "string") {
465
+ val = val.trim("'");
466
+ }
467
+ val = escape(val);
468
+ if (k.endWith('_min')) {
469
+ where += " and " + escapeId(k.replace('_min', '')) + " >= " + val;
470
+ } else if (k.endWith('_max')) {
471
+ where += " and " + escapeId(k.replace('_max', '')) + " <= " + val;
472
+ } else if (k.endWith('_not')) {
473
+ where += " and " + escapeId(k.replace('_not', '')) + " != " + val;
474
+ } else if (k.endWith('_has')) {
475
+ var vals = val.trim("'").split(',').map((o) => {
476
+ return "'" + o + "'"
477
+ });
478
+ where += " and " + escapeId(k.replace('_has', '')) + " in (" + vals.join(',') + ")";
479
+ } else if (k.endWith('_like')) {
480
+ where += " and " + escapeId(k.replace('_like', '')) + " LIKE '%" + val.trim("'") + "%'";
481
+ } else if (typeof(val) === "string" && !/^[0-9]+$/.test(val)) {
482
+ where += " and " + escapeId(k) + " LIKE '%" + val.trim("'") + "%'"
483
+ } else {
484
+ where += " and " + escapeId(k) + " = " + val;
485
+ }
486
+ }
487
+ } else {
488
+ for (var k in obj) {
489
+ var val = obj[k];
490
+ if (val && typeof(val) === "string") {
491
+ val = val.trim("'");
492
+ }
493
+ val = escape(val);
494
+ if (k.endWith('_min')) {
495
+ where += " and " + escapeId(k.replace('_min', '')) + " >= " + val;
496
+ } else if (k.endWith('_max')) {
497
+ where += " and " + escapeId(k.replace('_max', '')) + " <= " + val;
498
+ } else if (k.endWith('_not')) {
499
+ where += " and " + escapeId(k.replace('_not', '')) + " != " + val;
500
+ } else if (k.endWith('_has')) {
501
+ var vals = val.trim("'").split(',').map((o) => {
502
+ return "'" + o + "'"
503
+ });
504
+ where += " and " + escapeId(k.replace('_has', '')) + " in (" + vals.join(',') + ")";
505
+ } else {
506
+ where += " and " + escapeId(k) + " = " + val;
507
+ }
508
+ }
295
509
  }
296
510
  return where.replace(" and ", "");
297
511
  };
512
+
298
513
  /**
299
514
  * @description 转为set语句
300
515
  * @param {Object} obj 用作拼接的对象
@@ -303,7 +518,25 @@ Sql.prototype.toWhere = function(obj) {
303
518
  Sql.prototype.toSet = function(obj) {
304
519
  var set = "";
305
520
  for (var k in obj) {
306
- set += ",`" + k + "`='" + obj[k] + "'";
521
+ if (!Object.prototype.hasOwnProperty.call(obj, k)) continue;
522
+
523
+ var val = obj[k];
524
+ if (val === undefined || val === null) continue;
525
+
526
+ if (typeof val === "string") {
527
+ val = val.trim("'");
528
+ }
529
+ val = escape(val);
530
+
531
+ if (k.endWith('_add')) {
532
+ var k2 = escapeId(k.replace('_add', ''));
533
+ set += "," + k2 + " = " + k2 + " + " + val;
534
+ } else if (k.endWith('_del')) {
535
+ var k3 = escapeId(k.replace('_del', ''));
536
+ set += "," + k3 + " = " + k3 + " - " + val;
537
+ } else {
538
+ set += "," + escapeId(k) + " = " + val;
539
+ }
307
540
  }
308
541
  return set.replace(",", "");
309
542
  };
@@ -314,11 +547,21 @@ Sql.prototype.toSet = function(obj) {
314
547
  * @return {String} sql语句
315
548
  */
316
549
  Sql.prototype.toAddSql = function(item) {
550
+ if (!this.table || !item || typeof item !== 'object') {
551
+ throw new Error('表名或数据未设置');
552
+ }
553
+
317
554
  var key = "";
318
555
  var val = "";
319
556
  for (var k in item) {
320
- key += ",`" + k + "`";
321
- val += ",'" + item[k] + "'";
557
+ if (!Object.prototype.hasOwnProperty.call(item, k)) continue;
558
+
559
+ key += "," + escapeId(k);
560
+ var value = item[k];
561
+ if (typeof value === "string") {
562
+ value = value.trim("'");
563
+ }
564
+ val += "," + escape(value);
322
565
  }
323
566
  var sql = "INSERT INTO `{0}` ({1}) VALUES ({2});";
324
567
  return sql.replace("{0}", this.table).replace("{1}", key.replace(",", "")).replace("{2}", val.replace(",", ""));
@@ -327,10 +570,14 @@ Sql.prototype.toAddSql = function(item) {
327
570
  /**
328
571
  * @description 转删除sql语句
329
572
  * @param {Object} query 查询键值
573
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
330
574
  * @return {String} sql语句
331
575
  */
332
- Sql.prototype.toDelSql = function(query) {
333
- var where = this.toWhere(query);
576
+ Sql.prototype.toDelSql = function(query, like) {
577
+ if (!this.table) {
578
+ throw new Error('表名未设置');
579
+ }
580
+ var where = this.toWhere(query, like);
334
581
  var sql = "DELETE FROM `{0}` WHERE {1};";
335
582
  return sql.replace("{0}", this.table).replace("{1}", where);
336
583
  };
@@ -339,10 +586,14 @@ Sql.prototype.toDelSql = function(query) {
339
586
  * @description 转修改sql语句
340
587
  * @param {Object} query 查询的键值集合
341
588
  * @param {Object} item 修改的键值集合
589
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
342
590
  * @return {String} sql语句
343
591
  */
344
- Sql.prototype.toSetSql = function(query, item) {
345
- var where = this.toWhere(query);
592
+ Sql.prototype.toSetSql = function(query, item, like) {
593
+ if (!this.table) {
594
+ throw new Error('表名未设置');
595
+ }
596
+ var where = this.toWhere(query, like);
346
597
  var set = this.toSet(item);
347
598
  var sql = "UPDATE `{0}` SET {1} WHERE {2};";
348
599
  return sql.replace("{0}", this.table).replace("{1}", set).replace("{2}", where);
@@ -353,42 +604,110 @@ Sql.prototype.toSetSql = function(query, item) {
353
604
  * @param {Object} query 查询键值集合
354
605
  * @param {String} sort 排序规则
355
606
  * @param {String} view 显示的字段
607
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
356
608
  * @return {String} sql语句
357
609
  */
358
- Sql.prototype.toGetSql = function(query, sort, view) {
359
- var where = this.toWhere(query);
360
- var sql = this.toQuery(where, sort, view);
361
- return sql;
610
+ Sql.prototype.toGetSql = function(query, sort, view, like) {
611
+ var where = this.toWhere(query, like);
612
+ return this.toQuery(where, sort, view);
362
613
  };
363
614
  /* === 传入对象操作 === */
364
615
  /**
365
616
  * @description 增加数据
366
- * @param {Object} item 添加的对象
617
+ * @param {Object} body 添加的对象
367
618
  * @return {Promise|Object} 执行结果
368
619
  */
369
- Sql.prototype.add = function(item) {
370
- var sql = this.toAddSql(item);
371
- return this.exec(sql);
620
+ Sql.prototype.add = async function(body) {
621
+ if (!this.table || !body || typeof body !== 'object') {
622
+ throw new Error('表名或数据未设置');
623
+ }
624
+ try {
625
+ // 触发前置事件
626
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
627
+ await $.eventer.run("sqlite_add_before:" + this.table, { body });
628
+ }
629
+
630
+ var sql = this.toAddSql(body);
631
+ this.sql = sql;
632
+ var bl = await this.exec(sql);
633
+
634
+ // 触发后置事件
635
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
636
+ await $.eventer.run("sqlite_add_after:" + this.table, { body, sql: this.sql, error: this.error, bl });
637
+ }
638
+
639
+ return bl;
640
+ } catch (err) {
641
+ this.error = err.message;
642
+ $.log.error(`添加数据失败: ${err.message}`);
643
+ throw err;
644
+ }
372
645
  };
373
646
  /**
374
647
  * @description 删除数据
375
648
  * @param {Object} query 查询条件集合
649
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
376
650
  * @return {Promise|Object} 执行结果
377
651
  */
378
- Sql.prototype.del = function(query) {
379
- var sql = this.toDelSql(query);
380
- return this.exec(sql);
652
+ Sql.prototype.del = async function(query, like) {
653
+ if (!this.table) {
654
+ throw new Error('表名未设置');
655
+ }
656
+ try {
657
+ // 触发前置事件
658
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
659
+ await $.eventer.run("sqlite_del_before:" + this.table, { query, like });
660
+ }
661
+
662
+ var sql = this.toDelSql(query, like);
663
+ this.sql = sql;
664
+ var bl = await this.exec(sql);
665
+
666
+ // 触发后置事件
667
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
668
+ await $.eventer.run("sqlite_del_after:" + this.table, { query, like, sql: this.sql, error: this.error, bl });
669
+ }
670
+
671
+ return bl;
672
+ } catch (err) {
673
+ this.error = err.message;
674
+ $.log.error(`删除数据失败: ${err.message}`);
675
+ throw err;
676
+ }
381
677
  };
382
678
 
383
679
  /**
384
680
  * @description 修改数据
385
681
  * @param {Object} query 查询条件集合
386
- * @param {Object} item 修改的键值集合
682
+ * @param {Object} body 修改的键值集合
683
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
387
684
  * @return {Promise|Object} 执行结果
388
685
  */
389
- Sql.prototype.set = function(query, item) {
390
- var sql = this.toSetSql(query, item);
391
- return this.exec(sql);
686
+ Sql.prototype.set = async function(query, body, like) {
687
+ if (!this.table || !body || typeof body !== 'object') {
688
+ throw new Error('表名或数据未设置');
689
+ }
690
+ try {
691
+ // 触发前置事件
692
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
693
+ await $.eventer.run("sqlite_set_before:" + this.table, { query, body, like, page: this.page, size: this.size });
694
+ }
695
+
696
+ var sql = this.toSetSql(query, body, like);
697
+ this.sql = sql;
698
+ var bl = await this.exec(sql);
699
+
700
+ // 触发后置事件
701
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
702
+ await $.eventer.run("sqlite_set_after:" + this.table, { query, body, like, page: this.page, size: this.size, sql: this.sql, error: this.error, bl });
703
+ }
704
+
705
+ return bl;
706
+ } catch (err) {
707
+ this.error = err.message;
708
+ $.log.error(`修改数据失败: ${err.message}`);
709
+ throw err;
710
+ }
392
711
  };
393
712
 
394
713
  /**
@@ -396,82 +715,535 @@ Sql.prototype.set = function(query, item) {
396
715
  * @param {Object} query 查询条件
397
716
  * @param {String} sort 排序
398
717
  * @param {String} view 返回的字段
718
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
719
+ * @param {Number} timeout 超时时间(毫秒),默认30000ms
399
720
  * @return {Promise|Array} 查询结果
400
721
  */
401
- Sql.prototype.get = function(query, sort, view) {
402
- var sql = this.toGetSql(query, sort, view);
403
- return this.run(sql);
722
+ Sql.prototype.get = async function(query, sort, view, like, timeout = 30000) {
723
+ if (!this.table) {
724
+ throw new Error('表名未设置');
725
+ }
726
+
727
+ try {
728
+ // 添加超时控制的Promise
729
+ var timeoutPromise = new Promise((_, reject) => {
730
+ setTimeout(() => reject(new Error('查询操作超时')), timeout);
731
+ });
732
+
733
+ // 使用Promise.race实现超时控制
734
+ return await Promise.race([
735
+ (async () => {
736
+ // 安全触发事件,避免事件系统问题影响核心功能
737
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
738
+ try {
739
+ // 添加超时保护的事件触发
740
+ await Promise.race([
741
+ $.eventer.run("sqlite_get_before:" + this.table, {
742
+ query,
743
+ sort,
744
+ view,
745
+ like,
746
+ page: this.page,
747
+ size: this.size
748
+ }),
749
+ new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
750
+ ]);
751
+ } catch (eventError) {
752
+ $.log.warn(`事件处理失败,但不影响查询: ${eventError.message}`);
753
+ }
754
+ }
755
+
756
+ // 生成SQL并执行
757
+ var sql = this.toGetSql(query, sort, view, like);
758
+ this.sql = sql;
759
+ var list = await this.run(sql);
760
+
761
+ // 安全触发后置事件
762
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
763
+ try {
764
+ // 添加超时保护的事件触发
765
+ await Promise.race([
766
+ $.eventer.run("sqlite_get_after:" + this.table, {
767
+ query,
768
+ sort,
769
+ view,
770
+ like,
771
+ page: this.page,
772
+ size: this.size,
773
+ sql: this.sql,
774
+ error: this.error,
775
+ list
776
+ }),
777
+ new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
778
+ ]);
779
+ } catch (eventError) {
780
+ $.log.warn(`事件处理失败,但不影响查询返回: ${eventError.message}`);
781
+ }
782
+ }
783
+
784
+ return list;
785
+ })(),
786
+ timeoutPromise
787
+ ]);
788
+ } catch (err) {
789
+ this.error = err.message;
790
+ $.log.error(`查询数据失败: ${err.message}`);
791
+ throw err;
792
+ }
404
793
  };
405
794
 
406
795
  /**
407
796
  * @description 添加或修改
408
797
  * @param {Object} where 查询条件集合
409
798
  * @param {Object} set 修改的键值
799
+ * @param {Boolean} like 是否使用like匹配, 默认不使用
410
800
  * @return {Promise|Object} 执行结果
411
801
  */
412
- Sql.prototype.addOrSet = async function(where, set) {
413
- return await this.addOrSetSql(this.toWhere(where), this.toSet(set));
802
+ /**
803
+ * 添加或修改数据(存在则修改,不存在则添加)
804
+ * @param {Object|String} where 查询条件
805
+ * @param {Object|String} set 要设置的数据
806
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
807
+ * @return {Promise<Object>} 执行结果
808
+ */
809
+ Sql.prototype.addOrSet = async function(where, set, like) {
810
+ if (!this.table || !where || !set) {
811
+ throw new Error('表名、条件或数据未设置');
812
+ }
813
+ try {
814
+ let query = where;
815
+ let body = set;
816
+ let whereStr;
817
+
818
+ if (typeof where === "object") {
819
+ whereStr = await this.toWhere(where, like);
820
+ } else {
821
+ whereStr = where;
822
+ }
823
+
824
+ const count = await this.countSql(whereStr);
825
+
826
+ if (count === 0) {
827
+ let key = "";
828
+ let value = "";
829
+
830
+ if (typeof set === "string") {
831
+ const arr = set.split(",");
832
+ for (const o of arr) {
833
+ const ar = o.split('=');
834
+ if (ar.length === 2) {
835
+ key += "," + ar[0];
836
+ value += "," + ar[1];
837
+ }
838
+ }
839
+ } else {
840
+ // 触发前置事件
841
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof body === "object") {
842
+ await $.eventer.run("sqlite_add_before:" + this.table, { body });
843
+ }
844
+
845
+ for (const k in set) {
846
+ if (!Object.prototype.hasOwnProperty.call(set, k)) continue;
847
+
848
+ key += "," + this.escapeId(k);
849
+ let val = set[k];
850
+ if (typeof val === "string") {
851
+ val = val.trim("'");
852
+ }
853
+ value += "," + this.escape(val);
854
+ }
855
+ }
856
+
857
+ const bl = await this.addSql(key.replace(",", ""), value.replace(",", ""));
858
+
859
+ // 触发后置事件
860
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof body === "object") {
861
+ await $.eventer.run("sqlite_add_after:" + this.table, { body, sql: this.sql, error: this.error, bl });
862
+ }
863
+
864
+ return bl;
865
+ } else {
866
+ // 触发前置事件
867
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof set === "object") {
868
+ await $.eventer.run("sqlite_set_before:" + this.table, { query, body, like, page: this.page, size: this.size, sql: this.sql });
869
+ }
870
+
871
+ if (typeof set === "object") {
872
+ set = await this.toSet(set);
873
+ }
874
+
875
+ const bl1 = await this.setSql(whereStr, set);
876
+
877
+ // 触发后置事件
878
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof body === "object") {
879
+ await $.eventer.run("sqlite_set_after:" + this.table, { query, body, like, page: this.page, size: this.size, sql: this.sql, error: this.error, bl: bl1 });
880
+ }
881
+
882
+ return bl1;
883
+ }
884
+ } catch (err) {
885
+ this.error = err.message;
886
+ $.log.error(`添加或修改数据失败: ${err.message}`);
887
+ throw err;
888
+ }
414
889
  };
415
890
 
416
891
  /**
417
- * @description 查询符合结果总数
418
- * @param {Object} query 查询条件集合
419
- * @return {Promise|Number} 查询结果
892
+ * 统计记录数
893
+ * @param {Object} query 查询条件
894
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
895
+ * @param {Number} timeout 超时时间(毫秒),默认30000ms
896
+ * @return {Promise|Number} 记录数
420
897
  */
421
- Sql.prototype.count = function(query) {
422
- return this.countSql(this.toWhere(query));
898
+ Sql.prototype.count = async function(query, like, timeout = 30000) {
899
+ if (!this.table) {
900
+ throw new Error('表名未设置');
901
+ }
902
+
903
+ try {
904
+ // 添加超时控制
905
+ const timeoutPromise = new Promise((_, reject) => {
906
+ setTimeout(() => reject(new Error('统计操作超时')), timeout);
907
+ });
908
+
909
+ return await Promise.race([
910
+ (async () => {
911
+ // 安全触发事件
912
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
913
+ try {
914
+ await Promise.race([
915
+ $.eventer.run("sqlite_count_before:" + this.table, { query, like }),
916
+ new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
917
+ ]);
918
+ } catch (eventError) {
919
+ $.log.warn(`事件处理失败,但不影响统计: ${eventError.message}`);
920
+ }
921
+ }
922
+
923
+ // 正确生成count SQL
924
+ const where = typeof query === 'string' ? query : await this.toWhere(query, like);
925
+ const sql = "SELECT COUNT(*) as num FROM `" + this.table + "`" + (where ? " WHERE " + where : "");
926
+ this.sql = sql;
927
+ const list = await this.run(sql);
928
+ const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
929
+
930
+ // 安全触发事件
931
+ if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
932
+ try {
933
+ await Promise.race([
934
+ $.eventer.run("sqlite_count_after:" + this.table, {
935
+ query,
936
+ like,
937
+ sql: this.sql,
938
+ error: this.error,
939
+ total
940
+ }),
941
+ new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
942
+ ]);
943
+ } catch (eventError) {
944
+ $.log.warn(`事件处理失败,但不影响统计返回: ${eventError.message}`);
945
+ }
946
+ }
947
+
948
+ return total;
949
+ })(),
950
+ timeoutPromise
951
+ ]);
952
+ } catch (err) {
953
+ this.error = err.message;
954
+ $.log.error(`统计记录数失败: ${err.message}`, { sql: this.sql });
955
+ throw err;
956
+ }
423
957
  };
424
958
 
425
959
  /**
426
- * @description 查询数据并返回符合条件总数
960
+ * 查询数据并返回符合条件总数
427
961
  * @param {Object} query 查询条件
428
- * @param {String} sort 排序
429
- * @param {String} view 返回的字段
430
- * @return {Promise|Object} 查询到的内容列表和符合条件总数
962
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
963
+ * @param {Number} timeout 超时时间(毫秒),默认30000ms
964
+ * @return {Promise<Number>} 记录数
431
965
  */
432
- Sql.prototype.getCount = async function(query, sort, view) {
433
- return this.getCountSql(this.toWhere(query), sort, view);
966
+ Sql.prototype.getCount = async function(query, like, timeout = 30000) {
967
+ return await this.count(query, like, timeout);
434
968
  };
435
969
 
436
970
  /* === 传入数组操作 === */
437
971
  /**
438
972
  * @description 添加多条数据
439
973
  * @param {Array} list 对象数组
974
+ * @param {Boolean} lock 是否锁定
975
+ * @param {Number} batchSize 批量大小,默认100
976
+ * @param {Number} timeout 超时时间,默认60000毫秒
440
977
  * @return {Promise|Object} 执行结果
441
978
  */
442
- Sql.prototype.addList = function(list) {
443
- var sql = "START TRANSACTION;\n";
444
- var len = list.length;
445
- for (var i = 0; i < len; i++) {
446
- sql += this.toAddSql(list[i]);
979
+ Sql.prototype.addList = async function(list, lock = true, batchSize = 100, timeout = 60000) {
980
+ if (!this.table || !Array.isArray(list) || list.length === 0) {
981
+ throw new Error('表名或数据列表未设置');
982
+ }
983
+ try {
984
+ // 添加整体操作超时控制
985
+ const timeoutPromise = new Promise((_, reject) => {
986
+ setTimeout(() => reject(new Error('批量添加数据操作超时')), timeout);
987
+ });
988
+
989
+ return await Promise.race([
990
+ (async () => {
991
+ // 如果数据量较小,直接处理
992
+ if (list.length <= batchSize) {
993
+ // 使用批量插入语法,不使用事务包装
994
+ this.sql = this.toBatchAddSql(list);
995
+ return await this.exec(this.sql);
996
+ }
997
+
998
+ // 分批处理大数据量
999
+ const totalBatches = Math.ceil(list.length / batchSize);
1000
+ $.log.info(`开始分批添加数据,共${totalBatches}批,每批${batchSize}条`);
1001
+
1002
+ // 分批执行,每批一个单独的批量插入语句
1003
+ let finalResult = null;
1004
+ for (let i = 0; i < totalBatches; i++) {
1005
+ const batch = list.slice(i * batchSize, (i + 1) * batchSize);
1006
+ $.log.debug(`处理第${i + 1}/${totalBatches}批数据,${batch.length}条`);
1007
+
1008
+ // 生成批量插入SQL
1009
+ const batchSql = this.toBatchAddSql(batch);
1010
+ this.sql = batchSql;
1011
+
1012
+ // 为每批操作添加超时控制
1013
+ finalResult = await Promise.race([
1014
+ this.exec(batchSql),
1015
+ new Promise((_, reject) => {
1016
+ setTimeout(() => reject(new Error(`第${i + 1}批数据处理超时`)), timeout / totalBatches);
1017
+ })
1018
+ ]);
1019
+ }
1020
+
1021
+ $.log.success(`批量添加数据完成,共${list.length}条`);
1022
+ return finalResult;
1023
+ })(),
1024
+ timeoutPromise
1025
+ ]);
1026
+ } catch (error) {
1027
+ $.log.error(`[${this.constructor.name}] [addList]`, '批量添加数据失败', {
1028
+ error: error.message,
1029
+ table: this.table,
1030
+ list_length: list.length
1031
+ });
1032
+ throw error;
447
1033
  }
448
- return this.exec(sql);
449
1034
  };
1035
+
450
1036
  /**
451
- * @description 删除多条数据
1037
+ * 生成批量插入SQL语句
1038
+ * @param {Array} list 数据列表
1039
+ * @return {String} 批量插入SQL语句
1040
+ */
1041
+ Sql.prototype.toBatchAddSql = function(list) {
1042
+ if (!this.table || !Array.isArray(list) || list.length === 0) {
1043
+ throw new Error('表名或数据列表未设置');
1044
+ }
1045
+
1046
+ // 获取第一个对象的键名作为列名
1047
+ const firstItem = list[0];
1048
+ if (!firstItem || typeof firstItem !== 'object') {
1049
+ throw new Error('数据格式错误');
1050
+ }
1051
+
1052
+ let keys = [];
1053
+ for (const k in firstItem) {
1054
+ if (Object.prototype.hasOwnProperty.call(firstItem, k)) {
1055
+ keys.push(this.escapeId(k));
1056
+ }
1057
+ }
1058
+
1059
+ // 构建列名部分
1060
+ const columns = keys.join(',');
1061
+
1062
+ // 构建值部分
1063
+ const valuesList = [];
1064
+ for (const item of list) {
1065
+ const values = [];
1066
+ for (const k of keys) {
1067
+ const unescapedKey = k.replace(/`/g, '');
1068
+ let val = item[unescapedKey];
1069
+ if (typeof val === "string") {
1070
+ val = val.trim("'");
1071
+ }
1072
+ values.push(this.escape(val));
1073
+ }
1074
+ valuesList.push(`(${values.join(',')})`);
1075
+ }
1076
+
1077
+ // 生成最终SQL
1078
+ const sql = `INSERT INTO \`${this.table}\` (${columns}) VALUES ${valuesList.join(',')}`;
1079
+ return sql;
1080
+ };
1081
+
1082
+ /**
1083
+ * 删除多条数据
452
1084
  * @param {Array} list 对象数组
453
- * @return {Promise|Object} 执行结果
1085
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
1086
+ * @param {Number} batchSize 每批处理数量,默认100
1087
+ * @param {Number} timeout 超时时间(毫秒),默认60000
1088
+ * @return {Promise<Object>} 执行结果
454
1089
  */
455
- Sql.prototype.delList = function(list) {
456
- var sql = "";
457
- var len = list.length;
458
- for (var i = 0; i < len; i++) {
459
- sql += this.toDelSql(list[i].query);
1090
+ Sql.prototype.delList = async function(list, like, batchSize = 100, timeout = 60000) {
1091
+ if (!this.table || !Array.isArray(list) || list.length === 0) {
1092
+ throw new Error('表名或数据列表未设置');
1093
+ }
1094
+ try {
1095
+ // 添加整体操作超时控制
1096
+ const timeoutPromise = new Promise((_, reject) => {
1097
+ setTimeout(() => reject(new Error('批量删除数据操作超时')), timeout);
1098
+ });
1099
+
1100
+ return await Promise.race([
1101
+ (async () => {
1102
+ // 如果数据量较小,直接处理
1103
+ if (list.length <= batchSize) {
1104
+ let sql = "";
1105
+ for (const item of list) {
1106
+ sql += this.toDelSql(item.query, like);
1107
+ }
1108
+ this.sql = sql;
1109
+ return await this.exec(sql);
1110
+ }
1111
+
1112
+ // 分批处理大数据量
1113
+ const totalBatches = Math.ceil(list.length / batchSize);
1114
+ $.log.info(`开始分批删除数据,共${totalBatches}批,每批${batchSize}条`);
1115
+
1116
+ // 使用事务包装整个操作以保证原子性
1117
+ let finalResult = null;
1118
+ try {
1119
+ // 开始事务
1120
+ await this.exec("BEGIN;", 15000);
1121
+
1122
+ // 分批处理
1123
+ for (let i = 0; i < totalBatches; i++) {
1124
+ const batch = list.slice(i * batchSize, (i + 1) * batchSize);
1125
+ $.log.debug(`处理第${i + 1}/${totalBatches}批数据,${batch.length}条`);
1126
+
1127
+ let batchSql = "";
1128
+ for (const item of batch) {
1129
+ batchSql += this.toDelSql(item.query, like);
1130
+ }
1131
+
1132
+ // 为每批操作添加超时控制
1133
+ finalResult = await Promise.race([
1134
+ this.exec(batchSql),
1135
+ new Promise((_, reject) => {
1136
+ setTimeout(() => reject(new Error(`第${i + 1}批数据删除超时`)), timeout / totalBatches);
1137
+ })
1138
+ ]);
1139
+ }
1140
+
1141
+ // 提交事务
1142
+ await this.exec("COMMIT;", 15000);
1143
+ } catch (error) {
1144
+ // 发生错误时回滚事务
1145
+ try {
1146
+ await this.exec("ROLLBACK;", 15000);
1147
+ $.log.error(`批量删除数据失败,事务已回滚: ${error.message}`);
1148
+ } catch (rollbackError) {
1149
+ $.log.error(`批量删除数据失败,事务回滚也失败: ${rollbackError.message}`);
1150
+ }
1151
+ throw error;
1152
+ }
1153
+
1154
+ $.log.success(`批量删除数据完成,共${list.length}条`);
1155
+ return finalResult;
1156
+ })(),
1157
+ timeoutPromise
1158
+ ]);
1159
+ } catch (err) {
1160
+ this.error = err.message;
1161
+ $.log.error(`批量删除数据失败: ${err.message}`);
1162
+ throw err;
460
1163
  }
461
- return this.exec(sql);
462
1164
  };
463
1165
  /**
464
- * @description 修改多条数据
1166
+ * 更新多条数据
465
1167
  * @param {Array} list 对象数组
466
- * @return {Promise|Object} 执行结果
1168
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
1169
+ * @param {Number} batchSize 每批处理数量,默认100
1170
+ * @param {Number} timeout 超时时间(毫秒),默认60000
1171
+ * @return {Promise<Object>} 执行结果
467
1172
  */
468
- Sql.prototype.setList = function(list) {
469
- var sql = "";
470
- var len = list.length;
471
- for (var i = 0; i < len; i++) {
472
- sql += this.toSetSql(list[i].query, list[i].item);
1173
+ Sql.prototype.setList = async function(list, like = false, batchSize = 100, timeout = 60000) {
1174
+ if (!this.table || !Array.isArray(list) || list.length === 0) {
1175
+ throw new Error('表名或数据列表未设置');
1176
+ }
1177
+ try {
1178
+ // 添加整体操作超时控制
1179
+ const timeoutPromise = new Promise((_, reject) => {
1180
+ setTimeout(() => reject(new Error('批量更新数据操作超时')), timeout);
1181
+ });
1182
+
1183
+ return await Promise.race([
1184
+ (async () => {
1185
+ // 如果数据量较小,直接处理
1186
+ if (list.length <= batchSize) {
1187
+ let sql = "";
1188
+ for (const item of list) {
1189
+ sql += this.toSetSql(item.query, item.obj, like);
1190
+ }
1191
+ this.sql = sql;
1192
+ return await this.exec(sql);
1193
+ }
1194
+
1195
+ // 分批处理大数据量
1196
+ const totalBatches = Math.ceil(list.length / batchSize);
1197
+ $.log.info(`开始分批更新数据,共${totalBatches}批,每批${batchSize}条`);
1198
+
1199
+ // 使用事务包装整个操作以保证原子性
1200
+ let finalResult = null;
1201
+ try {
1202
+ // 开始事务
1203
+ await this.exec("BEGIN;", 15000);
1204
+
1205
+ // 分批处理
1206
+ for (let i = 0; i < totalBatches; i++) {
1207
+ const batch = list.slice(i * batchSize, (i + 1) * batchSize);
1208
+ $.log.debug(`处理第${i + 1}/${totalBatches}批数据,${batch.length}条`);
1209
+
1210
+ let batchSql = "";
1211
+ for (const item of batch) {
1212
+ batchSql += this.toSetSql(item.query, item.obj, like);
1213
+ }
1214
+
1215
+ // 为每批操作添加超时控制
1216
+ finalResult = await Promise.race([
1217
+ this.exec(batchSql),
1218
+ new Promise((_, reject) => {
1219
+ setTimeout(() => reject(new Error(`第${i + 1}批数据更新超时`)), timeout / totalBatches);
1220
+ })
1221
+ ]);
1222
+ }
1223
+
1224
+ // 提交事务
1225
+ await this.exec("COMMIT;", 15000);
1226
+ } catch (error) {
1227
+ // 发生错误时回滚事务
1228
+ try {
1229
+ await this.exec("ROLLBACK;", 15000);
1230
+ $.log.error(`批量更新数据失败,事务已回滚: ${error.message}`);
1231
+ } catch (rollbackError) {
1232
+ $.log.error(`批量更新数据失败,事务回滚也失败: ${rollbackError.message}`);
1233
+ }
1234
+ throw error;
1235
+ }
1236
+
1237
+ $.log.success(`批量更新数据完成,共${list.length}条`);
1238
+ return finalResult;
1239
+ })(),
1240
+ timeoutPromise
1241
+ ]);
1242
+ } catch (err) {
1243
+ this.error = err.message;
1244
+ $.log.error(`批量更新数据失败: ${err.message}`);
1245
+ throw err;
473
1246
  }
474
- return this.exec(sql);
475
1247
  };
476
1248
 
477
1249
  /* 辅助类 */
@@ -481,10 +1253,11 @@ Sql.prototype.setList = function(list) {
481
1253
  * @param {Object} sqlDt sql模板集合
482
1254
  * @return {Bool} 有则返回true,没有则返回false
483
1255
  */
484
- Sql.prototype.has_param = function(paramDt, sqlDt) {
1256
+ Sql.prototype.hasParam = function(paramDt, sqlDt) {
485
1257
  var bl = false;
486
1258
  for (var key in sqlDt) {
487
- if (paramDt[key] !== undefined && paramDt[key] !== null && paramDt[key] !== '') {
1259
+ var value = paramDt[key];
1260
+ if (value !== undefined && value !== null && value !== '') {
488
1261
  bl = true;
489
1262
  break;
490
1263
  }
@@ -498,7 +1271,7 @@ Sql.prototype.has_param = function(paramDt, sqlDt) {
498
1271
  * @param {Object} sqlDt sql模板集合
499
1272
  * @return {Bool} 没有模板则返回名称,都有则返回undefined
500
1273
  */
501
- Sql.prototype.not_param = function(paramDt, sqlDt) {
1274
+ Sql.prototype.notParam = function(paramDt, sqlDt) {
502
1275
  var name;
503
1276
  for (var key in paramDt) {
504
1277
  if (!sqlDt[key]) {
@@ -515,7 +1288,7 @@ Sql.prototype.not_param = function(paramDt, sqlDt) {
515
1288
  * @param {Object} sqlDt sql模板集合
516
1289
  * @return {Object} 返回过滤后的参数集合
517
1290
  */
518
- Sql.prototype.filter_param = function(paramDt, sqlDt) {
1291
+ Sql.prototype.filterParam = function(paramDt, sqlDt) {
519
1292
  var dt = [];
520
1293
  for (var key in paramDt) {
521
1294
  if (!sqlDt[key]) {
@@ -531,9 +1304,9 @@ Sql.prototype.filter_param = function(paramDt, sqlDt) {
531
1304
  * @param {Object} sqlDt 模板集合
532
1305
  * @return {String} 返回拼接的查询参数
533
1306
  */
534
- Sql.prototype.tpl_query = function(paramDt, sqlDt) {
1307
+ Sql.prototype.tplQuery = function(paramDt, sqlDt) {
535
1308
  var sql = "";
536
- if(sqlDt){
1309
+ if (sqlDt) {
537
1310
  var l = this.config.separator;
538
1311
  if (l) {
539
1312
  for (var key in paramDt) {
@@ -546,12 +1319,12 @@ Sql.prototype.tpl_query = function(paramDt, sqlDt) {
546
1319
  var sl = "(";
547
1320
  var len = arr.length;
548
1321
  for (var i = 0; i < len; i++) {
549
- sl += " || " + tpl.replaceAll("{0}", arr[i]);
1322
+ sl += " || " + tpl.replaceAll("{0}", this.escape(arr[i]).trim("'"));
550
1323
  }
551
1324
  sl = sl.replace(" || ", "") + ")";
552
1325
  sql += " && " + sl;
553
1326
  } else {
554
- sql += " && " + tpl.replaceAll("{0}", value);
1327
+ sql += " && " + tpl.replaceAll("{0}", this.escape(value).trim("'"));
555
1328
  }
556
1329
  } else {
557
1330
  if (arr.length > 1) {
@@ -559,49 +1332,50 @@ Sql.prototype.tpl_query = function(paramDt, sqlDt) {
559
1332
  var sl = "(";
560
1333
  var len = arr.length;
561
1334
  for (var i = 0; i < len; i++) {
562
- sl += " || `" + key + "` = '" + arr[i] + "'";
1335
+ sl += " || " + this.escapeId(key) + " = " + this.escape(arr[i]);
563
1336
  }
564
1337
  sl = sl.replace(" || ", "") + ")";
565
1338
  sql += " && " + sl;
566
1339
  } else {
567
- sql += " && `" + key + "` = '" + value + "'";
1340
+ sql += " && " + this.escapeId(key) + " = " + this.escape(value);
568
1341
  }
569
1342
  }
570
1343
  }
571
1344
  } else {
572
1345
  for (var key in paramDt) {
1346
+ var value = this.escape(paramDt[key]);
573
1347
  if (sqlDt[key]) {
574
- sql += " && " + sqlDt[key].replaceAll("{0}", paramDt[key]);
1348
+ sql += " && " + sqlDt[key].replaceAll("{0}", value.trim("'"));
575
1349
  } else {
576
- sql += " && `" + key + "` = '" + paramDt[key] + "'";
1350
+ sql += " && " + this.escapeId(key) + " = " + value;
577
1351
  }
578
1352
  }
579
1353
  }
580
- }
581
- else {
1354
+ } else {
582
1355
  // 如果没有模板,则直接拼接参数
583
1356
  var l = this.config.separator;
584
1357
  if (l) {
585
1358
  // 使用分隔数组拼接
586
1359
  for (var key in paramDt) {
587
- var arr = paramDt[key].split(l);
1360
+ var value = paramDt[key];
1361
+ var arr = value.split(l);
588
1362
  if (arr.length > 1) {
589
1363
  // 如果数量大于0,则增加多条件
590
1364
  var sl = "(";
591
1365
  var len = arr.length;
592
1366
  for (var i = 0; i < len; i++) {
593
- sl += " || `" + key + "` = '" + arr[i] + "'";
1367
+ sl += " || " + this.escapeId(key) + " = " + this.escape(arr[i]);
594
1368
  }
595
1369
  sl = sl.replace(" || ", "") + ")";
596
1370
  sql += " && " + sl;
597
1371
  } else {
598
- sql += " && `" + key + "` = '" + paramDt[key] + "'";
1372
+ sql += " && " + this.escapeId(key) + " = " + this.escape(value);
599
1373
  }
600
1374
  }
601
1375
  } else {
602
1376
  // 直接拼接
603
1377
  for (var key in paramDt) {
604
- sql += " && `" + key + "` = '" + paramDt[key] + "'";
1378
+ sql += " && " + this.escapeId(key) + " = " + this.escape(paramDt[key]);
605
1379
  }
606
1380
  }
607
1381
  }
@@ -614,18 +1388,19 @@ Sql.prototype.tpl_query = function(paramDt, sqlDt) {
614
1388
  * @param {Object} sqlDt 模板集合
615
1389
  * @return {String} 返回拼接的查询参数
616
1390
  */
617
- Sql.prototype.tpl_body = function(paramDt, sqlDt) {
1391
+ Sql.prototype.tplBody = function(paramDt, sqlDt) {
618
1392
  var sql = "";
619
1393
  if (!sqlDt || sqlDt.length === 0) {
620
1394
  for (var key in paramDt) {
621
- sql += " , `" + key + "` = '" + val[key];
1395
+ sql += " , " + this.escapeId(key) + " = " + this.escape(val[key]);
622
1396
  }
623
1397
  } else {
624
1398
  for (var key in paramDt) {
1399
+ var value = this.escape(paramDt[key]);
625
1400
  if (sqlDt[key]) {
626
- sql += " , " + sqlDt[key].replace("{0}", paramDt[key]).replace('+ -', '- ').replace('- -', '+ ');
1401
+ sql += " , " + sqlDt[key].replace("{0}", value).replace('+ -', '- ').replace('- -', '+ ');
627
1402
  } else {
628
- sql += " , `" + key + "` = '" + paramDt[key] + "'";
1403
+ sql += " , " + this.escapeId(key) + " = " + value;
629
1404
  }
630
1405
  }
631
1406
  }
@@ -645,11 +1420,14 @@ Sql.prototype.model = function(model) {
645
1420
  var n = obj[prop];
646
1421
  var cha = value - n;
647
1422
  if (cha > 0) {
648
- _this.setSql("`" + _this.key + "`=" + obj[_this.key] + "", "`" + prop + "`=`" + prop + "` + " + cha);
1423
+ _this.setSql("`" + _this.key + "`=" + obj[_this.key], "`" + prop + "`=`" +
1424
+ prop + "` + " + cha);
649
1425
  } else if (cha < 0) {
650
- _this.setSql("`" + _this.key + "`=" + obj[_this.key] + "", "`" + prop + "`=`" + prop + "` - " + (-cha));
1426
+ _this.setSql("`" + _this.key + "`=" + obj[_this.key], "`" + prop + "`=`" +
1427
+ prop + "` - " + (-cha));
651
1428
  } else {
652
- _this.setSql("`" + _this.key + "`=" + obj[_this.key] + "", "`" + prop + "`=" + value);
1429
+ _this.setSql("`" + _this.key + "`=" + obj[_this.key], "`" + prop + "`=" +
1430
+ value);
653
1431
  }
654
1432
  } else {
655
1433
  var query = {};
@@ -665,32 +1443,207 @@ Sql.prototype.model = function(model) {
665
1443
  };
666
1444
 
667
1445
  /**
668
- * @description 查询一条数据
1446
+ * 查询单条数据
669
1447
  * @param {Object} query 查询条件
670
1448
  * @param {String} sort 排序
671
1449
  * @param {String} view 返回的字段
672
- * @return {Promise|Array} 查询结果
1450
+ * @param {Boolean} like 是否使用like匹配, 为空使用默认方式
1451
+ * @param {Number} timeout 超时时间(毫秒),默认30000ms
1452
+ * @return {Promise|Object|null} 查询结果
673
1453
  */
674
- Sql.prototype.getObj = async function(query, sort, view) {
675
- this.page = 1;
676
- this.size = 1;
677
- if (this.key) {
678
- if (view && view.indexOf(this.key) === -1 && view.indexOf('*') === -1) {
679
- view += ",`" + this.key + "`";
680
- }
681
- }
682
- var sql = this.toGetSql(query, sort, view);
683
- var list = await this.run(sql);
684
- if (list.length > 0) {
685
- var obj = list[0];
686
- if (this.key) {
687
- return this.model(obj);
688
- } else {
689
- return obj;
1454
+ Sql.prototype.getObj = async function(query, sort, view, like, timeout = 30000) {
1455
+ try {
1456
+ // 保存当前分页设置
1457
+ const oldPage = this.page;
1458
+ const oldSize = this.size;
1459
+
1460
+ // 设置为只查询一条
1461
+ this.page = 1;
1462
+ this.size = 1;
1463
+
1464
+ // 使用优化后的get方法
1465
+ const list = await this.get(query, sort, view, like, timeout);
1466
+
1467
+ // 恢复分页设置
1468
+ this.page = oldPage;
1469
+ this.size = oldSize;
1470
+
1471
+ var obj = null;
1472
+ if (list.length > 0) {
1473
+ obj = list[0];
1474
+ if (this.key) {
1475
+ obj = this.model(obj);
1476
+ }
690
1477
  }
691
- } else {
692
- return null;
1478
+
1479
+ return obj;
1480
+ } catch (err) {
1481
+ this.error = err.message;
1482
+ $.log.error(`查询单条数据失败: ${err.message}`, { sql: this.sql });
1483
+ throw err;
693
1484
  }
694
1485
  };
695
1486
 
696
- module.exports = Sql;
1487
+
1488
+
1489
+ /**
1490
+ * 从SQL文件加载数据库
1491
+ * @param {String} file - SQL文件路径
1492
+ * @returns {Promise<boolean>} 是否加载成功
1493
+ */
1494
+ Sql.prototype.load = async function (file) {
1495
+ try {
1496
+ // 记录操作日志
1497
+ if (this.config && this.config.debug) {
1498
+ $.log.debug(`[${this.constructor.name}] [load] 开始从文件加载数据库`, {
1499
+ file: file
1500
+ });
1501
+ }
1502
+
1503
+ // 检查文件是否存在
1504
+ if (!file.hasFile()) {
1505
+ throw new Error(`SQL文件不存在: ${file}`);
1506
+ }
1507
+
1508
+ // 读取SQL文件内容
1509
+ const sqlContent = await $.file.readText(file);
1510
+
1511
+ if (!sqlContent || sqlContent.trim() === '') {
1512
+ throw new Error(`SQL文件内容为空: ${file}`);
1513
+ }
1514
+
1515
+ // 分割SQL语句(按分号分割,但需要注意处理字符串中的分号)
1516
+ const sqlStatements = this._splitSqlStatements(sqlContent);
1517
+
1518
+ // 开始事务执行SQL语句
1519
+ await this.exec('BEGIN TRANSACTION');
1520
+
1521
+ try {
1522
+ // 逐个执行SQL语句
1523
+ for (const sql of sqlStatements) {
1524
+ const trimmedSql = sql.trim();
1525
+ if (trimmedSql) {
1526
+ await this.exec(trimmedSql);
1527
+ }
1528
+ }
1529
+
1530
+ // 提交事务
1531
+ await this.exec('COMMIT');
1532
+
1533
+ if (this.config && this.config.debug) {
1534
+ $.log.info(`[${this.constructor.name}] [load] 数据库加载成功`, {
1535
+ file: file,
1536
+ statementCount: sqlStatements.length
1537
+ });
1538
+ }
1539
+
1540
+ return true;
1541
+ } catch (err) {
1542
+ // 回滚事务
1543
+ await this.exec('ROLLBACK').catch(rollbackErr => {
1544
+ $.log.error(`[${this.constructor.name}] [load] 事务回滚失败`, {
1545
+ error: rollbackErr.message
1546
+ });
1547
+ });
1548
+
1549
+ throw err;
1550
+ }
1551
+ } catch (error) {
1552
+ // 记录错误日志
1553
+ $.log.error(`[${this.constructor.name}] [load] 数据库加载失败`, {
1554
+ error: error.message,
1555
+ file: file
1556
+ });
1557
+
1558
+ // 抛出错误
1559
+ throw error;
1560
+ }
1561
+ };
1562
+
1563
+ /**
1564
+ * 分割SQL语句
1565
+ * @private
1566
+ * @param {String} sqlContent - SQL内容
1567
+ * @returns {Array} SQL语句数组
1568
+ */
1569
+ Sql.prototype._splitSqlStatements = function(sqlContent) {
1570
+ const statements = [];
1571
+ let inString = false;
1572
+ let stringChar = '';
1573
+ let inComment = false;
1574
+ let currentStatement = '';
1575
+
1576
+ for (let i = 0; i < sqlContent.length; i++) {
1577
+ const char = sqlContent[i];
1578
+ const nextChar = i + 1 < sqlContent.length ? sqlContent[i + 1] : '';
1579
+
1580
+ // 处理注释
1581
+ if (!inString && !inComment && char === '-' && nextChar === '-') {
1582
+ inComment = true;
1583
+ i++; // 跳过第二个'-'
1584
+ continue;
1585
+ }
1586
+
1587
+ if (inComment && char === '\n') {
1588
+ inComment = false;
1589
+ continue;
1590
+ }
1591
+
1592
+ if (inComment) {
1593
+ continue;
1594
+ }
1595
+
1596
+ // 处理多行注释
1597
+ if (!inString && !inComment && char === '/' && nextChar === '*') {
1598
+ inComment = true;
1599
+ i++; // 跳过'*'
1600
+ continue;
1601
+ }
1602
+
1603
+ if (inComment && char === '*' && nextChar === '/') {
1604
+ inComment = false;
1605
+ i++; // 跳过'/'
1606
+ continue;
1607
+ }
1608
+
1609
+ // 处理字符串
1610
+ if (!inComment && (char === "'" || char === '"') && (!inString || stringChar === char)) {
1611
+ // 检查是否是转义的引号
1612
+ let escaped = false;
1613
+ for (let j = i - 1; j >= 0; j--) {
1614
+ if (sqlContent[j] === '\\') {
1615
+ escaped = !escaped;
1616
+ } else {
1617
+ break;
1618
+ }
1619
+ }
1620
+
1621
+ if (!escaped) {
1622
+ if (inString && stringChar === char) {
1623
+ inString = false;
1624
+ stringChar = '';
1625
+ } else if (!inString) {
1626
+ inString = true;
1627
+ stringChar = char;
1628
+ }
1629
+ }
1630
+ }
1631
+
1632
+ // 分割语句
1633
+ if (!inString && !inComment && char === ';') {
1634
+ statements.push(currentStatement.trim());
1635
+ currentStatement = '';
1636
+ } else {
1637
+ currentStatement += char;
1638
+ }
1639
+ }
1640
+
1641
+ // 添加最后一个语句(如果有)
1642
+ if (currentStatement.trim()) {
1643
+ statements.push(currentStatement.trim());
1644
+ }
1645
+
1646
+ return statements;
1647
+ };
1648
+
1649
+ module.exports = Sql;