baja-lite 1.8.7 → 1.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/package.json +3 -6
  2. package/sql.d.ts +22 -7
  3. package/sql.js +423 -391
package/sql.js CHANGED
@@ -238,20 +238,20 @@ class MysqlConnection {
238
238
  if (globalThis[_GlobalSqlOption].log === 'trace') {
239
239
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
240
240
  }
241
- return new Promise(async (resolve, reject) => {
241
+ return (async () => {
242
242
  try {
243
243
  const [_result] = await this[_daoConnection].execute(sql, params);
244
244
  const result = _result;
245
245
  if (globalThis[_GlobalSqlOption].log === 'trace') {
246
246
  globalThis[_LoggerService].verbose?.(result);
247
247
  }
248
- resolve({ affectedRows: result.affectedRows, insertId: result.insertId });
248
+ return { affectedRows: result.affectedRows, insertId: result.insertId };
249
249
  }
250
250
  catch (error) {
251
251
  globalThis[_LoggerService].error(error.message, { cause: error });
252
- reject(error);
252
+ throw error;
253
253
  }
254
- });
254
+ })();
255
255
  }
256
256
  pluck(sync, sql, params) {
257
257
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -267,16 +267,14 @@ class MysqlConnection {
267
267
  if (globalThis[_GlobalSqlOption].log === 'trace') {
268
268
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
269
269
  }
270
- return new Promise(async (resolve, reject) => {
270
+ return (async () => {
271
271
  try {
272
272
  const [result] = await this[_daoConnection].query(sql, params);
273
273
  if (result && result[0]) {
274
274
  const r = Object.values(result[0])[0];
275
- resolve(r === null ? null : r);
276
- }
277
- else {
278
- resolve(null);
275
+ return r === null ? null : r;
279
276
  }
277
+ return null;
280
278
  }
281
279
  catch (error) {
282
280
  globalThis[_LoggerService].error(`
@@ -284,9 +282,9 @@ class MysqlConnection {
284
282
  sql: ${sql},
285
283
  params: ${params}
286
284
  `);
287
- reject(error);
285
+ throw error;
288
286
  }
289
- });
287
+ })();
290
288
  }
291
289
  get(sync, sql, params) {
292
290
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -302,15 +300,16 @@ class MysqlConnection {
302
300
  if (globalThis[_GlobalSqlOption].log === 'trace') {
303
301
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
304
302
  }
305
- return new Promise(async (resolve, reject) => {
303
+ return (async () => {
306
304
  try {
307
305
  const [result] = await this[_daoConnection].query(sql, params);
308
306
  if (globalThis[_GlobalSqlOption].log === 'trace') {
309
307
  globalThis[_LoggerService].verbose?.(result);
310
308
  }
311
- if (result && result[0])
312
- resolve(result[0]);
313
- resolve(null);
309
+ if (result && result[0]) {
310
+ return result[0];
311
+ }
312
+ return null;
314
313
  }
315
314
  catch (error) {
316
315
  globalThis[_LoggerService].error(`
@@ -318,9 +317,9 @@ class MysqlConnection {
318
317
  sql: ${sql},
319
318
  params: ${params}
320
319
  `);
321
- reject(error);
320
+ throw error;
322
321
  }
323
- });
322
+ })();
324
323
  }
325
324
  raw(sync, sql, params) {
326
325
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -336,15 +335,18 @@ class MysqlConnection {
336
335
  if (globalThis[_GlobalSqlOption].log === 'trace') {
337
336
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
338
337
  }
339
- return new Promise(async (resolve, reject) => {
338
+ return (async () => {
340
339
  try {
341
340
  const [result] = await this[_daoConnection].query(sql, params);
342
341
  if (globalThis[_GlobalSqlOption].log === 'trace') {
343
342
  globalThis[_LoggerService].verbose?.(result);
344
343
  }
345
- if (result)
346
- resolve(result.map((i) => Object.values(i)[0]));
347
- resolve([]);
344
+ // 修复 fall-through:原代码 `if (result) resolve(...); resolve([])` 第二行
345
+ // no-op,但写法埋雷。
346
+ if (result) {
347
+ return result.map((i) => Object.values(i)[0]);
348
+ }
349
+ return [];
348
350
  }
349
351
  catch (error) {
350
352
  globalThis[_LoggerService].error(`
@@ -352,9 +354,9 @@ class MysqlConnection {
352
354
  sql: ${sql},
353
355
  params: ${params}
354
356
  `);
355
- reject(error);
357
+ throw error;
356
358
  }
357
- });
359
+ })();
358
360
  }
359
361
  query(sync, sql, params) {
360
362
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -370,13 +372,13 @@ class MysqlConnection {
370
372
  if (globalThis[_GlobalSqlOption].log === 'trace') {
371
373
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
372
374
  }
373
- return new Promise(async (resolve, reject) => {
375
+ return (async () => {
374
376
  try {
375
377
  const [result] = await this[_daoConnection].query(sql, params);
376
378
  if (globalThis[_GlobalSqlOption].log === 'trace') {
377
379
  globalThis[_LoggerService].verbose?.(result);
378
380
  }
379
- resolve(result);
381
+ return result;
380
382
  }
381
383
  catch (error) {
382
384
  globalThis[_LoggerService].error(`
@@ -384,9 +386,9 @@ class MysqlConnection {
384
386
  sql: ${sql},
385
387
  params: ${params}
386
388
  `);
387
- reject(error);
389
+ throw error;
388
390
  }
389
- });
391
+ })();
390
392
  }
391
393
  release(sync) {
392
394
  try {
@@ -435,17 +437,17 @@ export class Mysql {
435
437
  return null;
436
438
  }
437
439
  ;
438
- return new Promise(async (resolve, reject) => {
440
+ return (async () => {
439
441
  try {
440
442
  const connection = await this[_daoDB].getConnection();
441
443
  globalThis[_LoggerService].debug?.('create new connection!');
442
- resolve(new MysqlConnection(connection));
444
+ return new MysqlConnection(connection);
443
445
  }
444
446
  catch (error) {
445
447
  globalThis[_LoggerService].error(error.message, { cause: error });
446
- reject(error);
448
+ throw error;
447
449
  }
448
- });
450
+ })();
449
451
  }
450
452
  transaction(sync, fn, conn) {
451
453
  if (sync === SyncMode.Sync) {
@@ -453,7 +455,7 @@ export class Mysql {
453
455
  return null;
454
456
  }
455
457
  ;
456
- return new Promise(async (resolve, reject) => {
458
+ return (async () => {
457
459
  let needCommit = false;
458
460
  let newConn = false;
459
461
  if (!conn) {
@@ -474,7 +476,7 @@ export class Mysql {
474
476
  await conn[_daoConnection].commit();
475
477
  globalThis[_LoggerService].debug?.('commit end!');
476
478
  }
477
- resolve(result);
479
+ return result;
478
480
  }
479
481
  catch (error) {
480
482
  if (needCommit) {
@@ -483,7 +485,7 @@ export class Mysql {
483
485
  globalThis[_LoggerService].debug?.('rollback end!');
484
486
  }
485
487
  globalThis[_LoggerService].error(error.message, { cause: error });
486
- reject(error);
488
+ throw error;
487
489
  }
488
490
  finally {
489
491
  try {
@@ -501,7 +503,7 @@ export class Mysql {
501
503
  globalThis[_LoggerService].warn?.('Failed to release connection in finally block', error);
502
504
  }
503
505
  }
504
- });
506
+ })();
505
507
  }
506
508
  close(sync) {
507
509
  this.isClosing = true;
@@ -536,7 +538,7 @@ class PostgresqlConnection {
536
538
  if (globalThis[_GlobalSqlOption].log === 'trace') {
537
539
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
538
540
  }
539
- return new Promise(async (resolve, reject) => {
541
+ return (async () => {
540
542
  try {
541
543
  const { rowCount } = await this[_daoConnection].query({
542
544
  text: replacePlaceholders(sql),
@@ -546,7 +548,7 @@ class PostgresqlConnection {
546
548
  if (globalThis[_GlobalSqlOption].log === 'trace') {
547
549
  globalThis[_LoggerService].verbose?.(result);
548
550
  }
549
- resolve({ affectedRows: rowCount || 0, insertId: 0n });
551
+ return { affectedRows: rowCount || 0, insertId: 0n };
550
552
  }
551
553
  catch (error) {
552
554
  globalThis[_LoggerService].error(`
@@ -554,9 +556,9 @@ class PostgresqlConnection {
554
556
  sql: ${sql},
555
557
  params: ${params}
556
558
  `);
557
- reject(error);
559
+ throw error;
558
560
  }
559
- });
561
+ })();
560
562
  }
561
563
  pluck(sync, sql, params) {
562
564
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -572,20 +574,19 @@ class PostgresqlConnection {
572
574
  if (globalThis[_GlobalSqlOption].log === 'trace') {
573
575
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
574
576
  }
575
- return new Promise(async (resolve, reject) => {
577
+ return (async () => {
576
578
  try {
577
579
  const { rows } = await this[_daoConnection].query({
578
580
  text: replacePlaceholders(sql),
579
581
  values: params
580
582
  });
583
+ // 修复 fall-through:原代码 if 分支 resolve 后没 return,导致 resolve(null)
584
+ // 紧跟其后(虽然第二次 resolve 是 no-op,但写法有歧义)。
581
585
  if (rows && rows[0]) {
582
586
  const r = Object.values(rows[0])[0];
583
- if (r === null)
584
- resolve(r);
585
- else
586
- resolve(r);
587
+ return r === null ? null : r;
587
588
  }
588
- resolve(null);
589
+ return null;
589
590
  }
590
591
  catch (error) {
591
592
  globalThis[_LoggerService].error(`
@@ -593,9 +594,9 @@ class PostgresqlConnection {
593
594
  sql: ${sql},
594
595
  params: ${params}
595
596
  `);
596
- reject(error);
597
+ throw error;
597
598
  }
598
- });
599
+ })();
599
600
  }
600
601
  get(sync, sql, params) {
601
602
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -611,7 +612,7 @@ class PostgresqlConnection {
611
612
  if (globalThis[_GlobalSqlOption].log === 'trace') {
612
613
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
613
614
  }
614
- return new Promise(async (resolve, reject) => {
615
+ return (async () => {
615
616
  try {
616
617
  const { rows } = await this[_daoConnection].query({
617
618
  text: replacePlaceholders(sql),
@@ -620,9 +621,10 @@ class PostgresqlConnection {
620
621
  if (globalThis[_GlobalSqlOption].log === 'trace') {
621
622
  globalThis[_LoggerService].verbose?.(rows);
622
623
  }
623
- if (rows && rows[0])
624
- resolve(rows[0]);
625
- resolve(null);
624
+ if (rows && rows[0]) {
625
+ return rows[0];
626
+ }
627
+ return null;
626
628
  }
627
629
  catch (error) {
628
630
  globalThis[_LoggerService].error(`
@@ -630,9 +632,9 @@ class PostgresqlConnection {
630
632
  sql: ${sql},
631
633
  params: ${params}
632
634
  `);
633
- reject(error);
635
+ throw error;
634
636
  }
635
- });
637
+ })();
636
638
  }
637
639
  raw(sync, sql, params) {
638
640
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -648,7 +650,7 @@ class PostgresqlConnection {
648
650
  if (globalThis[_GlobalSqlOption].log === 'trace') {
649
651
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
650
652
  }
651
- return new Promise(async (resolve, reject) => {
653
+ return (async () => {
652
654
  try {
653
655
  const { rows } = await this[_daoConnection].query({
654
656
  text: replacePlaceholders(sql),
@@ -657,9 +659,10 @@ class PostgresqlConnection {
657
659
  if (globalThis[_GlobalSqlOption].log === 'trace') {
658
660
  globalThis[_LoggerService].verbose?.(rows);
659
661
  }
660
- if (rows)
661
- resolve(rows.map((i) => Object.values(i)[0]));
662
- resolve([]);
662
+ if (rows) {
663
+ return rows.map((i) => Object.values(i)[0]);
664
+ }
665
+ return [];
663
666
  }
664
667
  catch (error) {
665
668
  globalThis[_LoggerService].error(`
@@ -667,9 +670,9 @@ class PostgresqlConnection {
667
670
  sql: ${sql},
668
671
  params: ${params}
669
672
  `);
670
- reject(error);
673
+ throw error;
671
674
  }
672
- });
675
+ })();
673
676
  }
674
677
  query(sync, sql, params) {
675
678
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -685,7 +688,7 @@ class PostgresqlConnection {
685
688
  if (globalThis[_GlobalSqlOption].log === 'trace') {
686
689
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
687
690
  }
688
- return new Promise(async (resolve, reject) => {
691
+ return (async () => {
689
692
  try {
690
693
  const { rows } = await this[_daoConnection].query({
691
694
  text: replacePlaceholders(sql),
@@ -694,7 +697,7 @@ class PostgresqlConnection {
694
697
  if (globalThis[_GlobalSqlOption].log === 'trace') {
695
698
  globalThis[_LoggerService].verbose?.(rows);
696
699
  }
697
- resolve(rows);
700
+ return rows;
698
701
  }
699
702
  catch (error) {
700
703
  globalThis[_LoggerService].error(`
@@ -702,9 +705,9 @@ class PostgresqlConnection {
702
705
  sql: ${sql},
703
706
  params: ${params}
704
707
  `);
705
- reject(error);
708
+ throw error;
706
709
  }
707
- });
710
+ })();
708
711
  }
709
712
  release(sync) {
710
713
  try {
@@ -753,16 +756,16 @@ export class Postgresql {
753
756
  return null;
754
757
  }
755
758
  ;
756
- return new Promise(async (resolve, reject) => {
759
+ return (async () => {
757
760
  try {
758
761
  const connection = await this[_daoDB].connect();
759
762
  globalThis[_LoggerService].debug?.('create new connection!');
760
- resolve(new PostgresqlConnection(connection));
763
+ return new PostgresqlConnection(connection);
761
764
  }
762
765
  catch (error) {
763
- reject(error);
766
+ throw error;
764
767
  }
765
- });
768
+ })();
766
769
  }
767
770
  transaction(sync, fn, conn) {
768
771
  if (sync === SyncMode.Sync) {
@@ -770,7 +773,7 @@ export class Postgresql {
770
773
  return null;
771
774
  }
772
775
  ;
773
- return new Promise(async (resolve, reject) => {
776
+ return (async () => {
774
777
  let needCommit = false;
775
778
  let newConn = false;
776
779
  if (!conn) {
@@ -791,7 +794,7 @@ export class Postgresql {
791
794
  await conn[_daoConnection].query('COMMIT');
792
795
  globalThis[_LoggerService].debug?.('commit end!');
793
796
  }
794
- resolve(result);
797
+ return result;
795
798
  }
796
799
  catch (error) {
797
800
  if (needCommit) {
@@ -800,7 +803,7 @@ export class Postgresql {
800
803
  globalThis[_LoggerService].debug?.('rollback end!');
801
804
  }
802
805
  globalThis[_LoggerService].error(error.message, { cause: error });
803
- reject(error);
806
+ throw error;
804
807
  }
805
808
  finally {
806
809
  try {
@@ -818,7 +821,7 @@ export class Postgresql {
818
821
  globalThis[_LoggerService].warn?.('Failed to release connection in finally block', error);
819
822
  }
820
823
  }
821
- });
824
+ })();
822
825
  }
823
826
  close(sync) {
824
827
  this.isClosing = true;
@@ -1087,11 +1090,11 @@ export class SqliteRemoteConnection {
1087
1090
  if (globalThis[_GlobalSqlOption].log === 'trace') {
1088
1091
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
1089
1092
  }
1090
- return new Promise(async (resolve, reject) => {
1093
+ return (async () => {
1091
1094
  try {
1092
1095
  const data = await this[_daoConnection].execute(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
1093
1096
  const { affectedRows, insertId } = decode(data, { extensionCodec });
1094
- resolve({ affectedRows, insertId: insertId ? BigInt(insertId) : 0n });
1097
+ return { affectedRows, insertId: insertId ? BigInt(insertId) : 0n };
1095
1098
  }
1096
1099
  catch (error) {
1097
1100
  globalThis[_LoggerService].error(`
@@ -1099,9 +1102,9 @@ export class SqliteRemoteConnection {
1099
1102
  sql: ${sql},
1100
1103
  params: ${params}
1101
1104
  `);
1102
- reject(error);
1105
+ throw error;
1103
1106
  }
1104
- });
1107
+ })();
1105
1108
  }
1106
1109
  pluck(sync, sql, params) {
1107
1110
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -1117,11 +1120,10 @@ export class SqliteRemoteConnection {
1117
1120
  if (globalThis[_GlobalSqlOption].log === 'trace') {
1118
1121
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
1119
1122
  }
1120
- return new Promise(async (resolve, reject) => {
1123
+ return (async () => {
1121
1124
  try {
1122
1125
  const data = await this[_daoConnection].pluck(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
1123
- const r = decode(data, { extensionCodec });
1124
- resolve(r);
1126
+ return decode(data, { extensionCodec });
1125
1127
  }
1126
1128
  catch (error) {
1127
1129
  globalThis[_LoggerService].error(`
@@ -1129,9 +1131,9 @@ export class SqliteRemoteConnection {
1129
1131
  sql: ${sql},
1130
1132
  params: ${params}
1131
1133
  `);
1132
- reject(error);
1134
+ throw error;
1133
1135
  }
1134
- });
1136
+ })();
1135
1137
  }
1136
1138
  get(sync, sql, params) {
1137
1139
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -1147,11 +1149,10 @@ export class SqliteRemoteConnection {
1147
1149
  if (globalThis[_GlobalSqlOption].log === 'trace') {
1148
1150
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
1149
1151
  }
1150
- return new Promise(async (resolve, reject) => {
1152
+ return (async () => {
1151
1153
  try {
1152
1154
  const data = await this[_daoConnection].get(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
1153
- const r = decode(data, { extensionCodec });
1154
- resolve(r);
1155
+ return decode(data, { extensionCodec });
1155
1156
  }
1156
1157
  catch (error) {
1157
1158
  globalThis[_LoggerService].error(`
@@ -1159,9 +1160,9 @@ export class SqliteRemoteConnection {
1159
1160
  sql: ${sql},
1160
1161
  params: ${params}
1161
1162
  `);
1162
- reject(error);
1163
+ throw error;
1163
1164
  }
1164
- });
1165
+ })();
1165
1166
  }
1166
1167
  raw(sync, sql, params) {
1167
1168
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -1177,11 +1178,10 @@ export class SqliteRemoteConnection {
1177
1178
  if (globalThis[_GlobalSqlOption].log === 'trace') {
1178
1179
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
1179
1180
  }
1180
- return new Promise(async (resolve, reject) => {
1181
+ return (async () => {
1181
1182
  try {
1182
1183
  const data = await this[_daoConnection].raw(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
1183
- const r = decode(data, { extensionCodec });
1184
- resolve(r);
1184
+ return decode(data, { extensionCodec });
1185
1185
  }
1186
1186
  catch (error) {
1187
1187
  globalThis[_LoggerService].error(`
@@ -1189,9 +1189,9 @@ export class SqliteRemoteConnection {
1189
1189
  sql: ${sql},
1190
1190
  params: ${params}
1191
1191
  `);
1192
- reject(error);
1192
+ throw error;
1193
1193
  }
1194
- });
1194
+ })();
1195
1195
  }
1196
1196
  query(sync, sql, params) {
1197
1197
  globalThis[_LoggerService].debug?.(sql, params ?? '');
@@ -1207,11 +1207,10 @@ export class SqliteRemoteConnection {
1207
1207
  if (globalThis[_GlobalSqlOption].log === 'trace') {
1208
1208
  globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
1209
1209
  }
1210
- return new Promise(async (resolve, reject) => {
1210
+ return (async () => {
1211
1211
  try {
1212
1212
  const data = await this[_daoConnection].query(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
1213
- const r = decode(data, { extensionCodec });
1214
- resolve(r);
1213
+ return decode(data, { extensionCodec });
1215
1214
  }
1216
1215
  catch (error) {
1217
1216
  globalThis[_LoggerService].error(`
@@ -1219,9 +1218,9 @@ export class SqliteRemoteConnection {
1219
1218
  sql: ${sql},
1220
1219
  params: ${params}
1221
1220
  `);
1222
- reject(error);
1221
+ throw error;
1223
1222
  }
1224
- });
1223
+ })();
1225
1224
  }
1226
1225
  release(sync) {
1227
1226
  }
@@ -1238,17 +1237,10 @@ export class SqliteRemote {
1238
1237
  return null;
1239
1238
  }
1240
1239
  ;
1241
- return new Promise(async (resolve, reject) => {
1242
- if (!this.connection) {
1243
- this.connection = new SqliteRemoteConnection(this[_daoDB], this[_sqliteRemoteName]);
1244
- }
1245
- try {
1246
- resolve(this.connection);
1247
- }
1248
- catch (error) {
1249
- reject(error);
1250
- }
1251
- });
1240
+ if (!this.connection) {
1241
+ this.connection = new SqliteRemoteConnection(this[_daoDB], this[_sqliteRemoteName]);
1242
+ }
1243
+ return Promise.resolve(this.connection);
1252
1244
  }
1253
1245
  transaction(sync, fn, conn) {
1254
1246
  globalThis[_LoggerService].warn(`SQLITEREMOTE not supported transaction`);
@@ -1847,6 +1839,19 @@ function P(skipConn = false) {
1847
1839
  option.dbName = option?.dbName ?? this[_daoDBName] ?? _primaryDB;
1848
1840
  option.dbType = this[_dbType] ?? globalThis[_GlobalSqlOption].dbType ?? DBType.Mysql;
1849
1841
  option.dao = globalThis[_dao][option.dbType][option.dbName];
1842
+ // 错误日志输出函数:原实现里 `let args = ''` 把外层 rest 参数 `args` 给遮蔽了,
1843
+ // 导致 args.length 永远是 0,params 日志永远打不出来,生产排查时丢失关键上下文。
1844
+ const dumpArgs = (callArgs) => {
1845
+ if (callArgs.length > 0 && callArgs[0] && callArgs[0].params) {
1846
+ try {
1847
+ return JSON.stringify(callArgs[0].params);
1848
+ }
1849
+ catch {
1850
+ return '<unserializable params>';
1851
+ }
1852
+ }
1853
+ return '';
1854
+ };
1850
1855
  if (option.dbType === DBType.Sqlite) {
1851
1856
  if (!option.dao) {
1852
1857
  const db = new Sqlite(new globalThis[_GlobalSqlOption].BetterSqlite3(option.dbName, { fileMustExist: false }));
@@ -1870,11 +1875,7 @@ function P(skipConn = false) {
1870
1875
  return result;
1871
1876
  }
1872
1877
  catch (error) {
1873
- let args = '';
1874
- if (args.length > 0 && args[0].params) {
1875
- args = JSON.stringify(args[0].params);
1876
- }
1877
- console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
1878
+ console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
1878
1879
  throw error;
1879
1880
  }
1880
1881
  finally {
@@ -1898,26 +1899,22 @@ function P(skipConn = false) {
1898
1899
  option.dao = db;
1899
1900
  }
1900
1901
  Throw.if(option.sync === SyncMode.Sync, 'SqliteRemote remote can not sync!');
1901
- return new Promise(async (resolve, reject) => {
1902
- // 连接共享
1903
- if (skipConn === false && !option.conn) {
1904
- (option).conn = await option.dao.createConnection(SyncMode.Async);
1905
- }
1906
- else {
1907
- needRealseConn = false;
1908
- }
1902
+ return (async () => {
1909
1903
  try {
1904
+ // 连接共享
1905
+ if (skipConn === false && !option.conn) {
1906
+ (option).conn = await option.dao.createConnection(SyncMode.Async);
1907
+ }
1908
+ else {
1909
+ needRealseConn = false;
1910
+ }
1910
1911
  const result = await fn.call(this, ...args);
1911
1912
  globalThis[_LoggerService].log(`${propertyKey}:${option.sqlId ?? option.tableName}:use ${+new Date() - startTime}ms`);
1912
- resolve(result);
1913
+ return result;
1913
1914
  }
1914
1915
  catch (error) {
1915
- let args = '';
1916
- if (args.length > 0 && args[0].params) {
1917
- args = JSON.stringify(args[0].params);
1918
- }
1919
- console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
1920
- reject(error);
1916
+ console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
1917
+ throw error;
1921
1918
  }
1922
1919
  finally {
1923
1920
  if (needRealseConn && option && option.conn) {
@@ -1928,11 +1925,11 @@ function P(skipConn = false) {
1928
1925
  }
1929
1926
  }
1930
1927
  }
1931
- });
1928
+ })();
1932
1929
  }
1933
1930
  else if (option.dbType === DBType.Mysql) {
1934
1931
  Throw.if(!option.dao, `not found db:${String(option.dbName)}(${option.dbType})`);
1935
- return new Promise(async (resolve, reject) => {
1932
+ return (async () => {
1936
1933
  try {
1937
1934
  // 连接共享
1938
1935
  if (skipConn === false && !option.conn) {
@@ -1943,15 +1940,11 @@ function P(skipConn = false) {
1943
1940
  }
1944
1941
  const result = await fn.call(this, ...args);
1945
1942
  globalThis[_LoggerService].log(`${propertyKey}:${option.sqlId ?? option.tableName}:use ${+new Date() - startTime}ms`);
1946
- resolve(result);
1943
+ return result;
1947
1944
  }
1948
1945
  catch (error) {
1949
- let args = '';
1950
- if (args.length > 0 && args[0].params) {
1951
- args = JSON.stringify(args[0].params);
1952
- }
1953
- console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
1954
- reject(error);
1946
+ console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
1947
+ throw error;
1955
1948
  }
1956
1949
  finally {
1957
1950
  if (needRealseConn && option && option.conn) {
@@ -1962,11 +1955,11 @@ function P(skipConn = false) {
1962
1955
  }
1963
1956
  }
1964
1957
  }
1965
- });
1958
+ })();
1966
1959
  }
1967
1960
  else if (option.dbType === DBType.Postgresql) {
1968
1961
  Throw.if(!option.dao, `not found db:${String(option.dbName)}(${option.dbType})`);
1969
- return new Promise(async (resolve, reject) => {
1962
+ return (async () => {
1970
1963
  try {
1971
1964
  // 连接共享
1972
1965
  if (skipConn === false && !option.conn) {
@@ -1977,15 +1970,11 @@ function P(skipConn = false) {
1977
1970
  }
1978
1971
  const result = await fn.call(this, ...args);
1979
1972
  globalThis[_LoggerService].log(`${propertyKey}:${option.sqlId ?? option.tableName}:use ${+new Date() - startTime}ms`);
1980
- resolve(result);
1973
+ return result;
1981
1974
  }
1982
1975
  catch (error) {
1983
- let args = '';
1984
- if (args.length > 0 && args[0].params) {
1985
- args = JSON.stringify(args[0].params);
1986
- }
1987
- console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
1988
- reject(error);
1976
+ console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
1977
+ throw error;
1989
1978
  }
1990
1979
  finally {
1991
1980
  if (needRealseConn && option && option.conn) {
@@ -1996,7 +1985,7 @@ function P(skipConn = false) {
1996
1985
  }
1997
1986
  }
1998
1987
  }
1999
- });
1988
+ })();
2000
1989
  }
2001
1990
  };
2002
1991
  };
@@ -2368,7 +2357,9 @@ export class SqlService {
2368
2357
  break;
2369
2358
  }
2370
2359
  case InsertMode.InsertWithTempTable: {
2371
- const tableTemp = `${option?.tableName}_${Math.random()}`.replace(/\./, '');
2360
+ // snowflake 替代 Math.random()——Math.random() 在高并发下可能碰撞,
2361
+ // 同一连接里多次 InsertWithTempTable 会因为撞名 DDL 报错。
2362
+ const tableTemp = `${option?.tableName}_${snowflake.generate()}`;
2372
2363
  const tableTempESC = tableTemp;
2373
2364
  sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
2374
2365
  const finalColumns = new Set();
@@ -2608,7 +2599,7 @@ export class SqlService {
2608
2599
  Throw.if(!!option.id && !!this[_ids] && this[_ids].length > 1, 'muit id must set where!');
2609
2600
  Throw.if(!!option.id && !!option.where && !option.whereSql, 'id and where only one can set!');
2610
2601
  option.mode ?? (option.mode = DeleteMode.Common);
2611
- const tableTemp = `${option?.tableName}_${Math.random()}`.replace(/\./, '');
2602
+ const tableTemp = `${option?.tableName}_${snowflake.generate()}`;
2612
2603
  const tableTempESC = tableTemp;
2613
2604
  const tableNameESC = option?.tableName;
2614
2605
  if (option.id) {
@@ -2754,7 +2745,7 @@ export class SqlService {
2754
2745
  option.mode ?? (option.mode = SelectMode.Common);
2755
2746
  option.templateResult ?? (option.templateResult = TemplateResult.AssertOne);
2756
2747
  option.error ?? (option.error = 'error data!');
2757
- const tableTemp = `${option?.tableName}_${Math.random()}`.replace(/\./, '');
2748
+ const tableTemp = `${option?.tableName}_${snowflake.generate()}`;
2758
2749
  const tableTempESC = tableTemp;
2759
2750
  const tableNameESC = option?.tableName;
2760
2751
  if (option.id) {
@@ -2800,23 +2791,18 @@ export class SqlService {
2800
2791
  return this._template(option.templateResult, result, option.error);
2801
2792
  }
2802
2793
  else {
2803
- return new Promise(async (resolve, reject) => {
2804
- try {
2805
- let result;
2806
- for (let i = 0; i < sqls.length; i++) {
2807
- if (i === resultIndex) {
2808
- result = await option.conn.query(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
2809
- }
2810
- else {
2811
- await option.conn.execute(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
2812
- }
2794
+ return (async () => {
2795
+ let result;
2796
+ for (let i = 0; i < sqls.length; i++) {
2797
+ if (i === resultIndex) {
2798
+ result = await option.conn.query(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
2799
+ }
2800
+ else {
2801
+ await option.conn.execute(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
2813
2802
  }
2814
- resolve(this._template(option.templateResult, result, option.error));
2815
- }
2816
- catch (error) {
2817
- reject(error);
2818
2803
  }
2819
- });
2804
+ return this._template(option.templateResult, result, option.error);
2805
+ })();
2820
2806
  }
2821
2807
  }
2822
2808
  _select(templateResult, result, def, errorMsg, hump, mapper, mapperIfUndefined, dataConvert) {
@@ -2931,15 +2917,10 @@ export class SqlService {
2931
2917
  return this._select(option.selectResult, result, option.defValue, option.errorMsg, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert);
2932
2918
  }
2933
2919
  else {
2934
- return new Promise(async (resolve, reject) => {
2935
- try {
2936
- const result = await option.conn.query(SyncMode.Async, sql, params);
2937
- resolve(this._select(option.selectResult, result, option.defValue, option.errorMsg, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert));
2938
- }
2939
- catch (error) {
2940
- reject(error);
2941
- }
2942
- });
2920
+ return (async () => {
2921
+ const result = await option.conn.query(SyncMode.Async, sql, params);
2922
+ return this._select(option.selectResult, result, option.defValue, option.errorMsg, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert);
2923
+ })();
2943
2924
  }
2944
2925
  }
2945
2926
  selectBatch(option) {
@@ -2956,15 +2937,10 @@ export class SqlService {
2956
2937
  return result.map(item => this._select(option.selectResult, item, null, undefined, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert));
2957
2938
  }
2958
2939
  else {
2959
- return new Promise(async (resolve, reject) => {
2960
- try {
2961
- const result = await option.conn.query(SyncMode.Async, sql, params);
2962
- resolve(result.map(item => this._select(option.selectResult, item, null, undefined, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert)));
2963
- }
2964
- catch (error) {
2965
- reject(error);
2966
- }
2967
- });
2940
+ return (async () => {
2941
+ const result = await option.conn.query(SyncMode.Async, sql, params);
2942
+ return result.map(item => this._select(option.selectResult, item, null, undefined, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert));
2943
+ })();
2968
2944
  }
2969
2945
  }
2970
2946
  excute(option) {
@@ -2977,15 +2953,10 @@ export class SqlService {
2977
2953
  return result.affectedRows;
2978
2954
  }
2979
2955
  else {
2980
- return new Promise(async (resolve, reject) => {
2981
- try {
2982
- const result = await option.conn.execute(SyncMode.Async, sql, params);
2983
- resolve(result.affectedRows);
2984
- }
2985
- catch (error) {
2986
- reject(error);
2987
- }
2988
- });
2956
+ return (async () => {
2957
+ const result = await option.conn.execute(SyncMode.Async, sql, params);
2958
+ return result.affectedRows;
2959
+ })();
2989
2960
  }
2990
2961
  }
2991
2962
  transaction(option) {
@@ -2993,15 +2964,7 @@ export class SqlService {
2993
2964
  return option.dao.transaction(SyncMode.Sync, option.fn, option.conn);
2994
2965
  }
2995
2966
  else {
2996
- return new Promise(async (resolve, reject) => {
2997
- try {
2998
- const rt = await option.dao.transaction(SyncMode.Async, option.fn, option.conn);
2999
- resolve(rt);
3000
- }
3001
- catch (error) {
3002
- reject(error);
3003
- }
3004
- });
2967
+ return option.dao.transaction(SyncMode.Async, option.fn, option.conn);
3005
2968
  }
3006
2969
  }
3007
2970
  stream() {
@@ -3019,7 +2982,9 @@ export class SqlService {
3019
2982
  if (option.hump || (option.hump === undefined && globalThis[_Hump]) && option.sortName) {
3020
2983
  option.sortName = P2C(option.sortName);
3021
2984
  }
3022
- Object.assign(option.params, {
2985
+ // 不要 Object.assign 进调用方的 params——@P 装饰器对 option 只做了浅拷贝,
2986
+ // option.params 仍指向用户传入的对象,原写法会把分页字段写回去污染下一次调用。
2987
+ option.params = Object.assign({}, option.params, {
3023
2988
  limitStart: calc(option.pageNumber).sub(1).mul(option.pageSize).over(),
3024
2989
  limitEnd: option.pageSize - 0,
3025
2990
  sortName: option.sortName ?? undefined,
@@ -3081,43 +3046,40 @@ export class SqlService {
3081
3046
  return result;
3082
3047
  }
3083
3048
  else {
3084
- return new Promise(async (resolve, reject) => {
3085
- try {
3086
- if (sqlCount) {
3087
- result.total = await this.select({
3088
- ...option,
3089
- sql: sqlCount,
3090
- sync: SyncMode.Async,
3091
- selectResult: SelectResult.R_C_Assert
3092
- });
3093
- result.size = calc(result.total)
3094
- .add(option.pageSize ?? 10 - 1)
3095
- .div(option.pageSize)
3096
- .round(0, 2)
3097
- .over();
3098
- }
3099
- if (sqlSum) {
3100
- result.sum = await this.select({
3101
- ...option,
3102
- sql: sqlSum,
3103
- sync: SyncMode.Async,
3104
- selectResult: SelectResult.R_CS_Assert
3105
- });
3106
- }
3107
- if (sql) {
3108
- result.records = await this.select({
3109
- ...option,
3110
- sql,
3111
- sync: SyncMode.Async,
3112
- selectResult: SelectResult.RS_CS
3113
- });
3114
- }
3115
- resolve(result);
3049
+ return (async () => {
3050
+ if (sqlCount) {
3051
+ result.total = await this.select({
3052
+ ...option,
3053
+ sql: sqlCount,
3054
+ sync: SyncMode.Async,
3055
+ selectResult: SelectResult.R_C_Assert
3056
+ });
3057
+ // 修正运算符优先级:`option.pageSize ?? 10 - 1` 被解析为 `pageSize ?? 9`,
3058
+ // 而同步分支用的是 `option.pageSize - 1`,行为不一致。
3059
+ result.size = calc(result.total)
3060
+ .add((option.pageSize ?? 10) - 1)
3061
+ .div(option.pageSize)
3062
+ .round(0, 2)
3063
+ .over();
3116
3064
  }
3117
- catch (error) {
3118
- reject(error);
3065
+ if (sqlSum) {
3066
+ result.sum = await this.select({
3067
+ ...option,
3068
+ sql: sqlSum,
3069
+ sync: SyncMode.Async,
3070
+ selectResult: SelectResult.R_CS_Assert
3071
+ });
3119
3072
  }
3120
- });
3073
+ if (sql) {
3074
+ result.records = await this.select({
3075
+ ...option,
3076
+ sql,
3077
+ sync: SyncMode.Async,
3078
+ selectResult: SelectResult.RS_CS
3079
+ });
3080
+ }
3081
+ return result;
3082
+ })();
3121
3083
  }
3122
3084
  }
3123
3085
  /**
@@ -3219,48 +3181,25 @@ export class SqlService {
3219
3181
  }
3220
3182
  }
3221
3183
  else if (option.dbType === DBType.SqliteRemote) {
3222
- return new Promise(async (resolve, reject) => {
3223
- try {
3224
- if (option?.force) {
3225
- await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${tableES};`);
3226
- }
3227
- const lastVersion = this[_sqlite_version] ?? '1';
3228
- // 检查表
3229
- const tableCheckResult = await option.conn.pluck(SyncMode.Async, `SELECT COUNT(1) t FROM sqlite_master WHERE TYPE = 'table' AND name = ?`, [option.tableName]);
3230
- if (tableCheckResult) {
3231
- // 旧版本
3232
- const tableVersion = await option.conn.pluck(SyncMode.Async, 'SELECT ______version v from TABLE_VERSION WHERE ______tableName = ?', [option.tableName]);
3233
- if (tableVersion && tableVersion < lastVersion) { // 发现需要升级的版本
3234
- // 更新版本
3235
- const columns = iterate(await option.conn.query(SyncMode.Async, `PRAGMA table_info(${tableES})`))
3236
- .filter(c => this[_fields].hasOwnProperty(C2P(c.name, globalThis[_Hump])))
3237
- .map(c => c.name)
3238
- .join(',');
3239
- const rtable = `${option.tableName}_${tableVersion.replace(/\./, '_')}`;
3240
- await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
3241
- await option.conn.execute(SyncMode.Async, `ALTER TABLE ${tableES} RENAME TO ${rtable};`);
3242
- await option.conn.execute(SyncMode.Async, `
3243
- CREATE TABLE IF NOT EXISTS ${tableES}(
3244
- ${Object.values(this[_fields]).map(K => K[DBType.Sqlite]()).join(',')}
3245
- ${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields][i]?.C2()).join(',')})` : ''}
3246
- );
3247
- `);
3248
- if (this[_index] && this[_index].length) {
3249
- for (const index of this[_index]) {
3250
- await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${tableES} ("${this[_fields][index]?.C2()}");`);
3251
- }
3252
- }
3253
- await option.conn.execute(SyncMode.Async, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
3254
- await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
3255
- // 更新完毕,保存版本号
3256
- await option.conn.execute(SyncMode.Async, 'UPDATE TABLE_VERSION SET ______version = ? WHERE ______tableName = ?', [option.tableName, lastVersion]);
3257
- }
3258
- else if (!tableVersion) { // 不需要升级情况:没有旧的版本号
3259
- await option.conn.execute(SyncMode.Async, 'INSERT INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option.tableName, lastVersion]);
3260
- }
3261
- }
3262
- else { // 表不存在
3263
- // 创建表
3184
+ return (async () => {
3185
+ if (option?.force) {
3186
+ await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${tableES};`);
3187
+ }
3188
+ const lastVersion = this[_sqlite_version] ?? '1';
3189
+ // 检查表
3190
+ const tableCheckResult = await option.conn.pluck(SyncMode.Async, `SELECT COUNT(1) t FROM sqlite_master WHERE TYPE = 'table' AND name = ?`, [option.tableName]);
3191
+ if (tableCheckResult) {
3192
+ // 旧版本
3193
+ const tableVersion = await option.conn.pluck(SyncMode.Async, 'SELECT ______version v from TABLE_VERSION WHERE ______tableName = ?', [option.tableName]);
3194
+ if (tableVersion && tableVersion < lastVersion) { // 发现需要升级的版本
3195
+ // 更新版本
3196
+ const columns = iterate(await option.conn.query(SyncMode.Async, `PRAGMA table_info(${tableES})`))
3197
+ .filter(c => this[_fields].hasOwnProperty(C2P(c.name, globalThis[_Hump])))
3198
+ .map(c => c.name)
3199
+ .join(',');
3200
+ const rtable = `${option.tableName}_${tableVersion.replace(/\./, '_')}`;
3201
+ await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
3202
+ await option.conn.execute(SyncMode.Async, `ALTER TABLE ${tableES} RENAME TO ${rtable};`);
3264
3203
  await option.conn.execute(SyncMode.Async, `
3265
3204
  CREATE TABLE IF NOT EXISTS ${tableES}(
3266
3205
  ${Object.values(this[_fields]).map(K => K[DBType.Sqlite]()).join(',')}
@@ -3269,17 +3208,34 @@ export class SqlService {
3269
3208
  `);
3270
3209
  if (this[_index] && this[_index].length) {
3271
3210
  for (const index of this[_index]) {
3272
- await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${option.tableName} ("${this[_fields][index]?.C2()}");`);
3211
+ await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${tableES} ("${this[_fields][index]?.C2()}");`);
3273
3212
  }
3274
3213
  }
3275
- await option.conn.execute(SyncMode.Async, 'INSERT OR REPLACE INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option.tableName, lastVersion]);
3214
+ await option.conn.execute(SyncMode.Async, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
3215
+ await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
3216
+ // 更新完毕,保存版本号
3217
+ await option.conn.execute(SyncMode.Async, 'UPDATE TABLE_VERSION SET ______version = ? WHERE ______tableName = ?', [option.tableName, lastVersion]);
3218
+ }
3219
+ else if (!tableVersion) { // 不需要升级情况:没有旧的版本号
3220
+ await option.conn.execute(SyncMode.Async, 'INSERT INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option.tableName, lastVersion]);
3276
3221
  }
3277
- resolve();
3278
3222
  }
3279
- catch (error) {
3280
- reject(error);
3223
+ else { // 表不存在
3224
+ // 创建表
3225
+ await option.conn.execute(SyncMode.Async, `
3226
+ CREATE TABLE IF NOT EXISTS ${tableES}(
3227
+ ${Object.values(this[_fields]).map(K => K[DBType.Sqlite]()).join(',')}
3228
+ ${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields][i]?.C2()).join(',')})` : ''}
3229
+ );
3230
+ `);
3231
+ if (this[_index] && this[_index].length) {
3232
+ for (const index of this[_index]) {
3233
+ await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${option.tableName} ("${this[_fields][index]?.C2()}");`);
3234
+ }
3235
+ }
3236
+ await option.conn.execute(SyncMode.Async, 'INSERT OR REPLACE INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option.tableName, lastVersion]);
3281
3237
  }
3282
- });
3238
+ })();
3283
3239
  }
3284
3240
  }
3285
3241
  close(option) {
@@ -4022,33 +3978,28 @@ class StreamQuery {
4022
3978
  return result;
4023
3979
  }
4024
3980
  else {
4025
- return new Promise(async (resolve, reject) => {
4026
- try {
4027
- result.total = await this._service.select({
4028
- ...option,
4029
- params,
4030
- sql: sqlCount,
4031
- sync: SyncMode.Async,
4032
- selectResult: SelectResult.R_C_Assert
4033
- });
4034
- result.size = calc(result.total)
4035
- .add(this._pageSize - 1)
4036
- .div(this._pageSize)
4037
- .round(0, 2)
4038
- .over();
4039
- result.records = await this._service.select({
4040
- ...option,
4041
- params,
4042
- sql,
4043
- sync: SyncMode.Async,
4044
- selectResult: SelectResult.RS_CS
4045
- });
4046
- resolve(result);
4047
- }
4048
- catch (error) {
4049
- reject(error);
4050
- }
4051
- });
3981
+ return (async () => {
3982
+ result.total = await this._service.select({
3983
+ ...option,
3984
+ params,
3985
+ sql: sqlCount,
3986
+ sync: SyncMode.Async,
3987
+ selectResult: SelectResult.R_C_Assert
3988
+ });
3989
+ result.size = calc(result.total)
3990
+ .add(this._pageSize - 1)
3991
+ .div(this._pageSize)
3992
+ .round(0, 2)
3993
+ .over();
3994
+ result.records = await this._service.select({
3995
+ ...option,
3996
+ params,
3997
+ sql,
3998
+ sync: SyncMode.Async,
3999
+ selectResult: SelectResult.RS_CS
4000
+ });
4001
+ return result;
4002
+ })();
4052
4003
  }
4053
4004
  }
4054
4005
  excuteUpdate(option) {
@@ -5127,42 +5078,54 @@ export function getRedisDB(db) {
5127
5078
  return rd;
5128
5079
  }
5129
5080
  /**
5130
- redlock
5081
+ redlock —— 用 redlock 做一把元锁([lockex]key),把"读 count、判断 count、incr"做成原子操作。
5082
+ 原实现失败时 `return await GetRedisLock(...)` 递归调用自己,redis 长时间不可用会栈溢出;
5083
+ 改成循环 + 指数退避 + 最大重试上限,达到上限后抛出(让上层决定是否走降级路径)。
5131
5084
  */
5085
+ const GET_REDIS_LOCK_MAX_RETRIES = 10;
5086
+ const GET_REDIS_LOCK_BASE_DELAY = 50; // 毫秒
5132
5087
  export async function GetRedisLock(key, lockMaxActive) {
5133
5088
  const lock = globalThis[_dao][DBType.RedisLock];
5134
5089
  Throw.if(!lock, 'not found lock!');
5135
5090
  const db = getRedisDB();
5136
- let initLock;
5137
- try {
5138
- initLock = await lock.acquire([`[lockex]${key}`], 5000);
5139
- const count = await db.get(key);
5140
- if (count === null || parseInt(count) < (lockMaxActive ?? 1)) {
5141
- await db.incr(key);
5142
- return true;
5091
+ let lastError;
5092
+ for (let attempt = 0; attempt < GET_REDIS_LOCK_MAX_RETRIES; attempt++) {
5093
+ let initLock;
5094
+ try {
5095
+ initLock = await lock.acquire([`[lockex]${key}`], 5000);
5096
+ const count = await db.get(key);
5097
+ if (count === null || parseInt(count) < (lockMaxActive ?? 1)) {
5098
+ await db.incr(key);
5099
+ return true;
5100
+ }
5101
+ else {
5102
+ return false;
5103
+ }
5143
5104
  }
5144
- else {
5145
- return false;
5105
+ catch (er) {
5106
+ lastError = er;
5107
+ // 指数退避,避免 redis 抖动时打死服务
5108
+ const delay = Math.min(GET_REDIS_LOCK_BASE_DELAY * Math.pow(2, attempt), 1000);
5109
+ globalThis[_LoggerService].debug?.(`GetRedisLock ${key} attempt ${attempt + 1} failed: ${er?.message}, retry after ${delay}ms`);
5110
+ await sleep(delay);
5146
5111
  }
5147
- }
5148
- catch (er) {
5149
- return await GetRedisLock(key, lockMaxActive);
5150
- }
5151
- finally {
5152
- if (initLock) {
5153
- try {
5154
- await initLock.release();
5155
- // eslint-disable-next-line no-empty
5156
- }
5157
- catch (error) {
5112
+ finally {
5113
+ if (initLock) {
5114
+ try {
5115
+ await initLock.release();
5116
+ // eslint-disable-next-line no-empty
5117
+ }
5118
+ catch (error) {
5119
+ }
5158
5120
  }
5159
5121
  }
5160
5122
  }
5123
+ throw new Error(`GetRedisLock ${key} failed after ${GET_REDIS_LOCK_MAX_RETRIES} retries: ${lastError?.message ?? lastError}`);
5161
5124
  }
5162
5125
  ;
5163
5126
  /** 对FN加锁、缓存执行 */
5164
5127
  export async function excuteWithLock(config, fn__) {
5165
- const key = config.key_real ? `[lock]${config.key_real}` : `[lock]${typeof config.key === 'function' ? config.key() : config.key}`;
5128
+ const key = `[lock]${typeof config.key === 'function' ? config.key() : config.key}`;
5166
5129
  const db = getRedisDB();
5167
5130
  let wait_time = 0;
5168
5131
  const fn = async () => {
@@ -5193,43 +5156,72 @@ export async function excuteWithLock(config, fn__) {
5193
5156
  };
5194
5157
  return await fn();
5195
5158
  }
5196
- /** 与缓存共用时,需要在缓存之前:有缓存则返回缓存,否则加锁执行并缓存,后续队列全部返回缓存,跳过执行 */
5159
+ /**
5160
+ * 方法加锁装饰器。
5161
+ * 与 `MethodCache` 组合时,**不要**手工叠装饰器——`MethodCache` 内部已经实现了
5162
+ * "先查缓存 → miss 才加锁 → 锁内二次检查 → 仍 miss 才执行" 的 single-flight 语义,
5163
+ * 直接给方法加 `@MethodCache` 即可。`@MethodLock` 单独使用时仅做并发互斥,不感知缓存。
5164
+ */
5197
5165
  export function MethodLock(config) {
5198
5166
  return function (target, _propertyKey, descriptor) {
5199
5167
  const fn__ = descriptor.value;
5200
5168
  descriptor.value = async function (...args) {
5201
- config.key_real = typeof config.key === 'function' ? config.key.call(target, ...args) : config.key;
5202
- return await excuteWithLock(config, async () => await fn__.call(this, ...args));
5169
+ // 注意:装饰器闭包里的 `config` 是所有调用共享的同一个对象,
5170
+ // 不能把 per-call 的状态写回去——必须每次构造一份新的 config 传给 excuteWithLock。
5171
+ const resolvedKey = typeof config.key === 'function' ? config.key.call(this, ...args) : config.key;
5172
+ const perCallConfig = { ...config, key: resolvedKey };
5173
+ return await excuteWithLock(perCallConfig, async () => await fn__.call(this, ...args));
5203
5174
  };
5204
5175
  };
5205
5176
  }
5206
5177
  /** 设置方法缓存 */
5207
5178
  async function setMethodCache(config, devid) {
5208
5179
  const db = getRedisDB();
5209
- if (config.result !== null && config.result !== undefined) {
5210
- // 映射关系存放
5180
+ const isNullish = config.result === null || config.result === undefined;
5181
+ // 旧行为:null / undefined 不进缓存;新增 cacheNullValue=true 时写入 'null'(穿透防御)。
5182
+ if (isNullish && !config.cacheNullValue) {
5183
+ return;
5184
+ }
5185
+ // 统一序列化:undefined 不是合法 JSON,归一化成 null。
5186
+ const payload = isNullish ? 'null' : JSON.stringify(config.result);
5187
+ // 负缓存通常用更短 TTL,避免业务恢复后还长时间返回旧的 null。
5188
+ const ttl = isNullish
5189
+ ? (config.nullCacheTime ?? config.autoClearTime)
5190
+ : config.autoClearTime;
5191
+ // 映射关系存放(负缓存也参与关联清除,因为外部清缓存的语义不区分正负)
5192
+ if (config.clearKey && config.clearKey.length > 0) {
5193
+ for (const clear of config.clearKey) {
5194
+ await db.sadd(`[cache-parent]${clear}`, config.key);
5195
+ await db.sadd(`[cache-child]${config.key}`, clear);
5196
+ }
5197
+ }
5198
+ if (ttl) { // 自动清空
5199
+ await db.set(`[cache]${config.key}`, payload, 'EX', ttl * 60);
5200
+ globalThis[_LoggerService].debug?.(`cache ${config.key} seted${isNullish ? ' (null)' : ''}!`);
5201
+ // 订阅:清空 clear list —— 同一 key 在缓存生命周期内只注册一次监听器,
5202
+ // 否则每次 miss 都会 .on 一次,clearMethodCache 触发时回调会被执行 N 遍,
5203
+ // 长期运行造成监听器泄漏。
5211
5204
  if (config.clearKey && config.clearKey.length > 0) {
5212
- for (const clear of config.clearKey) {
5213
- await db.sadd(`[cache-parent]${clear}`, config.key);
5214
- await db.sadd(`[cache-child]${config.key}`, clear);
5215
- }
5216
- }
5217
- if (config.autoClearTime) { // 自动清空
5218
- await db.set(`[cache]${config.key}`, JSON.stringify(config.result), 'EX', config.autoClearTime * 60);
5219
- // 订阅:清空 clear list
5220
- if (config.clearKey && config.clearKey.length > 0) {
5221
- globalThis[_EventBus].on(`[cache]${config.key}`, async (key) => {
5205
+ const event = `[cache]${config.key}`;
5206
+ if (globalThis[_EventBus].listenerCount(event) === 0) {
5207
+ globalThis[_EventBus].on(event, async (key) => {
5222
5208
  await clearChild(key, true);
5209
+ globalThis[_LoggerService].debug?.(`cache ${key} clear by key!`);
5223
5210
  });
5224
5211
  }
5225
5212
  }
5226
- else {
5227
- await db.set(`[cache]${config.key}`, JSON.stringify(config.result));
5228
- }
5229
- if (devid) {
5230
- // 订阅:清空 clear list
5231
- globalThis[_EventBus].on(`user-${devid}`, async function (key) {
5213
+ }
5214
+ else {
5215
+ await db.set(`[cache]${config.key}`, payload);
5216
+ globalThis[_LoggerService].debug?.(`cache ${config.key} seted${isNullish ? ' (null)' : ''}!`);
5217
+ }
5218
+ if (devid) {
5219
+ // 订阅:清空 clear list —— 同样去重,避免每次 miss 都重复注册。
5220
+ const event = `user-${devid}`;
5221
+ if (globalThis[_EventBus].listenerCount(event) === 0) {
5222
+ globalThis[_EventBus].on(event, async function (key) {
5232
5223
  await clearChild(key);
5224
+ globalThis[_LoggerService].debug?.(`cache ${key} clear by devid!`);
5233
5225
  });
5234
5226
  }
5235
5227
  }
@@ -5274,53 +5266,93 @@ async function clearParent(clearKey) {
5274
5266
  }
5275
5267
  }
5276
5268
  /**
5277
- * 执行一个方法fn,
5278
- * 如果有缓存,则返回缓存,否则执行方法并缓存
5269
+ * 缓存执行的核心:先查缓存 → miss 才加锁 → 锁内二次检查 → 仍 miss 才执行原方法 → 写缓存。
5270
+ * 这样同一 key 的并发 N 个请求里,只会有 1 个真正穿透到 fn,其余等待者在锁内读到刚写入的缓存。
5271
+ * 锁本身有等待超时和锁 TTL 兜底,redis 不可用时 fallback 到无锁直跑(保留可用性,退化为旧行为)。
5272
+ *
5273
+ * 命中判断用 `cached !== null`(ioredis 在 key 不存在时返回 `null`),
5274
+ * 这样 `cacheNullValue=true` 时存进去的字面量 `'null'` 也会算作命中,达到防穿透效果。
5279
5275
  */
5280
- export async function excuteWithCache(config, fn) {
5276
+ async function excuteCacheCore(opts) {
5281
5277
  const db = getRedisDB();
5282
- const key = typeof config.key === 'function' ? config.key() : config.key;
5283
- const cache = await db.get(`[cache]${key}`);
5284
- if (cache) {
5285
- globalThis[_LoggerService].debug?.(`cache ${key} hit!`);
5286
- return JSON.parse(cache);
5287
- }
5288
- else {
5289
- const result = await fn();
5290
- globalThis[_LoggerService].debug?.(`cache ${key} miss!`);
5291
- await setMethodCache({
5292
- key,
5293
- clearKey: config.clearKey,
5294
- autoClearTime: config.autoClearTime,
5295
- result
5296
- });
5278
+ const cacheKey = `[cache]${opts.key}`;
5279
+ const cached = await db.get(cacheKey);
5280
+ if (cached !== null) {
5281
+ globalThis[_LoggerService].debug?.(`cache ${opts.key} hit!`);
5282
+ return JSON.parse(cached);
5283
+ }
5284
+ globalThis[_LoggerService].debug?.(`cache ${opts.key} miss!`);
5285
+ const setOpts = {
5286
+ clearKey: opts.clearKey,
5287
+ autoClearTime: opts.autoClearTime,
5288
+ cacheNullValue: opts.cacheNullValue,
5289
+ nullCacheTime: opts.nullCacheTime
5290
+ };
5291
+ // 没有 redlock 配置时直接退化为旧行为(无锁直跑)。
5292
+ const lockDao = globalThis[_dao][DBType.RedisLock];
5293
+ if (!lockDao) {
5294
+ const result = await opts.fn();
5295
+ await setMethodCache({ key: opts.key, ...setOpts, result }, opts.devid);
5297
5296
  return result;
5298
5297
  }
5298
+ return await excuteWithLock({
5299
+ key: opts.key,
5300
+ // 让等待者尽快读到首飞写入的缓存
5301
+ lockWait: true,
5302
+ lockRetryInterval: 100,
5303
+ // 锁 TTL 给一个相对合理的上限,避免首飞挂掉时其它请求永远卡住
5304
+ lockMaxTime: 30000
5305
+ }, async () => {
5306
+ // 锁内二次检查:等待者醒来后必须再读一次缓存,否则会和首飞一样跑一遍原方法。
5307
+ const recheck = await db.get(cacheKey);
5308
+ if (recheck !== null) {
5309
+ globalThis[_LoggerService].debug?.(`cache ${opts.key} hit after lock!`);
5310
+ return JSON.parse(recheck);
5311
+ }
5312
+ const result = await opts.fn();
5313
+ await setMethodCache({ key: opts.key, ...setOpts, result }, opts.devid);
5314
+ return result;
5315
+ });
5316
+ }
5317
+ /**
5318
+ * 执行一个方法fn,
5319
+ * 如果有缓存,则返回缓存,否则【加锁、二次检查】后执行方法并缓存,防止缓存击穿。
5320
+ * 可选 `cacheNullValue=true` 开启负缓存,防穿透;负缓存默认沿用 autoClearTime,可单独用 nullCacheTime 设短。
5321
+ */
5322
+ export async function excuteWithCache(config, fn) {
5323
+ const key = typeof config.key === 'function' ? config.key() : config.key;
5324
+ return await excuteCacheCore({
5325
+ key,
5326
+ clearKey: config.clearKey,
5327
+ autoClearTime: config.autoClearTime,
5328
+ cacheNullValue: config.cacheNullValue,
5329
+ nullCacheTime: config.nullCacheTime,
5330
+ fn
5331
+ });
5299
5332
  }
5300
- /** 缓存注解 */
5333
+ /**
5334
+ * 缓存注解:先查缓存 → miss 才加锁 → 锁内再查一次 → 仍 miss 才执行原方法。
5335
+ * 已内置 single-flight,不需要再叠 `@MethodLock`。
5336
+ * 可选 `cacheNullValue=true` 开启负缓存,防穿透。
5337
+ */
5301
5338
  export function MethodCache(config) {
5302
- return function (target, _propertyKey, descriptor) {
5339
+ return function (_target, _propertyKey, descriptor) {
5303
5340
  const fn = descriptor.value;
5304
5341
  descriptor.value = async function (...args) {
5305
5342
  const key = typeof config.key === 'function' ? config.key.call(this, ...args) : config.key;
5306
- const db = getRedisDB();
5307
- const cache = await db.get(`[cache]${key}`);
5308
- if (cache) {
5309
- globalThis[_LoggerService].debug?.(`cache ${key} hit!`);
5310
- return JSON.parse(cache);
5311
- }
5312
- else {
5313
- globalThis[_LoggerService].debug?.(`cache ${key} miss!`);
5314
- const result = await fn.call(this, ...args);
5315
- const clearKey = config.clearKey ? typeof config.clearKey === 'function' ? config.clearKey.call(this, ...args) : config.clearKey : undefined;
5316
- await setMethodCache({
5317
- key,
5318
- clearKey,
5319
- autoClearTime: config.autoClearTime,
5320
- result
5321
- }, config.clearWithSession && this.ctx.me && this.ctx.me.devid);
5322
- return result;
5323
- }
5343
+ const clearKey = config.clearKey
5344
+ ? (typeof config.clearKey === 'function' ? config.clearKey.call(this, ...args) : config.clearKey)
5345
+ : undefined;
5346
+ const devid = config.clearWithSession && this.ctx && this.ctx.me && this.ctx.me.devid;
5347
+ return await excuteCacheCore({
5348
+ key,
5349
+ clearKey,
5350
+ autoClearTime: config.autoClearTime,
5351
+ cacheNullValue: config.cacheNullValue,
5352
+ nullCacheTime: config.nullCacheTime,
5353
+ devid,
5354
+ fn: async () => await fn.call(this, ...args)
5355
+ });
5324
5356
  };
5325
5357
  };
5326
5358
  }