baja-lite 1.0.2 → 1.0.3

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/src/sql.ts ADDED
@@ -0,0 +1,4021 @@
1
+ import 'reflect-metadata';
2
+ import { Throw } from './error';
3
+ import tslib from 'tslib';
4
+ import Sqlstring from 'sqlstring';
5
+ import iterare from 'iterare';
6
+ import { emptyString } from './string';
7
+ import pino from 'pino';
8
+ import { excuteSplit, ExcuteSplitMode, sleep } from './fn';
9
+ import { add } from './math';
10
+ import mustache, { PartialsOrLookupFn } from 'mustache';
11
+
12
+ // #region 常量
13
+ const _daoDBName = Symbol('dbName');
14
+ const _tableName = Symbol('tableName');
15
+ const _ids = Symbol('ids');
16
+ const _columns = Symbol('columns');
17
+ const _columnsNoId = Symbol('columnsNoId');
18
+ const _fields = Symbol('fields');
19
+ const _stateFileName = Symbol('stateFileName');
20
+ const _deleteState = Symbol('deleteState');
21
+ const _transformer = Symbol('transformer');
22
+ const _index = Symbol('index');
23
+ const _def = Symbol('def');
24
+ const _sqlCache = Symbol('sqlMap');
25
+ const _dao = Symbol('dao');
26
+ const _primaryDB = Symbol('primaryDB');
27
+ const _dbType = Symbol('dbType');
28
+ const _sqlite_version = Symbol('sqlite_version');
29
+ const _daoConnection = Symbol('daoConnection');
30
+ const _inTransaction = Symbol('inTransaction');
31
+ const _daoDB = Symbol('daoDB');
32
+ const _sqliteRemoteName = Symbol('sqliteRemoteName');
33
+ const _SqlOption = Symbol('SqlOption');
34
+ const _GlobalSqlOption = Symbol('GlobalSqlOption');
35
+ const _EventBus = Symbol('EventBus');
36
+ const _path = Symbol('path');
37
+ const _fs = Symbol('fs');
38
+ const logger = pino({
39
+ name: 'sql',
40
+ transport: {
41
+ target: 'pino-pretty'
42
+ }
43
+ });
44
+ // #endregion
45
+
46
+ // #region 可选配置
47
+ export enum DBType { Mysql, Sqlite, Mongo, SqliteRemote, Redis, RedisLock }
48
+ export enum SyncMode {
49
+ /** 同步执行 */
50
+ Sync,
51
+ /** 异步执行 */
52
+ Async
53
+ }
54
+ export enum InsertMode {
55
+ /**
56
+ # 默认使用
57
+ ** 支持单个、批量,语法 `INSERT INTO XX VALUES (第一条数据), (第二条数据);`
58
+ ** 批量执行有性能优势,但无法利用数据库的sql预编译功能
59
+ */
60
+ Insert,
61
+ /**
62
+ # 利用临时表
63
+ ## 执行步骤
64
+ 1. 建立临时表(从正式表复制)
65
+ 2. 数据全部进入临时表
66
+ 3. 临时表数据转移到正式表: `INSERT INTO 正式表 SELECT * FROM 临时表`
67
+ 4. 删除临时表
68
+ ## 注意
69
+ 1. 适用于:主键不会冲突、非自增
70
+ 2. 临时表的结构复制正式表
71
+ */
72
+ InsertWithTempTable,
73
+ InsertIfNotExists,
74
+ /**
75
+ # 插入或者更新
76
+ 1. 判断依据是主键
77
+ */
78
+ Replace
79
+ }
80
+ export enum DeleteMode {
81
+ /**
82
+ ##常规删除 默认
83
+ ### 例一
84
+ `DELETE FROM WHERE (id = 1) OR (id = 2)`
85
+ ### 例二
86
+ `DELETE FROM WHERE (id = 1 AND idx = 11) OR (id = 2 AND idx = 22)`
87
+ */
88
+ Common,
89
+ /*
90
+ ## 借助临时表
91
+ ### 注意:必须保证where的字段都相同,否则会漏删数据
92
+ DELETE FROM 正式表 INNER JOIN 临时表 WHERE 字段1 = 字段1 AND 字段2 = 字段2
93
+ */
94
+ TempTable
95
+ }
96
+ export enum SelectMode {
97
+ /**
98
+ ##常规 默认
99
+ ### 例一
100
+ `SELECT * FROM WHERE (id = 1) OR (id = 2)`
101
+ ### 例二
102
+ `SELECT * FROM WHERE (id = 1 AND idx = 11) OR (id = 2 AND idx = 22)`
103
+ */
104
+ Common,
105
+ /*
106
+ ## 借助临时表
107
+ ### 注意:必须保证where的字段都相同,否则会漏删数据
108
+ SELECT * FROM 正式表 INNER JOIN 临时表 WHERE 字段1 = 字段1 AND 字段2 = 字段2
109
+ */
110
+ TempTable
111
+ }
112
+ export enum TemplateResult {
113
+ /** 确定返回一条记录,如果不是一个,将报错,返回类型是T */
114
+ AssertOne,
115
+ /** 可能返回一条记录,返回类型是T|null */
116
+ NotSureOne,
117
+ /** 返回多条记录 */
118
+ Many,
119
+ /** 仅查询记录数量 */
120
+ Count
121
+ }
122
+ export enum SelectResult {
123
+ One_Row_One_Column_Assert,
124
+ One_Row_One_Column_NotSure,
125
+ One_Row_Many_Column_Assert,
126
+ One_Row_Many_Column_NotSure,
127
+ Many_Row_One_Column,
128
+ Many_Row_Many_Column
129
+ }
130
+ export enum SqlType {
131
+ tinyint,
132
+ smallint,
133
+ mediumint,
134
+ int,
135
+ bigint,
136
+
137
+ float,
138
+ double,
139
+ decimal,
140
+
141
+
142
+ date,
143
+ time,
144
+ year,
145
+ datetime,
146
+ timestamp,
147
+
148
+ char,
149
+ varchar,
150
+ tinyblob,
151
+ tinytext,
152
+ blob,
153
+ text,
154
+ mediumblob,
155
+ mediumtext,
156
+ longblob,
157
+ longtext,
158
+
159
+ set,
160
+ enum,
161
+
162
+ json,
163
+
164
+ geometry,
165
+ point,
166
+ linestring,
167
+ polygon,
168
+ multipoint,
169
+ multilinestring,
170
+ multipolygon,
171
+ geometrycollection
172
+
173
+ }
174
+ export const SqliteMemory = ':memory:';
175
+ // #endregion
176
+
177
+ // #region 选项
178
+ interface MethodOption {
179
+ tableName?: string;
180
+ /** 数据库、连接名称,对于MYSQL、mongo,适用于多数据源,对于sqlite,适用于不同的数据库文件 */
181
+ dbName?: string;
182
+ /** 调用时,永远不需要传
183
+ * @deprecated
184
+ * */
185
+ dao?: Dao;
186
+ /** 调用时,仅在开启事务时需要主动传入,传入方式: */
187
+ conn?: Connection | null;
188
+ }
189
+ const _defOption = {
190
+ maxDeal: 500,
191
+ skipUndefined: true,
192
+ skipNull: true,
193
+ skipEmptyString: true
194
+ };
195
+ /**
196
+ 数据服务注解
197
+ */
198
+ interface ServiceOption {
199
+ /** 增改忽略Undefined */
200
+ skipUndefined?: boolean;
201
+ /** 增改忽略NULL */
202
+ skipNull?: boolean;
203
+ /** 增改忽略空字符串 */
204
+ skipEmptyString?: boolean;
205
+ /** 批量增改时,每次执行最多处理的记录数量 */
206
+ maxDeal?: number;
207
+ tableName?: string;
208
+ /** 数据库、连接名称,对于MYSQL、mongo,适用于多数据源,对于sqlite,适用于不同的数据库文件 */
209
+ dbName?: string;
210
+ /** 调用时,永远不需要传
211
+ * @deprecated
212
+ * */
213
+ dao?: Dao;
214
+ /** 调用时,仅在开启事务时需要主动传入,传入方式: */
215
+ conn?: Connection | null;
216
+ /** 对应的实体类,必须是Class */
217
+ clz?: any;
218
+ /** 默认mysql */
219
+ dbType?: DBType;
220
+ /** SQLite版本以及升级为该版本时需要执行的SQL,初始版本为0.0.1,切记每个位置不要变为两位数*/
221
+ sqliteVersion?: string;
222
+ }
223
+ /**
224
+ # 全局行为配置文件
225
+ MYSQL编码: 'utf8mb4', utf8mb4_general_ci'
226
+ ### `sqlDir?: string;` 数据库查询语句存放目录.存放格式为 export.default 的js、ts, 存放内容满足格式:
227
+
228
+ ```
229
+ interface SqlModel {
230
+ [key: string]: string | ((params: { [k: string]: any }, context: any, isCount?: boolean) => string)
231
+ }
232
+ ```
233
+ 可以继承该接口来约束格式
234
+ */
235
+ interface GlobalSqlOption {
236
+ /** 增改忽略Undefined */
237
+ skipUndefined?: boolean;
238
+ /** 增改忽略NULL */
239
+ skipNull?: boolean;
240
+ /** 增改忽略空字符串 */
241
+ skipEmptyString?: boolean;
242
+ /** 批量增改时,每次执行最多处理的记录数量 */
243
+ maxDeal?: number;
244
+ /**
245
+ 初始化MYSQL链接 支持多数据源
246
+ ## 单一数据源: 直接传入Mysql2的连接配置
247
+ [MYSQL初始化文档](https://github.com/mysqljs/mysql#connection-options)
248
+ ```
249
+ Mysql: {
250
+ host: '127.0.0.1',
251
+ ...
252
+ }
253
+ ```
254
+ ## 多数据源:传入多个Mysql2的连接配置
255
+ ```
256
+ Mysql: {
257
+ db1: {
258
+ host: '127.0.0.1',
259
+ ...
260
+ },
261
+ db2: {
262
+ host: '127.0.0.1',
263
+ ...
264
+ },
265
+ ...
266
+ }
267
+ ```
268
+ */
269
+ Mysql?: Record<string, Record<string, any>> | Record<string, any>;
270
+ /**
271
+ ## 单一数据源
272
+ ```
273
+ Sqlite: 'd:/1.db'
274
+ ```
275
+ ## 多数据源:传入多个Mysql2的连接配置
276
+ ```
277
+ Sqlite: {
278
+ db1: 'd:/1.db',
279
+ db2: 'd:/2.db'
280
+ }
281
+ ```
282
+ 路径 = `SqliteMemory` 将创建内存库
283
+ */
284
+ Sqlite?: Record<string, string> | string,
285
+ SqliteRemote?: {
286
+ /**
287
+ ## 单一数据源
288
+ ```
289
+ db: 'd:/1.db'
290
+ ```
291
+ ## 多数据源:传入多个Mysql2的连接配置
292
+ ```
293
+ db: {
294
+ db1: 'd:/1.db',
295
+ db2: 'd:/2.db'
296
+ }
297
+ ```
298
+ 不支持 `SqliteMemory`
299
+ */
300
+ db: Record<string, string> | string,
301
+ /** 远程SQLITE接口实现,适用于Electron, 采用Ipc 的handel机制实现 */
302
+ service: SqliteRemoteInterface
303
+ },
304
+ /** 日志等级 */
305
+ log?: 'trace' | 'debug' | 'info' | 'warn',
306
+ /**
307
+ ## 日志文件存放路径,该目录下文件名是模块名,例如有一个文件名为 `user.js`,内容为:
308
+ ```
309
+ export default {
310
+ 'sql_1': 'SELECT * FROM user WHERE username = :username',
311
+ 'sql_2': (options: {
312
+ ctx: any;
313
+ isCount?: boolean;
314
+ limitStart?: number;
315
+ limitEnd?: number;
316
+ orderBy?: string;
317
+ [k: string]: any;
318
+ }) => {
319
+ return `
320
+ SELECT * FROM user u LEFT JOIN organ o ON u.orgid = o.orgid
321
+ WHERE o.orgid = :orgid;
322
+ `;
323
+ }
324
+ } as SqlModel;
325
+ ```
326
+ ** 可以看到,sql语句支持直接映射一个sql语句,也可以通过函数返回,返回字符串支持[mustache](https://github.com/janl/mustache.js)
327
+ ** 上面的文件中,将注册两个SQL:`user.sql_1` 和 `user.sql_2`.
328
+ ** `[k: string]: any;` 是用查询时传入的参数,可以指定为任意类型,可以用来生成sql,例如进行循环语句
329
+ ** ctx 是框架的上下文,可以自行指定类型
330
+ ** 其他 是保留参数
331
+ ** 函数类型中,可以调用自己定义的通用sql
332
+ ### 注意
333
+ 1. 不要直接拼接参数:不安全且效率低
334
+ 2. sqlite不支持多语句拼接
335
+ ## 也支持.mu文件,格式略
336
+ */
337
+ sqlDir?: string;
338
+ /**
339
+ 作用与sqlDir类似,不同在于sqlMap`不需要`目录,而是直接指定一个sqlModel对象,对象的格式和sqlDir的文件内容一样。
340
+ ** 适用于简单使用
341
+ */
342
+ sqlMap?: SqlModel;
343
+ /**
344
+ ## [mustache](https://mustache.github.io/) 编译时的[模板](https://github.com/janl/mustache.js#:~:text=requires%20only%20this%3A-,%7B%7B%3E%20next_more%7D%7D,-Why%3F%20Because%20the)
345
+ ** 文件名就是模板名
346
+ */
347
+ sqlFNDir?: string;
348
+ /**
349
+ 作用与sqlFnDir类似,不同在于sqlFNMap`不需要`目录,而是直接指定一个 Record<string, string>,对象的格式和sqlFnDir的文件内容一样。
350
+ ** 适用于简单使用
351
+ */
352
+ sqlFNMap?: Record<string, string>;
353
+
354
+ /**
355
+ [REDIS初始化文档](https://github.com/redis/ioredis?tab=readme-ov-file#:~:text=connect%20to%20by%3A-,new%20Redis()%3B,-//%20Connect%20to%20127.0.0.1)
356
+ ```
357
+ Redis: {
358
+ host: '127.0.0.1',
359
+ ...
360
+ }
361
+ ```
362
+ ## 多数据源:传入多个Redis的连接配置
363
+ ```
364
+ Redis: {
365
+ db1: {
366
+ host: '127.0.0.1',
367
+ ...
368
+ },
369
+ db2: {
370
+ host: '127.0.0.1',
371
+ ...
372
+ },
373
+ ...
374
+ }
375
+ ```
376
+ */
377
+ Redis?: Record<string, Record<string, any>> | Record<string, any>;
378
+ }
379
+ interface FieldOption extends Object {
380
+ type?: SqlType;
381
+ name?: string;
382
+ length?: number;
383
+ scale?: number;
384
+ def?: any;
385
+ index?: boolean;
386
+ id?: boolean;
387
+ logicDelete?: string;
388
+ notNull?: boolean;
389
+ }
390
+ interface AField extends FieldOption {
391
+ esName: string;
392
+ [DBType.Mysql]: string;
393
+ [DBType.Sqlite]: string;
394
+ [DBType.SqliteRemote]: string;
395
+ }
396
+ // #endregion
397
+
398
+ // #region 数据方言
399
+ /** sqlite electron服务端需要支持的接口 */
400
+ export interface SqliteRemoteInterface {
401
+ execute(dbName: string, sql?: string, params?: any): | Promise<{ affectedRows: number; insertId: bigint; }>
402
+ pluck<One_Row_Many_Column = any>(dbName: string, sql?: string, params?: any): Promise<One_Row_Many_Column | undefined>;
403
+ get<One_Row_Many_Column = any>(dbName: string, sql?: string, params?: any): Promise<One_Row_Many_Column | undefined>;
404
+ raw<Many_Row_One_Column = any>(dbName: string, sql?: string, params?: any): Promise<Many_Row_One_Column[]>;
405
+ query<Many_Row_Many_Column = any>(dbName: string, sql?: string, params?: any): Promise<Many_Row_Many_Column[]>;
406
+
407
+ initDB(dbName: string): Promise<void>;
408
+ backup(dbName: string, name: string): Promise<void>;
409
+ restore(dbName: string, name: string): Promise<void>;
410
+ };
411
+ interface Connection {
412
+ [_daoConnection]: any;
413
+ [_inTransaction]: boolean;
414
+ execute(sync: SyncMode.Sync, sql?: string, params?: any): { affectedRows: number; insertId: bigint; };
415
+ execute(sync: SyncMode.Async, sql?: string, params?: any): | Promise<{ affectedRows: number; insertId: bigint; }>;
416
+ /** 一行一列 */
417
+ pluck<One_Row_One_Column = any>(sync: SyncMode.Sync, sql?: string, params?: any): One_Row_One_Column | null;
418
+ pluck<One_Row_One_Column = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<One_Row_One_Column | null>;
419
+ /** 一行多列 */
420
+ get<One_Row_Many_Column = any>(sync: SyncMode.Sync, sql?: string, params?: any): One_Row_Many_Column | null;
421
+ get<One_Row_Many_Column = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<One_Row_Many_Column | null>;
422
+ /** 多行一列 */
423
+ raw<Many_Row_One_Column = any>(sync: SyncMode.Sync, sql?: string, params?: any): Many_Row_One_Column[];
424
+ raw<Many_Row_One_Column = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<Many_Row_One_Column[]>;
425
+ /** 多行多列 */
426
+ query<Many_Row_Many_Column = any>(sync: SyncMode.Sync, sql?: string, params?: any): Many_Row_Many_Column[];
427
+ query<Many_Row_Many_Column = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<Many_Row_Many_Column[]>;
428
+ realse(sync: SyncMode.Sync): void;
429
+ realse(sync: SyncMode.Async): Promise<void>;
430
+ }
431
+ interface Dao {
432
+ [_daoDB]: any;
433
+ transaction<T = any>(sync: SyncMode.Sync, fn: (conn: Connection) => T, conn?: Connection | null): T | null;
434
+ transaction<T = any>(sync: SyncMode.Async, fn: (conn: Connection) => Promise<T>, conn?: Connection | null): Promise<T | null>;
435
+ createConnection(sync: SyncMode.Sync): Connection | null;
436
+ createConnection(sync: SyncMode.Async): Promise<Connection | null>;
437
+ close(sync: SyncMode.Sync): void;
438
+ close(sync: SyncMode.Async): Promise<void>;
439
+ backup(sync: SyncMode.Sync, name: string): void;
440
+ backup(sync: SyncMode.Async, name: string): Promise<void>;
441
+ remove(sync: SyncMode.Sync): void;
442
+ remove(sync: SyncMode.Async): Promise<void>;
443
+ restore(sync: SyncMode.Sync, name: string): void;
444
+ restore(sync: SyncMode.Async, name: string): Promise<void>;
445
+ }
446
+ class MysqlConnection implements Connection {
447
+ [_daoConnection]: any;
448
+ [_inTransaction] = false;
449
+ constructor(conn: any) {
450
+ this[_daoConnection] = conn;
451
+ }
452
+
453
+ execute(sync: SyncMode.Sync, sql?: string, params?: any): { affectedRows: number; insertId: bigint; };
454
+ execute(sync: SyncMode.Async, sql?: string, params?: any): Promise<{ affectedRows: number; insertId: bigint; }>;
455
+ execute(sync: SyncMode, sql?: string, params?: any): { affectedRows: number; insertId: bigint; } | Promise<{ affectedRows: number; insertId: bigint; }> {
456
+ logger.debug(sql, params ?? '');
457
+ if (!sql) { return { affectedRows: 0, insertId: 0n }; };
458
+ if (sync === SyncMode.Sync) {
459
+ logger.warn('MYSQL not suppouted sync mode');
460
+ return { affectedRows: 0, insertId: 0n };
461
+ };
462
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
463
+ logger.trace(Sqlstring.format(sql!, params));
464
+ }
465
+ return new Promise<{ affectedRows: number; insertId: bigint; }>(async (resolve, reject) => {
466
+ try {
467
+ const [_result] = await this[_daoConnection].execute(sql, params);
468
+ const result = _result as any;
469
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
470
+ logger.trace(result);
471
+ }
472
+ resolve({ affectedRows: result.affectedRows, insertId: result.insertId });
473
+ } catch (error) {
474
+ logger.error(`
475
+ error: ${error},
476
+ sql: ${sql},
477
+ params: ${params}
478
+ `);
479
+ reject(error);
480
+ }
481
+ });
482
+ }
483
+
484
+ pluck<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T | null;
485
+ pluck<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T | null>;
486
+ pluck<T = any>(sync: SyncMode, sql?: string, params?: any): T | null | Promise<T | null> {
487
+ logger.debug(sql, params ?? '');
488
+ if (!sql) { return null };
489
+ if (sync === SyncMode.Sync) {
490
+ logger.warn('MYSQL not suppouted sync mode');
491
+ return null;
492
+ };
493
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
494
+ logger.trace(Sqlstring.format(sql!, params));
495
+ }
496
+ return new Promise<T | null>(async (resolve, reject) => {
497
+ try {
498
+ const [result] = await this[_daoConnection].query(sql, params);
499
+ if (result && result[0]) {
500
+ const r = Object.values(result[0])[0];
501
+ if (r === null) resolve(r);
502
+ else resolve(r as T);
503
+ }
504
+ resolve(null);
505
+ } catch (error) {
506
+ logger.error(`
507
+ error: ${error},
508
+ sql: ${sql},
509
+ params: ${params}
510
+ `);
511
+ reject(error);
512
+ }
513
+ });
514
+ }
515
+
516
+ get<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T | null;
517
+ get<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T | null>;
518
+ get<T = any>(sync: SyncMode, sql?: string, params?: any): T | null | Promise<T | null> {
519
+ logger.debug(sql, params ?? '');
520
+ if (!sql) { return null };
521
+ if (sync === SyncMode.Sync) {
522
+ logger.warn('MYSQL not suppouted sync mode');
523
+ return null;
524
+ };
525
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
526
+ logger.trace(Sqlstring.format(sql!, params));
527
+ }
528
+ return new Promise<T | null>(async (resolve, reject) => {
529
+ try {
530
+ const [result] = await this[_daoConnection].query(sql, params);
531
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
532
+ logger.trace(result);
533
+ }
534
+ if (result && result[0]) resolve(result[0] as T);
535
+ resolve(null);
536
+ } catch (error) {
537
+ logger.error(`
538
+ error: ${error},
539
+ sql: ${sql},
540
+ params: ${params}
541
+ `);
542
+ reject(error);
543
+ }
544
+ });
545
+ }
546
+
547
+ raw<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T[];
548
+ raw<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T[]>;
549
+ raw<T = any>(sync: SyncMode, sql?: string, params?: any): T[] | Promise<T[]> {
550
+ logger.debug(sql, params ?? '');
551
+ if (!sql) { return []; };
552
+ if (sync === SyncMode.Sync) {
553
+ logger.warn('MYSQL not suppouted sync mode');
554
+ return [];
555
+ };
556
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
557
+ logger.trace(Sqlstring.format(sql!, params));
558
+ }
559
+ return new Promise<T[]>(async (resolve, reject) => {
560
+ try {
561
+ const [result] = await this[_daoConnection].query(sql, params);
562
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
563
+ logger.trace(result);
564
+ }
565
+ if (result) resolve(result.map((i: any) => Object.values(i)[0]));
566
+ resolve([]);
567
+ } catch (error) {
568
+ logger.error(`
569
+ error: ${error},
570
+ sql: ${sql},
571
+ params: ${params}
572
+ `);
573
+ reject(error);
574
+ }
575
+ });
576
+ }
577
+
578
+ query<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T[];
579
+ query<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T[]>;
580
+ query<T = any>(sync: SyncMode, sql?: string, params?: any): T[] | Promise<T[]> {
581
+ logger.debug(sql, params ?? '');
582
+ if (!sql) { return []; };
583
+ if (sync === SyncMode.Sync) {
584
+ logger.warn('MYSQL not suppouted sync mode');
585
+ return [];
586
+ };
587
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
588
+ logger.trace(Sqlstring.format(sql!, params));
589
+ }
590
+ return new Promise<T[]>(async (resolve, reject) => {
591
+ try {
592
+ const [result] = await this[_daoConnection].query(sql, params);
593
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
594
+ logger.trace(result);
595
+ }
596
+ resolve(result);
597
+ } catch (error) {
598
+ logger.error(`
599
+ error: ${error},
600
+ sql: ${sql},
601
+ params: ${params}
602
+ `);
603
+ reject(error);
604
+ }
605
+ });
606
+ }
607
+
608
+ realse(sync: SyncMode.Sync): void;
609
+ realse(sync: SyncMode.Async): Promise<void>;
610
+ realse(sync: SyncMode): Promise<void> | void {
611
+ if (sync === SyncMode.Sync) {
612
+ try {
613
+ this[_daoConnection]?.release();
614
+ } catch (error) {
615
+
616
+ }
617
+ };
618
+ }
619
+ }
620
+ class Mysql implements Dao {
621
+ [_daoDB]: any;
622
+ constructor(pool: any) {
623
+ this[_daoDB] = pool;
624
+ }
625
+
626
+ createConnection(sync: SyncMode.Sync): Connection | null;
627
+ createConnection(sync: SyncMode.Async): Promise<Connection | null>;
628
+ createConnection(sync: SyncMode): Connection | null | Promise<Connection | null> {
629
+ if (sync === SyncMode.Sync) {
630
+ logger.error('MYSQL not suppouted sync mode');
631
+ return null;
632
+ };
633
+ return new Promise<Connection>(async (resolve, reject) => {
634
+ try {
635
+ const connection = await this[_daoDB].getConnection();
636
+ logger.debug('create new!');
637
+ resolve(new MysqlConnection(connection));
638
+ } catch (error) {
639
+ reject(error);
640
+ }
641
+ });
642
+ }
643
+
644
+ transaction<T = any>(sync: SyncMode.Sync, fn: (conn: Connection) => T, conn?: Connection | null): T | null;
645
+ transaction<T = any>(sync: SyncMode.Async, fn: (conn: Connection) => Promise<T>, conn?: Connection | null): Promise<T | null>;
646
+ transaction<T = any>(sync: SyncMode, fn: (conn: Connection) => T | Promise<T>, conn?: Connection | null): T | null | Promise<T | null> {
647
+ if (sync === SyncMode.Sync) {
648
+ logger.warn('MYSQL not suppouted sync mode');
649
+ return null;
650
+ };
651
+ return new Promise<T>(async (resolve, reject) => {
652
+ let needCommit = false;
653
+ let newConn = false;
654
+ if (!conn) {
655
+ conn = await this.createConnection(SyncMode.Async) ?? undefined;
656
+ newConn = true;
657
+ }
658
+ if (conn?.[_inTransaction] !== true) {
659
+ needCommit = true;
660
+ logger.debug('beginTransaction begin!');
661
+ await conn![_daoConnection].beginTransaction();
662
+ logger.debug('beginTransaction end!');
663
+ }
664
+ conn![_inTransaction] = true;
665
+ try {
666
+ const result = await fn(conn!);
667
+ if (needCommit === true) {
668
+ logger.debug('commit begin!');
669
+ await conn![_daoConnection].commit();
670
+ conn![_inTransaction] = false;
671
+ logger.debug('commit end!');
672
+ }
673
+ resolve(result);
674
+ } catch (error) {
675
+ logger.debug('rollback begin!');
676
+ await conn![_daoConnection].rollback();
677
+ logger.debug('rollback end!');
678
+ conn![_inTransaction] = false;
679
+ logger.error(error);
680
+ reject(error);
681
+ } finally {
682
+ try {
683
+ if (needCommit === true) {
684
+ conn![_inTransaction] = false;
685
+ }
686
+ if (newConn === true) {
687
+ logger.debug('release begin!');
688
+ conn![_daoConnection].release();
689
+ logger.debug('release end!');
690
+ }
691
+ } catch (error) {
692
+ }
693
+ }
694
+ });
695
+ }
696
+
697
+ close(sync: SyncMode.Sync): void;
698
+ close(sync: SyncMode.Async): Promise<void>;
699
+ close(sync: SyncMode): Promise<void> | void {
700
+ if (sync === SyncMode.Sync) {
701
+ this[_daoDB]?.destroy();
702
+ };
703
+ }
704
+
705
+ backup(sync: SyncMode.Sync, name: string): void;
706
+ backup(sync: SyncMode.Async, name: string): Promise<void>;
707
+ backup(sync: SyncMode, name: string): Promise<void> | void {
708
+ }
709
+
710
+ remove(sync: SyncMode.Sync): void;
711
+ remove(sync: SyncMode.Async): Promise<void>;
712
+ remove(sync: SyncMode): Promise<void> | void {
713
+ }
714
+
715
+ restore(sync: SyncMode.Sync, name: string): void;
716
+ restore(sync: SyncMode.Async, name: string): Promise<void>;
717
+ restore(sync: SyncMode, name: string): Promise<void> | void {
718
+ }
719
+ }
720
+ class SqliteConnection implements Connection {
721
+ [_daoConnection]: any;
722
+ [_inTransaction] = false;
723
+ constructor(conn: any) {
724
+ this[_daoConnection] = conn;
725
+ }
726
+
727
+ execute(sync: SyncMode.Sync, sql?: string, params?: any): { affectedRows: number; insertId: bigint; };
728
+ execute(sync: SyncMode.Async, sql?: string, params?: any): Promise<{ affectedRows: number; insertId: bigint; }>;
729
+ execute(sync: SyncMode, sql?: string, params?: any): { affectedRows: number; insertId: bigint; } | Promise<{ affectedRows: number; insertId: bigint; }> {
730
+ try {
731
+ logger.debug(sql, params ?? '');
732
+ if (!sql) { return { affectedRows: 0, insertId: 0n }; };
733
+ if (sync === SyncMode.Async) {
734
+ logger.warn(`SQLITE not suppoted async mode`);
735
+ return { affectedRows: 0, insertId: 0n };
736
+ };
737
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
738
+ logger.trace(Sqlstring.format(sql!, params));
739
+ }
740
+ const result = this[_daoConnection].prepare(sql).run(params ?? {});
741
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
742
+ logger.trace(result);
743
+ }
744
+ const { changes, lastInsertRowid } = result;
745
+ return { affectedRows: changes, insertId: lastInsertRowid ? BigInt(lastInsertRowid) : 0n };
746
+ } catch (error) {
747
+ logger.error(`
748
+ error: ${error},
749
+ sql: ${sql},
750
+ params: ${params}
751
+ `);
752
+ throw error;
753
+ }
754
+ }
755
+
756
+ pluck<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T | null;
757
+ pluck<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T | null>;
758
+ pluck<T = any>(sync: SyncMode, sql?: string, params?: any): T | null | Promise<T | null> {
759
+ try {
760
+ logger.debug(sql, params ?? '');
761
+ if (!sql) { return null };
762
+ if (sync === SyncMode.Async) {
763
+ logger.warn(`SQLITE not suppoted async mode`);
764
+ return null;
765
+ };
766
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
767
+ logger.trace(Sqlstring.format(sql!, params));
768
+ }
769
+ return this[_daoConnection].prepare(sql).pluck().get(params ?? {});
770
+ } catch (error) {
771
+ logger.error(`
772
+ error: ${error},
773
+ sql: ${sql},
774
+ params: ${params}
775
+ `);
776
+ throw error;
777
+ }
778
+ }
779
+
780
+ get<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T | null;
781
+ get<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T | null>;
782
+ get<T = any>(sync: SyncMode, sql?: string, params?: any): T | null | Promise<T | null> {
783
+ try {
784
+ logger.debug(sql, params ?? '');
785
+ if (!sql) { return null };
786
+ if (sync === SyncMode.Async) { return null };
787
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
788
+ logger.trace(Sqlstring.format(sql!, params));
789
+ }
790
+ return this[_daoConnection].prepare(sql).get(params ?? {});
791
+ } catch (error) {
792
+ logger.error(`
793
+ error: ${error},
794
+ sql: ${sql},
795
+ params: ${params}
796
+ `);
797
+ throw error;
798
+ }
799
+ }
800
+
801
+ raw<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T[];
802
+ raw<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T[]>;
803
+ raw<T = any>(sync: SyncMode, sql?: string, params?: any): T[] | Promise<T[]> {
804
+ try {
805
+ logger.debug(sql, params ?? '');
806
+ if (!sql) { return []; };
807
+ if (sync === SyncMode.Async) {
808
+ logger.warn(`SQLITE not suppoted async mode`);
809
+ return [];
810
+ };
811
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
812
+ logger.trace(Sqlstring.format(sql!, params));
813
+ }
814
+ return this[_daoConnection].prepare(sql).raw().all(params ?? {});
815
+ } catch (error) {
816
+ logger.error(`
817
+ error: ${error},
818
+ sql: ${sql},
819
+ params: ${params}
820
+ `);
821
+ throw error;
822
+ }
823
+ }
824
+
825
+ query<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T[];
826
+ query<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T[]>;
827
+ query<T = any>(sync: SyncMode, sql?: string, params?: any): T[] | Promise<T[]> {
828
+ try {
829
+ logger.debug(sql, params ?? '');
830
+ if (!sql) { return []; };
831
+ if (sync === SyncMode.Async) {
832
+ logger.warn(`SQLITE not suppoted async mode`);
833
+ return [];
834
+ };
835
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
836
+ logger.trace(Sqlstring.format(sql!, params));
837
+ }
838
+ return this[_daoConnection].prepare(sql).all(params ?? {});
839
+ } catch (error) {
840
+ logger.error(`
841
+ error: ${error},
842
+ sql: ${sql},
843
+ params: ${params}
844
+ `);
845
+ throw error;
846
+ }
847
+ }
848
+
849
+ realse(sync: SyncMode.Sync): void;
850
+ realse(sync: SyncMode.Async): Promise<void>;
851
+ realse(sync: SyncMode): Promise<void> | void {
852
+ }
853
+ }
854
+ class Sqlite implements Dao {
855
+ [_daoDB]: any;
856
+ constructor(db: any) {
857
+ this[_daoDB] = db;
858
+ this[_daoDB].pragma('journal_mode = WAL');
859
+ this[_daoDB].exec(`
860
+ CREATE TABLE IF NOT EXISTS DUAL ( ______id INTEGER NOT NULL, PRIMARY KEY ( ______id ));
861
+ DELETE FROM DUAL;
862
+ INSERT INTO DUAL (______id ) VALUES ( 1 );
863
+ CREATE TABLE IF NOT EXISTS TABLE_VERSION (
864
+ ______tableName text NOT NULL,
865
+ ______version text NOT NULL,
866
+ PRIMARY KEY ( ______tableName )
867
+ );
868
+ `);
869
+ }
870
+
871
+ createConnection(sync: SyncMode.Sync): Connection | null;
872
+ createConnection(sync: SyncMode.Async): Promise<Connection | null>;
873
+ createConnection(sync: SyncMode): Connection | null | Promise<Connection | null> {
874
+ if (sync === SyncMode.Async) {
875
+ logger.error(`SQLITE not suppoted async mode`);
876
+ return null;
877
+ };
878
+ return new SqliteConnection(this[_daoDB]);
879
+ }
880
+
881
+ transaction<T = any>(sync: SyncMode.Sync, fn: (conn: Connection) => T, conn?: Connection | null): T | null;
882
+ transaction<T = any>(sync: SyncMode.Async, fn: (conn: Connection) => Promise<T>, conn?: Connection | null): Promise<T | null>;
883
+ transaction<T = any>(sync: SyncMode, fn: (conn: Connection) => T | Promise<T>, conn?: Connection | null): T | null | Promise<T | null> {
884
+ if (sync === SyncMode.Async) {
885
+ logger.warn(`SQLITE not suppoted async mode`);
886
+ return null;
887
+ };
888
+ if (!conn) {
889
+ conn = this.createConnection(SyncMode.Sync) ?? undefined;
890
+ }
891
+ if (conn![_inTransaction] !== true) {
892
+ return this[_daoDB].transaction(() => {
893
+ conn![_inTransaction] = true;
894
+ const rt = fn(conn!);
895
+ conn![_inTransaction] = false;
896
+ return rt;
897
+ })();
898
+ } else {
899
+ const rt = fn(conn!);
900
+ return rt;
901
+ }
902
+ }
903
+
904
+
905
+
906
+ close(sync: SyncMode.Sync): void;
907
+ close(sync: SyncMode.Async): Promise<void>;
908
+ close(sync: SyncMode): Promise<void> | void {
909
+ if (sync === SyncMode.Sync) {
910
+ this[_daoDB].close();
911
+ };
912
+ }
913
+
914
+ backup(sync: SyncMode.Sync, name: string): void;
915
+ backup(sync: SyncMode.Async, name: string): Promise<void>;
916
+ backup(sync: SyncMode, name: string): Promise<void> | void {
917
+ if (sync === SyncMode.Sync) {
918
+ this[_daoDB].backup(name);
919
+ };
920
+ }
921
+
922
+ remove(sync: SyncMode.Sync): void;
923
+ remove(sync: SyncMode.Async): Promise<void>;
924
+ remove(sync: SyncMode): Promise<void> | void {
925
+ }
926
+
927
+ restore(sync: SyncMode.Sync, name: string): void;
928
+ restore(sync: SyncMode.Async, name: string): Promise<void>;
929
+ restore(sync: SyncMode, name: string): Promise<void> | void {
930
+ }
931
+ }
932
+ class SqliteRemoteConnection implements Connection {
933
+ [_daoConnection]: SqliteRemoteInterface;
934
+ [_sqliteRemoteName]: string;
935
+ [_inTransaction] = false;
936
+ constructor(conn: SqliteRemoteInterface, name: string) {
937
+ this[_daoConnection] = conn;
938
+ this[_sqliteRemoteName] = name;
939
+ }
940
+
941
+ execute(sync: SyncMode.Sync, sql?: string, params?: any): { affectedRows: number; insertId: bigint; };
942
+ execute(sync: SyncMode.Async, sql?: string, params?: any): Promise<{ affectedRows: number; insertId: bigint; }>;
943
+ execute(sync: SyncMode, sql?: string, params?: any): { affectedRows: number; insertId: bigint; } | Promise<{ affectedRows: number; insertId: bigint; }> {
944
+ logger.debug(sql, params ?? '');
945
+ if (!sql) { return { affectedRows: 0, insertId: 0n }; };
946
+ if (sync === SyncMode.Sync) {
947
+ logger.warn('SqliteRemote not suppouted sync mode');
948
+ return { affectedRows: 0, insertId: 0n };
949
+ };
950
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
951
+ logger.trace(Sqlstring.format(sql!, params));
952
+ }
953
+ return new Promise<{ affectedRows: number; insertId: bigint; }>(async (resolve, reject) => {
954
+ try {
955
+ const { affectedRows, insertId } = await this[_daoConnection].execute(this[_sqliteRemoteName], sql, params);
956
+ resolve({ affectedRows, insertId: insertId ? BigInt(insertId) : 0n });
957
+ } catch (error) {
958
+ logger.error(`
959
+ error: ${error},
960
+ sql: ${sql},
961
+ params: ${params}
962
+ `);
963
+ reject(error);
964
+ }
965
+ });
966
+ }
967
+
968
+ pluck<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T | null;
969
+ pluck<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T | null>;
970
+ pluck<T = any>(sync: SyncMode, sql?: string, params?: any): T | null | Promise<T | null> {
971
+ logger.debug(sql, params ?? '');
972
+ if (!sql) { return null };
973
+ if (sync === SyncMode.Sync) {
974
+ logger.warn('SqliteRemote not suppouted sync mode');
975
+ return null;
976
+ };
977
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
978
+ logger.trace(Sqlstring.format(sql!, params));
979
+ }
980
+ return new Promise<T | null>(async (resolve, reject) => {
981
+ try {
982
+ const r = await this[_daoConnection].pluck(this[_sqliteRemoteName], sql, params);
983
+ resolve(r);
984
+ } catch (error) {
985
+ logger.error(`
986
+ error: ${error},
987
+ sql: ${sql},
988
+ params: ${params}
989
+ `);
990
+ reject(error);
991
+ }
992
+ });
993
+ }
994
+
995
+ get<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T | null;
996
+ get<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T | null>;
997
+ get<T = any>(sync: SyncMode, sql?: string, params?: any): T | null | Promise<T | null> {
998
+ logger.debug(sql, params ?? '');
999
+ if (!sql) { return null };
1000
+ if (sync === SyncMode.Sync) {
1001
+ logger.warn('SqliteRemote not suppouted sync mode');
1002
+ return null;
1003
+ };
1004
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
1005
+ logger.trace(Sqlstring.format(sql!, params));
1006
+ }
1007
+ return new Promise<T | null>(async (resolve, reject) => {
1008
+ try {
1009
+ const r = await this[_daoConnection].get(this[_sqliteRemoteName], sql, params);
1010
+ resolve(r);
1011
+ } catch (error) {
1012
+ logger.error(`
1013
+ error: ${error},
1014
+ sql: ${sql},
1015
+ params: ${params}
1016
+ `);
1017
+ reject(error);
1018
+ }
1019
+ });
1020
+ }
1021
+
1022
+ raw<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T[];
1023
+ raw<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T[]>;
1024
+ raw<T = any>(sync: SyncMode, sql?: string, params?: any): T[] | Promise<T[]> {
1025
+ logger.debug(sql, params ?? '');
1026
+ if (!sql) { return []; };
1027
+ if (sync === SyncMode.Sync) {
1028
+ logger.warn('SqliteRemote not suppouted sync mode');
1029
+ return [];
1030
+ };
1031
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
1032
+ logger.trace(Sqlstring.format(sql!, params));
1033
+ }
1034
+ return new Promise<T[]>(async (resolve, reject) => {
1035
+ try {
1036
+ const r = await this[_daoConnection].raw(this[_sqliteRemoteName], sql, params);
1037
+ resolve(r);
1038
+ } catch (error) {
1039
+ logger.error(`
1040
+ error: ${error},
1041
+ sql: ${sql},
1042
+ params: ${params}
1043
+ `);
1044
+ reject(error);
1045
+ }
1046
+ });
1047
+ }
1048
+
1049
+ query<T = any>(sync: SyncMode.Sync, sql?: string, params?: any): T[];
1050
+ query<T = any>(sync: SyncMode.Async, sql?: string, params?: any): Promise<T[]>;
1051
+ query<T = any>(sync: SyncMode, sql?: string, params?: any): T[] | Promise<T[]> {
1052
+ logger.debug(sql, params ?? '');
1053
+ if (!sql) { return []; };
1054
+ if (sync === SyncMode.Sync) {
1055
+ logger.warn('SqliteRemote not suppouted sync mode');
1056
+ return [];
1057
+ };
1058
+ if (globalThis[_GlobalSqlOption].log === 'trace') {
1059
+ logger.trace(Sqlstring.format(sql!, params));
1060
+ }
1061
+ return new Promise<T[]>(async (resolve, reject) => {
1062
+ try {
1063
+ const r = await this[_daoConnection].query(this[_sqliteRemoteName], sql, params);
1064
+ resolve(r);
1065
+ } catch (error) {
1066
+ logger.error(`
1067
+ error: ${error},
1068
+ sql: ${sql},
1069
+ params: ${params}
1070
+ `);
1071
+ reject(error);
1072
+ }
1073
+ });
1074
+ }
1075
+
1076
+ realse(sync: SyncMode.Sync): void;
1077
+ realse(sync: SyncMode.Async): Promise<void>;
1078
+ realse(sync: SyncMode): Promise<void> | void {
1079
+ }
1080
+ }
1081
+ class SqliteRemote implements Dao {
1082
+ [_sqliteRemoteName]: string;
1083
+ [_daoDB]: SqliteRemoteInterface;
1084
+
1085
+ constructor(db: SqliteRemoteInterface, name: string) {
1086
+ this[_daoDB] = db;
1087
+ this[_sqliteRemoteName] = name;
1088
+ }
1089
+
1090
+
1091
+ createConnection(sync: SyncMode.Sync): Connection | null;
1092
+ createConnection(sync: SyncMode.Async): Promise<Connection | null>;
1093
+ createConnection(sync: SyncMode): Connection | null | Promise<Connection | null> {
1094
+ if (sync === SyncMode.Sync) {
1095
+ logger.error('SQLITEREMOTE not suppouted sync mode');
1096
+ return null;
1097
+ };
1098
+ return new Promise<Connection>(async resolve => {
1099
+ resolve(new SqliteRemoteConnection(this[_daoDB], this[_sqliteRemoteName]));
1100
+ });
1101
+ }
1102
+
1103
+ transaction<T = any>(sync: SyncMode.Sync, fn: (conn: Connection) => T, conn?: Connection | null): T | null;
1104
+ transaction<T = any>(sync: SyncMode.Async, fn: (conn: Connection) => Promise<T>, conn?: Connection | null): Promise<T | null>;
1105
+ transaction<T = any>(sync: SyncMode, fn: (conn: Connection) => T | Promise<T>, conn?: Connection | null): T | null | Promise<T | null> {
1106
+ logger.warn(`SQLITEREMOTE not suppoted transaction`);
1107
+ return null;
1108
+ }
1109
+
1110
+
1111
+ close(sync: SyncMode.Sync): void;
1112
+ close(sync: SyncMode.Async): Promise<void>;
1113
+ close(sync: SyncMode): Promise<void> | void {
1114
+ if (sync === SyncMode.Async) {
1115
+ return new Promise(async () => {
1116
+ await this[_daoConnection].close();
1117
+ });
1118
+ };
1119
+ }
1120
+
1121
+ backup(sync: SyncMode.Sync, name: string): void;
1122
+ backup(sync: SyncMode.Async, name: string): Promise<void>;
1123
+ backup(sync: SyncMode, name: string): Promise<void> | void {
1124
+ if (sync === SyncMode.Async) {
1125
+ return new Promise(async () => {
1126
+ await this[_daoConnection].backup(this[_sqliteRemoteName], name);
1127
+ });
1128
+ };
1129
+ }
1130
+
1131
+ remove(sync: SyncMode.Sync): void;
1132
+ remove(sync: SyncMode.Async): Promise<void>;
1133
+ remove(sync: SyncMode): Promise<void> | void {
1134
+ if (sync === SyncMode.Async) {
1135
+ return new Promise(async () => {
1136
+ await this[_daoConnection].remove();
1137
+ });
1138
+ };
1139
+ }
1140
+
1141
+ restore(sync: SyncMode.Sync, name: string): void;
1142
+ restore(sync: SyncMode.Async, name: string): Promise<void>;
1143
+ restore(sync: SyncMode, name: string): Promise<void> | void {
1144
+ if (sync === SyncMode.Async) {
1145
+ return new Promise(async () => {
1146
+ await this[_daoConnection].restore(this[_sqliteRemoteName], name);
1147
+ });
1148
+ };
1149
+ }
1150
+ }
1151
+ // #endregion
1152
+
1153
+ // #region 查询sql
1154
+ export type SqlModel = Record<string, string | (
1155
+ (options: {
1156
+ ctx: any;
1157
+ isCount?: boolean;
1158
+ isSum?: boolean;
1159
+ limitStart?: number;
1160
+ limitEnd?: number;
1161
+ orderBy?: string;
1162
+ [k: string]: any;
1163
+ }) => string
1164
+ )>;
1165
+ class Build {
1166
+ private static page = 'COUNT(1) zccw1986 ';
1167
+ private isCount: boolean;
1168
+ private isSum: boolean;
1169
+ private brage = { haveOrderBy: false, haveLimit: false };
1170
+ /**
1171
+ *
1172
+ * @param count 是否是count查询
1173
+ * @param isSum 是否是sum查询
1174
+ * @param param
1175
+ */
1176
+ constructor(
1177
+ isCount: boolean,
1178
+ isSum: boolean,
1179
+ param: { [propName: string]: any } = {}
1180
+ ) {
1181
+ this.isCount = isCount;
1182
+ this.isSum = isSum;
1183
+ Object.assign(this, param);
1184
+ }
1185
+ /**
1186
+ *
1187
+ * 当分页时将函数内包含的内容替换为COUNT(1)
1188
+ * @returns
1189
+ * @memberof Build
1190
+ */
1191
+ pageTag() {
1192
+ return (text: string, render: (text: string) => string) => {
1193
+ if (this.isCount === true) {
1194
+ return Build.page;
1195
+ } else if (this.isSum !== true) {
1196
+ return render(text);
1197
+ }
1198
+ };
1199
+ }
1200
+ /**
1201
+ *
1202
+ * 汇总查询专用
1203
+ * @returns
1204
+ * @memberof Build
1205
+ */
1206
+ sumTag() {
1207
+ return (text: string, render: (text: string) => string) => {
1208
+ if (this.isSum !== true) {
1209
+ return '';
1210
+ } else {
1211
+ return render(text);
1212
+ }
1213
+ };
1214
+ }
1215
+ /**
1216
+ *
1217
+ * 当分页时、汇总时忽略函数内包含的内容
1218
+ * @returns
1219
+ * @memberof Build
1220
+ */
1221
+ pageIgnoreTag() {
1222
+ return (text: string, render: (text: string) => string) => {
1223
+ if (this.isCount === true || this.isSum === true) {
1224
+ return '';
1225
+ } else {
1226
+ return render(text);
1227
+ }
1228
+ };
1229
+ }
1230
+ /**
1231
+ *
1232
+ * 将查询条件包起来,如果条件内容不为空,则自动添加WHERE,同时将第一个条件的and、or替换为空
1233
+ * 例如:
1234
+ * {{#whereTag}}
1235
+ * and name = 1
1236
+ * and page = 2
1237
+ * {{/whereTag}}
1238
+ * 输出
1239
+ * where name = 1 and page = 2
1240
+ * @returns
1241
+ * @memberof Build
1242
+ */
1243
+ where() {
1244
+ return (text: string, render: (text: string) => string) => {
1245
+ let data = render(text);
1246
+ data = data.trim();
1247
+ if (data) {
1248
+ data = data.replace(/and|or/i, '');
1249
+ return ` WHERE ${data} `;
1250
+ } else {
1251
+ return '';
1252
+ }
1253
+ };
1254
+ }
1255
+ /**
1256
+ * 删除第一个and、or
1257
+ * 删除最后一个,
1258
+ * 删除最后一个;
1259
+ * @memberof Build
1260
+ */
1261
+ trim() {
1262
+ return (text: string, render: (text: string) => string) => {
1263
+ let data = render(text);
1264
+ data = data.trim();
1265
+ if (data) {
1266
+ data = data.replace(/(^and\s)|(^or\s)|(,$)|(;$)/i, '');
1267
+ return data;
1268
+ } else {
1269
+ return '';
1270
+ }
1271
+ };
1272
+ }
1273
+ /**
1274
+ * 分页时将排序部分代码用此函数包起来,可以自动拼接order by
1275
+ * 查询条数时,自动忽略此部分
1276
+ * etc
1277
+ * {{#orderTag}} name desc, age asc {{/orderTag}}
1278
+ * ===
1279
+ * ORDER BY name desc, age asc
1280
+ * @returns
1281
+ * @memberof Build
1282
+ */
1283
+ orderTag() {
1284
+ return (text: string, render: (text: string) => string) => {
1285
+ if (this.isCount === true || this.isSum === true) {
1286
+ return '';
1287
+ } else {
1288
+ this.brage.haveOrderBy = true;
1289
+ const orderBy = new Array<string>();
1290
+ const renderOrder = render(text);
1291
+ if (/\S/.test(renderOrder)) {
1292
+ orderBy.push(renderOrder);
1293
+ }
1294
+ return orderBy.length > 0 ? ` ORDER BY ${orderBy.join(',')} ` : '';
1295
+ }
1296
+ };
1297
+ }
1298
+ limitTag() {
1299
+ return (text: string, render: (text: string) => string) => {
1300
+ if (this.isCount === true || this.isSum === true) {
1301
+ return '';
1302
+ } else {
1303
+ this.brage.haveOrderBy = true;
1304
+ const orderBy = new Array<string>();
1305
+ const renderOrder = render(text);
1306
+ if (/\S/.test(renderOrder)) {
1307
+ orderBy.push(renderOrder);
1308
+ }
1309
+ return orderBy.length > 0 ? ` ORDER BY ${orderBy.join(',')} ` : '';
1310
+ }
1311
+ };
1312
+ }
1313
+ /**
1314
+ *
1315
+ * 分页时将分组部分代码用此函数包起来,可以自动拼接GROUP BY
1316
+ * 当分页时、汇总时,自动忽略此部分
1317
+ * etc
1318
+ * {{#groupTag}} name, age {{/groupTag}}
1319
+ * ===
1320
+ * group by name.age
1321
+ * @returns
1322
+ * @memberof Build
1323
+ */
1324
+ groupTag() {
1325
+ return (text: string, render: (text: string) => string) => {
1326
+ if (this.isCount === true || this.isSum === true) {
1327
+ return '';
1328
+ } else {
1329
+ const groupBy = render(text) || '';
1330
+ return /\S/.test(groupBy) ? ` GROUP BY ${groupBy} ` : '';
1331
+ }
1332
+ };
1333
+ }
1334
+
1335
+ /**
1336
+ *
1337
+ * beetween and
1338
+ * etc.
1339
+ * {{#between}} AND t.createtime | ({{createtime}}) {{/between}}
1340
+ * createtime: 1,2
1341
+ * ===
1342
+ * AND t.createtime BETWEEN 1 AND 2
1343
+ * @returns
1344
+ * @memberof Build
1345
+ */
1346
+ between() {
1347
+ return (text: string, render: (text: string) => string) => {
1348
+ const result = render(text);
1349
+ if (/\(([\w\W]+)\)/.exec(result)) {
1350
+ return render(text).replace(/\(([\w\W]+)\)/, (a, b) => {
1351
+ if (a && b) {
1352
+ const xx = b.split(',');
1353
+ return `'${xx[0]}' AND '${xx[1]}'`;
1354
+ } else {
1355
+ return '';
1356
+ }
1357
+ }).replace(/\|/, ' BETWEEN ');
1358
+ } else {
1359
+ return '';
1360
+ }
1361
+ };
1362
+ }
1363
+
1364
+ /**
1365
+ *
1366
+ * 距离计算,单位米
1367
+ * etc
1368
+ * {{#distanceTag}} (t.longitude, t.latitude), ({{longitude}}, {{latitude}}) {{/distanceTag}}
1369
+ * ===
1370
+ * ROUND(ST_DISTANCE(POINT(longitude1, latitude1), POINT({{longitude}}, {{latitude}}))*111195, 2)
1371
+ * 可根据需求自行将数据转换为千米,例如
1372
+ * {{#distanceTag}} (t.longitude, t.latitude), ({{longitude}}, {{latitude}}) {{/distanceTag}} / 1000
1373
+ * @returns
1374
+ * @memberof Build
1375
+ */
1376
+ distanceTag() {
1377
+ return (text: string, render: (text: string) => string) => {
1378
+ const result = render(text);
1379
+ if (/\(([^()]+)\)/.exec(result)) {
1380
+ let index = 0;
1381
+ return render(text).replace(/\(([^()]+)\)/g, (a, b) => {
1382
+ if (a && b) {
1383
+ const xx = b.split(',');
1384
+ if (index === 0) {
1385
+ index++;
1386
+ return ` ROUND(ST_DISTANCE(POINT(${xx[0]}, ${xx[1]}) `;
1387
+ } else {
1388
+ return ` POINT(${xx[0]}, ${xx[1]}))*111195, 2)`;
1389
+ }
1390
+ } else {
1391
+ return '';
1392
+ }
1393
+ });
1394
+ } else {
1395
+ return '';
1396
+ }
1397
+ };
1398
+ }
1399
+ }
1400
+ class SqlCache {
1401
+ private sqlMap: SqlModel = {};
1402
+ private sqlFNMap: PartialsOrLookupFn = {};
1403
+ async init(options: {
1404
+ sqlMap?: SqlModel; sqlDir?: string;
1405
+ sqlFNMap?: Record<string, string>; sqlFNDir?: string;
1406
+ }) {
1407
+ if (options.sqlMap) {
1408
+ this.sqlMap = options.sqlMap;
1409
+ }
1410
+ if (options.sqlDir) {
1411
+ const sqlFis = globalThis[_fs].readdirSync(options.sqlDir);
1412
+ for (const modeName of sqlFis) {
1413
+ const extname = globalThis[_path].extname(modeName);
1414
+ const name = globalThis[_path].basename(modeName, extname);
1415
+ const file = globalThis[_path].join(options.sqlDir, modeName);
1416
+ if (extname === 'mu') {
1417
+ const parser = new MUParser(name, globalThis[_fs].readFileSync(file, { encoding: 'utf-8' }).toString());
1418
+ let source = parser.next();
1419
+ while (source != null) {
1420
+ this.sqlMap[source[0]] = source[1];
1421
+ logger.debug(`sql: ${source[0]} found!`);
1422
+ source = parser.next();
1423
+ }
1424
+ } else if (extname === '.js') {
1425
+ const obj = (await import(globalThis[_path].join(options.sqlDir, modeName))).default as SqlModel;
1426
+ for (const [key, fn] of Object.entries(obj)) {
1427
+ this.sqlMap[`${name}.${String(key)}`] = fn;
1428
+ }
1429
+ }
1430
+ }
1431
+ }
1432
+ if (options.sqlFNMap) {
1433
+ this.sqlFNMap = options.sqlFNMap;
1434
+ }
1435
+ if (options.sqlFNDir) {
1436
+ const sqlFis = globalThis[_fs].readdirSync(options.sqlDir);
1437
+ for (const modeName of sqlFis) {
1438
+ const extname = globalThis[_path].extname(modeName);
1439
+ const name = globalThis[_path].basename(modeName, extname);
1440
+ const file = globalThis[_path].join(options.sqlDir, modeName);
1441
+ if (extname === 'mu') {
1442
+ this.sqlFNMap[name] = globalThis[_fs].readFileSync(file, { encoding: 'utf-8' }).toString();
1443
+ }
1444
+ }
1445
+ }
1446
+ }
1447
+ load(sqlid: string, options: {
1448
+ ctx: any;
1449
+ isCount?: boolean;
1450
+ isSum?: boolean;
1451
+ limitStart?: number;
1452
+ limitEnd?: number;
1453
+ orderBy?: string;
1454
+ [k: string]: any;
1455
+ }): string {
1456
+ const sqlSource = this.sqlMap[sqlid];
1457
+ Throw.if(!sqlSource, `指定的语句${sqlid}不存在!`);
1458
+ const _sql = typeof sqlSource === 'function' ? sqlSource(options) : sqlSource!;
1459
+ const buildParam = new Build(options.isCount === true, options.isSum === true, options);
1460
+ const sql = mustache.render(_sql, buildParam, this.sqlFNMap);
1461
+ logger.debug(sqlid, sql);
1462
+ return sql;
1463
+ }
1464
+ }
1465
+ // #endregion
1466
+
1467
+ /**
1468
+
1469
+ ## 所有service中内置方法定义规则
1470
+ ** 方法第一个参数必须是 sync: SyncMode
1471
+ ** 方法最后一个参数必须是 option
1472
+
1473
+ ## sync 表示是否是同步方法
1474
+
1475
+ 因为mysql是异步、sqlite是同步,导致必须通过一个标识来区分,否则将必须为两种数据库设置不同的service,失去了意义
1476
+
1477
+ ## option 额外控制参数
1478
+
1479
+ ## length
1480
+ 方法的参数数量
1481
+ */
1482
+ function P<T extends object>(skipConn = false) {
1483
+ return (_target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
1484
+ const fn = descriptor.value;
1485
+ descriptor.value = function (this: SqlService<T>, ...args: any[]) {
1486
+ let needRealseConn = true;
1487
+ const startTime = +new Date();
1488
+ // option
1489
+ const option = args[0] = Object.assign({}, globalThis[_GlobalSqlOption], this[_SqlOption], args[0]) as (MethodOption & { sync?: SyncMode; });
1490
+ option.sync ??= SyncMode.Async;
1491
+ const dbName = option?.dbName ?? this[_daoDBName] ?? _primaryDB;
1492
+ option!.dao = globalThis[_dao][this[_dbType]!][dbName] as Dao;
1493
+ Throw.if(!option!.dao, `not found db:${String(dbName)}(${this[_dbType]})`);
1494
+ option!.tableName = option?.tableName ?? this[_tableName];
1495
+ const tableES = Sqlstring.escapeId(option!.tableName);
1496
+ if (this[_dbType] === DBType.Sqlite) {
1497
+ Throw.if(option.sync === SyncMode.Async, 'sqlite can not Async!')
1498
+ // 连接共享
1499
+ if (skipConn === false && !option!.conn) {
1500
+ option!.conn = option!.dao.createConnection(SyncMode.Sync)!;
1501
+ } else {
1502
+ needRealseConn = false;
1503
+ }
1504
+ if (skipConn === false) {
1505
+ const lastVersion = this[_sqlite_version] ?? '0.0.1';
1506
+ // 检查表
1507
+ const tableCheckResult = option!.conn!.pluck<number>(SyncMode.Sync, `SELECT COUNT(1) t FROM sqlite_master WHERE TYPE = 'table' AND name = ?`, [option!.tableName]);
1508
+ if (tableCheckResult) {
1509
+ // 旧版本
1510
+ const tableVersion = option!.conn!.pluck<string>(SyncMode.Sync, 'SELECT ______version v from TABLE_VERSION WHERE ______tableName = ?', [option!.tableName]);
1511
+ if (tableVersion && tableVersion < lastVersion) { // 发现需要升级的版本
1512
+ // 更新版本
1513
+ const columns = iterare<{ name: string }>(option!.conn!.query(SyncMode.Sync, `PRAGMA table_info(${tableES})`))
1514
+ .filter(c => this[_fields]!.hasOwnProperty(c.name))
1515
+ .map(c => Sqlstring.escapeId(c.name))
1516
+ .join(',');
1517
+
1518
+ const rtable = Sqlstring.escapeId(`${option!.tableName}_${tableVersion.replace(/\./, '_')}`);
1519
+ option!.conn!.execute(SyncMode.Sync, `DROP TABLE IF EXISTS ${rtable};`);
1520
+ option!.conn!.execute(SyncMode.Sync, `ALTER TABLE ${tableES} RENAME TO ${rtable};`);
1521
+ option!.conn!.execute(SyncMode.Sync, `
1522
+ CREATE TABLE IF NOT EXISTS ${tableES}(
1523
+ ${Object.values(this[_fields]!).map(K => K[DBType.Sqlite]).join(',')}
1524
+ ${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields]![i]?.esName).join(',')})` : ''}
1525
+ );
1526
+ `);
1527
+ if (this[_index] && this[_index].length) {
1528
+ for (const index of this[_index]) {
1529
+ option!.conn!.execute(SyncMode.Sync, `CREATE INDEX ${Sqlstring.escapeId(`${index}_${Math.random()}`.replace(/\./, ''))} ON ${tableES} ("${index}");`);
1530
+ }
1531
+ }
1532
+ option!.conn!.execute(SyncMode.Sync, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
1533
+ option!.conn!.execute(SyncMode.Sync, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
1534
+ option!.conn!.execute(SyncMode.Sync, `DROP TABLE IF EXISTS ${rtable};`);
1535
+ // 更新完毕,保存版本号
1536
+ option!.conn!.execute(SyncMode.Sync, 'UPDATE TABLE_VERSION SET ______version = ? WHERE ______tableName = ?', [option!.tableName, lastVersion]);
1537
+ } else if (!tableVersion) { // 不需要升级情况:没有旧的版本号
1538
+ option!.conn!.execute(SyncMode.Sync, 'INSERT INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option!.tableName, lastVersion]);
1539
+ }
1540
+ } else { // 表不存在
1541
+ // 创建表
1542
+ option!.conn!.execute(SyncMode.Sync, `
1543
+ CREATE TABLE IF NOT EXISTS ${tableES} (
1544
+ ${Object.values(this[_fields]!).map(K => K[DBType.Sqlite]).join(',')}
1545
+ ${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields]![i]?.esName).join(',')})` : ''}
1546
+
1547
+ );
1548
+ `);
1549
+ if (this[_index] && this[_index].length) {
1550
+ for (const index of this[_index]) {
1551
+ option!.conn!.execute(SyncMode.Sync, `CREATE INDEX ${Sqlstring.escapeId(`${index}_${Math.random()}`.replace(/\./, ''))} ON ${tableES} ("${index}");`);
1552
+ }
1553
+ }
1554
+ option!.conn!.execute(SyncMode.Sync, 'INSERT OR REPLACE INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option!.tableName, lastVersion]);
1555
+ }
1556
+ }
1557
+ try {
1558
+ const result = fn.call(this, ...args);
1559
+ logger.info(`${propertyKey}:${option!.tableName}:use ${+new Date() - startTime}ms`);
1560
+ return result;
1561
+ } catch (error) {
1562
+ try {
1563
+ console.error(`service ${propertyKey} have an error:${error}, it's argumens: ${JSON.stringify(args.filter(i => typeof i !== 'object' || (typeof i === 'object' && !i.insert)))}`)
1564
+ } catch (error) {
1565
+
1566
+ }
1567
+ throw error;
1568
+ } finally {
1569
+ if (needRealseConn && option && option!.conn) {
1570
+ try {
1571
+ option!.conn!.realse(SyncMode.Sync);
1572
+ } catch (error) {
1573
+ }
1574
+ }
1575
+ }
1576
+ } else if (this[_dbType] === DBType.SqliteRemote) {
1577
+ Throw.if(option.sync === SyncMode.Sync, 'SqliteRemote remote can not sync!')
1578
+ return new Promise(async (resolve, reject) => {
1579
+ // 连接共享
1580
+ if (skipConn === false && !option!.conn) {
1581
+ (option!).conn = await option!.dao!.createConnection(SyncMode.Async);
1582
+ } else {
1583
+ needRealseConn = false;
1584
+ }
1585
+ if (skipConn === false) {
1586
+ const lastVersion = this[_sqlite_version] ?? '0.0.1';
1587
+ // 检查表
1588
+ const tableCheckResult = await option!.conn!.pluck<number>(SyncMode.Async, `SELECT COUNT(1) t FROM sqlite_master WHERE TYPE = 'table' AND name = ?`, [option!.tableName]);
1589
+ if (tableCheckResult) {
1590
+ // 旧版本
1591
+ const tableVersion = await option!.conn!.pluck<string>(SyncMode.Async, 'SELECT ______version v from TABLE_VERSION WHERE ______tableName = ?', [option!.tableName]);
1592
+ if (tableVersion && tableVersion < lastVersion) { // 发现需要升级的版本
1593
+ // 更新版本
1594
+ const columns = iterare<{ name: string }>(await option!.conn!.query(SyncMode.Async, `PRAGMA table_info(${tableES})`))
1595
+ .filter(c => this[_fields]!.hasOwnProperty(c.name))
1596
+ .map(c => Sqlstring.escapeId(c.name))
1597
+ .join(',');
1598
+
1599
+ const rtable = `${option!.tableName}_${tableVersion.replace(/\./, '_')}`;
1600
+ await option!.conn!.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
1601
+ await option!.conn!.execute(SyncMode.Async, `ALTER TABLE ${tableES} RENAME TO ${rtable};`);
1602
+ await option!.conn!.execute(SyncMode.Async, `
1603
+ CREATE TABLE IF NOT EXISTS ${tableES}(
1604
+ ${Object.values(this[_fields]!).map(K => K[DBType.Sqlite]).join(',')}
1605
+ ${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields]![i]?.esName).join(',')})` : ''}
1606
+ );
1607
+ `);
1608
+ if (this[_index] && this[_index].length) {
1609
+ for (const index of this[_index]) {
1610
+ await option!.conn!.execute(SyncMode.Async, `CREATE INDEX ${Sqlstring.escapeId(`${index}_${Math.random()}`.replace(/\./, ''))} ON ${tableES} ("${index}");`);
1611
+ }
1612
+ }
1613
+ await option!.conn!.execute(SyncMode.Async, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
1614
+ await option!.conn!.execute(SyncMode.Async, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
1615
+ await option!.conn!.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
1616
+ // 更新完毕,保存版本号
1617
+ await option!.conn!.execute(SyncMode.Async, 'UPDATE TABLE_VERSION SET ______version = ? WHERE ______tableName = ?', [option!.tableName, lastVersion]);
1618
+ } else if (!tableVersion) { // 不需要升级情况:没有旧的版本号
1619
+ await option!.conn!.execute(SyncMode.Async, 'INSERT INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option!.tableName, lastVersion]);
1620
+ }
1621
+ } else { // 表不存在
1622
+ // 创建表
1623
+ await option!.conn!.execute(SyncMode.Async, `
1624
+ CREATE TABLE IF NOT EXISTS ${tableES}(
1625
+ ${Object.values(this[_fields]!).map(K => K[DBType.Sqlite]).join(',')}
1626
+ ${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields]![i]?.esName).join(',')})` : ''}
1627
+ );
1628
+ `);
1629
+ if (this[_index] && this[_index].length) {
1630
+ for (const index of this[_index]) {
1631
+ await option!.conn!.execute(SyncMode.Async, `CREATE INDEX ${Sqlstring.escapeId(`${index}_${Math.random()}`.replace(/\./, ''))} ON ${Sqlstring.escapeId(option!.tableName)} ("${index}");`);
1632
+ }
1633
+ }
1634
+ await option!.conn!.execute(SyncMode.Async, 'INSERT OR REPLACE INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option!.tableName, lastVersion]);
1635
+ }
1636
+ }
1637
+ try {
1638
+ const result = await fn.call(this, ...args);
1639
+ logger.info(`${propertyKey}:${option!.tableName}:use ${+new Date() - startTime}ms`);
1640
+ resolve(result);
1641
+ } catch (error) {
1642
+ console.error(`service ${propertyKey} have an error:${error}, it's argumens: ${JSON.stringify(args.filter(i => typeof i !== 'object' || (typeof i === 'object' && !i.insert)))}`)
1643
+ reject(error);
1644
+ } finally {
1645
+ if (needRealseConn && option && option!.conn) {
1646
+ try {
1647
+ option!.conn!.realse(SyncMode.Sync);
1648
+ } catch (error) {
1649
+
1650
+ }
1651
+ }
1652
+ }
1653
+ });
1654
+
1655
+ } else if (this[_dbType] === DBType.Mysql) {
1656
+ return new Promise(async (resolve, reject) => {
1657
+ try {
1658
+ // 连接共享
1659
+ if (skipConn === false && !option!.conn) {
1660
+ (option!).conn = await option!.dao!.createConnection(SyncMode.Async);
1661
+ } else {
1662
+ needRealseConn = false;
1663
+ }
1664
+ const result = await fn.call(this, ...args);
1665
+ logger.info(`${propertyKey}:${option!.tableName}:use ${+new Date() - startTime}ms`);
1666
+ resolve(result);
1667
+ } catch (error) {
1668
+ console.error(`service ${propertyKey} have an error:${error}, it's argumens: ${JSON.stringify(args.filter(i => typeof i !== 'object' || (typeof i === 'object' && !i.insert)))}`)
1669
+ reject(error);
1670
+ } finally {
1671
+ if (needRealseConn && option && option!.conn) {
1672
+ try {
1673
+ option!.conn!.realse(SyncMode.Sync);
1674
+ } catch (error) {
1675
+
1676
+ }
1677
+ }
1678
+ }
1679
+ });
1680
+ }
1681
+ };
1682
+ };
1683
+ }
1684
+ const FieldFilter = (
1685
+ K: string, V: any, def: any,
1686
+ option?: MethodOption & { finalColumns?: Set<string>; tempColumns?: Array<string>; def?: boolean; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; }
1687
+ ) => {
1688
+ let ret = 0;
1689
+ if (V === null) {
1690
+ if (option?.skipNull !== true) {
1691
+ ret = 1;
1692
+ V = option?.def === true && def && def.hasOwnProperty(K) ? def[K] : null;
1693
+ }
1694
+ } else if (V === undefined) {
1695
+ if (option?.skipUndefined !== true) {
1696
+ ret = 1;
1697
+ V = option?.def === true && def && def.hasOwnProperty(K) ? def[K] : null;
1698
+ }
1699
+ } else if (emptyString(`${V ?? ''}`)) {
1700
+ if (option?.skipEmptyString !== true) {
1701
+ ret = 1;
1702
+ V = option?.def === true && def && def.hasOwnProperty(K) ? def[K] : '';
1703
+ }
1704
+ } else {
1705
+ ret = 1;
1706
+ }
1707
+ if (ret === 1) {
1708
+ option?.finalColumns?.add(K);
1709
+ option?.tempColumns?.push(K);
1710
+ }
1711
+ return [ret, V];
1712
+ }
1713
+ const MYSQLCHARSET = `CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci`
1714
+ export const Field = (config: FieldOption) => {
1715
+ config.type ??= SqlType.varchar;
1716
+ return (object: object, propertyName: string) => {
1717
+ const field = config as AField;
1718
+ field.name = propertyName;
1719
+ field.esName = Sqlstring.escapeId(propertyName);
1720
+ const hasDef = field.hasOwnProperty('def') === true;
1721
+ switch (field.type) {
1722
+ case SqlType.tinyint: {
1723
+ field[DBType.Mysql] = `${field.esName} tinyint ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''}`;
1724
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} integer`;
1725
+ break;
1726
+ }
1727
+ case SqlType.smallint: {
1728
+ field[DBType.Mysql] = `${field.esName} smallint ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''}`;
1729
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} integer`;
1730
+ break;
1731
+ }
1732
+ case SqlType.mediumint: {
1733
+ field[DBType.Mysql] = `${field.esName} smallint ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''}`;
1734
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} integer`;
1735
+ break;
1736
+ }
1737
+ case SqlType.int: {
1738
+ field[DBType.Mysql] = `${field.esName} int ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''}`;
1739
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} integer`;
1740
+ break;
1741
+ }
1742
+ case SqlType.bigint: {
1743
+ field[DBType.Mysql] = `${field.esName} bigint ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1744
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} integer`;
1745
+ break;
1746
+ }
1747
+
1748
+ case SqlType.float: {
1749
+ field[DBType.Mysql] = `${field.esName} float(${config.length ?? 1}, ${config.scale ?? 2}) ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} `;
1750
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} real`;
1751
+ break;
1752
+ }
1753
+ case SqlType.double: {
1754
+ field[DBType.Mysql] = `${field.esName} double(${config.length ?? 1}, ${config.scale ?? 2}) ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} `;
1755
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} real`;
1756
+ break;
1757
+ }
1758
+ case SqlType.decimal: {
1759
+ field[DBType.Mysql] = `${field.esName} decimal(${config.length ?? 1}, ${config.scale ?? 2}) ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} `;
1760
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} real`;
1761
+ break;
1762
+ }
1763
+
1764
+ case SqlType.longtext: {
1765
+ field[DBType.Mysql] = `${field.esName} longtext ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1766
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1767
+ break;
1768
+ }
1769
+ case SqlType.mediumtext: {
1770
+ field[DBType.Mysql] = `${field.esName} mediumtext ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1771
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1772
+ break;
1773
+ }
1774
+ case SqlType.text: {
1775
+ field[DBType.Mysql] = `${field.esName} text ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1776
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1777
+ break;
1778
+ }
1779
+
1780
+ case SqlType.date: {
1781
+ field[DBType.Mysql] = `${field.esName} date ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1782
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1783
+ break;
1784
+ }
1785
+ case SqlType.time: {
1786
+ field[DBType.Mysql] = `${field.esName} time ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1787
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1788
+ break;
1789
+ }
1790
+ case SqlType.year: {
1791
+ field[DBType.Mysql] = `${field.esName} year ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1792
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1793
+ break;
1794
+ }
1795
+ case SqlType.datetime: {
1796
+ field[DBType.Mysql] = `${field.esName} datetime ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1797
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1798
+ break;
1799
+ }
1800
+ case SqlType.timestamp: {
1801
+ field[DBType.Mysql] = `${field.esName} timestamp ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1802
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} integer`;
1803
+ break;
1804
+ }
1805
+
1806
+ case SqlType.char: {
1807
+ field[DBType.Mysql] = `${field.esName} char(${config.length ?? 1}) ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1808
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1809
+ break;
1810
+ }
1811
+ case SqlType.varchar: {
1812
+ field[DBType.Mysql] = `${field.esName} varchar(${config.length ?? 1}) ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1813
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1814
+ break;
1815
+ }
1816
+ case SqlType.tinyblob: {
1817
+ field[DBType.Mysql] = `${field.esName} tinyblob ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1818
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1819
+ break;
1820
+ }
1821
+ case SqlType.tinytext: {
1822
+ field[DBType.Mysql] = `${field.esName} tinytext ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1823
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1824
+ break;
1825
+ }
1826
+ case SqlType.blob: {
1827
+ field[DBType.Mysql] = `${field.esName} binary ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1828
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1829
+ break;
1830
+ }
1831
+ case SqlType.text: {
1832
+ field[DBType.Mysql] = `${field.esName} text ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1833
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1834
+ break;
1835
+ }
1836
+ case SqlType.mediumblob: {
1837
+ field[DBType.Mysql] = `${field.esName} mediumblob ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1838
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1839
+ break;
1840
+ }
1841
+ case SqlType.mediumtext: {
1842
+ field[DBType.Mysql] = `${field.esName} mediumtext ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1843
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1844
+ break;
1845
+ }
1846
+ case SqlType.longblob: {
1847
+ field[DBType.Mysql] = `${field.esName} longblob ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1848
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1849
+ break;
1850
+ }
1851
+ case SqlType.longtext: {
1852
+ field[DBType.Mysql] = `${field.esName} longtext ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1853
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1854
+ break;
1855
+ }
1856
+
1857
+ case SqlType.set: {
1858
+ field[DBType.Mysql] = `${field.esName} set ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1859
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1860
+ break;
1861
+ }
1862
+ case SqlType.enum: {
1863
+ field[DBType.Mysql] = `${field.esName} enum ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1864
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1865
+ break;
1866
+ }
1867
+ case SqlType.json: {
1868
+ field[DBType.Mysql] = `${field.esName} json ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1869
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1870
+ break;
1871
+ }
1872
+
1873
+ case SqlType.geometry: {
1874
+ field[DBType.Mysql] = `${field.esName} geometry ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1875
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1876
+ break;
1877
+ }
1878
+ case SqlType.point: {
1879
+ field[DBType.Mysql] = `${field.esName} point ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1880
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1881
+ break;
1882
+ }
1883
+ case SqlType.linestring: {
1884
+ field[DBType.Mysql] = `${field.esName} linestring ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1885
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1886
+ break;
1887
+ }
1888
+ case SqlType.polygon: {
1889
+ field[DBType.Mysql] = `${field.esName} polygon ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1890
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1891
+ break;
1892
+ }
1893
+ case SqlType.multipoint: {
1894
+ field[DBType.Mysql] = `${field.esName} multipoint ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1895
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1896
+ break;
1897
+ }
1898
+ case SqlType.multilinestring: {
1899
+ field[DBType.Mysql] = `${field.esName} multilinestring ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1900
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1901
+ break;
1902
+ }
1903
+ case SqlType.multipolygon: {
1904
+ field[DBType.Mysql] = `${field.esName} multipolygon ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1905
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1906
+ break;
1907
+ }
1908
+ case SqlType.geometrycollection: {
1909
+ field[DBType.Mysql] = `${field.esName} geometrycollection ${config.notNull === true ? 'NOT NULL' : ''} ${hasDef ? field.def : ''} ${MYSQLCHARSET}`;
1910
+ field[DBType.SqliteRemote] = field[DBType.Sqlite] = `${field.esName} text`;
1911
+ break;
1912
+ }
1913
+ };
1914
+ let __fields = Reflect.getMetadata(_fields, object);
1915
+ let __columns = Reflect.getMetadata(_columns, object);
1916
+ let __columnsNoId = Reflect.getMetadata(_columnsNoId, object);
1917
+ let __ids = Reflect.getMetadata(_ids, object);
1918
+ let __index = Reflect.getMetadata(_index, object);
1919
+ let __def = Reflect.getMetadata(_def, object);
1920
+
1921
+ if (!__fields) {
1922
+ __fields = {};
1923
+ __columns = [];
1924
+ __columnsNoId = [];
1925
+ __ids = [];
1926
+ __index = [];
1927
+ __def = {};
1928
+ }
1929
+ __fields[propertyName] = field;
1930
+ __columns.push(propertyName);
1931
+ if (field.id === true) {
1932
+ __ids.push(propertyName);
1933
+ } else {
1934
+ __columnsNoId.push(propertyName);
1935
+ }
1936
+ if (field.index === true) {
1937
+ __index.push(propertyName);
1938
+ }
1939
+ if (hasDef) {
1940
+ __def[propertyName] = field.def;
1941
+ }
1942
+
1943
+ Reflect.defineMetadata(_fields, __fields, object);
1944
+ Reflect.defineMetadata(_columns, __columns, object);
1945
+ Reflect.defineMetadata(_columnsNoId, __columnsNoId, object);
1946
+ Reflect.defineMetadata(_ids, __ids, object);
1947
+ Reflect.defineMetadata(_index, __index, object);
1948
+ Reflect.defineMetadata(_def, __def, object);
1949
+ if (field.hasOwnProperty('logicDelete')) {
1950
+ Reflect.defineMetadata(_deleteState, field.logicDelete, object);
1951
+ Reflect.defineMetadata(_stateFileName, propertyName, object);
1952
+ }
1953
+ };
1954
+ }
1955
+ export const DB = (config: ServiceOption) => {
1956
+ return function <C extends { new(...args: any[]): {} }>(constructor: C) {
1957
+ const __ids = Reflect.getMetadata(_ids, config.clz.prototype) || new Array<string>;
1958
+ const __fields = Reflect.getMetadata(_fields, config.clz.prototype);
1959
+ const __columns = Reflect.getMetadata(_columns, config.clz.prototype);
1960
+ const __columnsNoId = __columns.filter((c: string) => __ids.includes(c) === false);
1961
+ const __stateFileName = Reflect.getMetadata(_stateFileName, config.clz.prototype);
1962
+ const __deleteState = Reflect.getMetadata(_deleteState, config.clz.prototype);
1963
+ const __index = Reflect.getMetadata(_index, config.clz.prototype);
1964
+ const __def = Reflect.getMetadata(_def, config.clz.prototype);
1965
+ return class extends constructor {
1966
+ [_tableName] = config.tableName;
1967
+ [_daoDBName] = config.dbName;
1968
+ [_dbType] = config.dbType ?? DBType.Mysql;
1969
+ [_sqlite_version] = config.sqliteVersion;
1970
+ [_SqlOption] = Object.assign({}, _defOption, config);
1971
+
1972
+ [_ids] = __ids;
1973
+ [_fields] = __fields;
1974
+ [_columns] = __columns;
1975
+ [_columnsNoId] = __columnsNoId;
1976
+ [_index] = __index;
1977
+ [_def] = __def;
1978
+ [_stateFileName] = __stateFileName;
1979
+ [_deleteState] = __deleteState;
1980
+
1981
+ [_transformer] = <L extends Object>(
1982
+ data: L,
1983
+ option?: MethodOption & {
1984
+ finalColumns?: Set<string>;
1985
+ tempColumns?: Array<string>;
1986
+ def?: boolean;
1987
+ skipId?: boolean;
1988
+ onFieldExists?: (K: string, V: any) => void;
1989
+ }
1990
+ ) => {
1991
+ return Object.fromEntries(
1992
+ iterare(option?.skipId === true ? __columnsNoId : __columns)
1993
+ .map(K => [K, FieldFilter(K as string, data[K as string], __def, option)])
1994
+ .filter(data => {
1995
+ if ((data[1] as any)[0] === 1) {
1996
+ if (option?.onFieldExists) {
1997
+ option.onFieldExists(data[0] as string, (data[1] as any)[1]);
1998
+ }
1999
+ return true;
2000
+ } else {
2001
+ return false;
2002
+ }
2003
+ })
2004
+ .map(data => [data[0], (data[1] as any)[1]])
2005
+ .toArray()
2006
+ );
2007
+ };
2008
+ };
2009
+ }
2010
+ };
2011
+ /**
2012
+ js项目中实体类注解替代品,只要确保函数被执行即可,举例:
2013
+ ```
2014
+ // 声明一个class
2015
+ export class AmaFuck {}
2016
+ DeclareClass(AmaFuck, [
2017
+ { type: "String", name: "SellerSKU" },
2018
+ { type: "String", name: "SellerSKU2" },
2019
+ { type: "String", name: "site" }
2020
+ ]);
2021
+ ```
2022
+ */
2023
+ export function DeclareClass(clz: any, FieldOptions: FieldOption[]) {
2024
+ for (const item of FieldOptions) {
2025
+ tslib.__decorate([Field(item)], clz.prototype, item.name, void 0);
2026
+ }
2027
+ }
2028
+ /**
2029
+ JS项目中,service注解代替,举例:
2030
+ ```
2031
+ // 声明一个service,注意这里的let
2032
+ export let AmaService = class AmaService extends SqlService {};
2033
+ AmaService = DeclareService(AmaService, {
2034
+ tableName: "ama_fuck2",
2035
+ clz: AmaFuck,
2036
+ dbType: DBType.Sqlite,
2037
+ sqliteVersion: "0.0.3"
2038
+ });
2039
+ ```
2040
+ */
2041
+ export function DeclareService(clz: any, config: ServiceOption) {
2042
+ return tslib.__decorate([DB(config)], clz)
2043
+ }
2044
+ /**
2045
+ ## 数据库服务
2046
+ ### 注解DB
2047
+
2048
+ ### 泛型 T,同DB注解中的clz
2049
+ ** 服务中所有方法默认以该类型为准
2050
+ **
2051
+
2052
+ */
2053
+ export class SqlService<T extends object> {
2054
+ private [_tableName]?: string;
2055
+ private [_daoDBName]?: string;
2056
+ private [_ids]?: string[];
2057
+ private [_fields]?: Record<string, AField>;
2058
+ private [_columns]?: string[];
2059
+ private [_columnsNoId]?: string[];
2060
+ private [_stateFileName]?: string;
2061
+ private [_deleteState]?: string;
2062
+ private [_SqlOption]?: ServiceOption;
2063
+ private [_dbType]?: DBType;
2064
+ private [_sqlite_version]?: string;
2065
+ private [_index]?: string[];
2066
+ private [_def]?: Partial<T>;
2067
+ private [_transformer]?: (data: Partial<T>, option?: MethodOption & { finalColumns?: Set<string>; def?: boolean; skipId?: boolean; onFieldExists?: (K: string, V: any) => void; }) => Partial<T>;
2068
+ private _insert(
2069
+ datas: Partial<T>[],
2070
+ option: MethodOption & {
2071
+ mode?: InsertMode;
2072
+ existConditionOtherThanIds?: (keyof T)[];
2073
+ }): { sql: string; params?: any[] }[] {
2074
+ const sqls: { sql: string; params?: any[] }[] = [];
2075
+ const tableName = Sqlstring.escapeId(option!.tableName);
2076
+ switch (option?.mode) {
2077
+ case InsertMode.InsertIfNotExists: {
2078
+ const conditions = option!.existConditionOtherThanIds || this[_ids];
2079
+ Throw.if(!conditions, 'not found where condition for insertIfNotExists!');
2080
+ Throw.if(conditions!.length === 0, 'insertIfNotExists must have not null where!');
2081
+ const where = iterare<string>(conditions! as string[]).map(c => `${this[_fields]![c]?.esName} = ?`).join(' AND ');
2082
+ const finalColumns = new Set<string>();
2083
+ const whereColumns = conditions! as string[];
2084
+ const params = datas
2085
+ .map(data => this[_transformer]!(data, { ...option, finalColumns, def: true }))
2086
+ .flatMap(data => {
2087
+ const result: any[] = [];
2088
+ const questMark = new Array<string>();
2089
+ for (const column of finalColumns) {
2090
+ questMark.push('?');
2091
+ result.push(
2092
+ data.hasOwnProperty(column)
2093
+ ? data[column]
2094
+ : this[_def] && this[_def].hasOwnProperty(column)
2095
+ ? this[_def][column]
2096
+ : null
2097
+ );
2098
+ }
2099
+
2100
+ for (const column of whereColumns) {
2101
+ questMark.push('?');
2102
+ result.push(
2103
+ data.hasOwnProperty(column)
2104
+ ? data[column]
2105
+ : this[_def] && this[_def].hasOwnProperty(column)
2106
+ ? this[_def][column]
2107
+ : null
2108
+ );
2109
+ }
2110
+ return result;
2111
+ });
2112
+ const quests = new Array<string>(finalColumns.size).fill('?').join(',');
2113
+ const columnNames = iterare<string>(finalColumns).map(i => this[_fields]![i]?.esName).join(',');
2114
+ const selects = iterare<string>(new Array<string>(datas.length)).map(() => `SELECT ${quests} FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM ${tableName} WHERE ${where})`).join(' UNION ALL ');
2115
+ const sql = `INSERT INTO
2116
+ ${tableName}
2117
+ (${columnNames})
2118
+ ${selects};`;
2119
+ sqls.push({ sql, params });
2120
+ }
2121
+ case InsertMode.Replace: {
2122
+ const finalColumns = new Set<string>();
2123
+ const params = datas
2124
+ .map(data => this[_transformer]!(data, { ...option, finalColumns, def: true }))
2125
+ .flatMap(data => {
2126
+ const result: any[] = [];
2127
+ const questMark = new Array<string>();
2128
+ for (const column of finalColumns) {
2129
+ questMark.push('?');
2130
+ result.push(
2131
+ data.hasOwnProperty(column)
2132
+ ? data[column]
2133
+ : this[_def] && this[_def].hasOwnProperty(column)
2134
+ ? this[_def][column]
2135
+ : null
2136
+ );
2137
+ }
2138
+ return result;
2139
+ });
2140
+ const quests = new Array<string>(finalColumns.size).fill('?').join(',');
2141
+ const columnNames = iterare<string>(finalColumns).map(i => this[_fields]![i]?.esName).join(',');
2142
+ const questMarks = iterare<string>(new Array<string>(datas.length)).map(() => `(${quests})`).join(',');
2143
+ const sql = `
2144
+ ${this[_dbType] === DBType.Mysql ? '' : 'INSERT OR'} REPLACE INTO
2145
+ ${tableName}
2146
+ (${columnNames})
2147
+ VALUES ${questMarks};
2148
+ `;
2149
+ sqls.push({ sql, params });
2150
+ }
2151
+ case InsertMode.Insert: {
2152
+ const finalColumns = new Set<string>();
2153
+ const params = datas
2154
+ .map(data => this[_transformer]!(data, { ...option, finalColumns, def: true }))
2155
+ .flatMap(data => {
2156
+ const result: any[] = [];
2157
+ const questMark = new Array<string>();
2158
+ for (const column of finalColumns) {
2159
+ questMark.push('?');
2160
+ result.push(
2161
+ data.hasOwnProperty(column)
2162
+ ? data[column]
2163
+ : this[_def] && this[_def].hasOwnProperty(column)
2164
+ ? this[_def][column]
2165
+ : null
2166
+ );
2167
+ }
2168
+ return result;
2169
+ });
2170
+ const quests = new Array<string>(finalColumns.size).fill('?').join(',');
2171
+ const columnNames = iterare(finalColumns).map(i => this[_fields]![i]?.esName).join(',');
2172
+ const questMarks = iterare(new Array<string>(datas.length)).map(() => `(${quests})`).join(',');
2173
+ const sql = `
2174
+ INSERT INTO
2175
+ ${tableName}
2176
+ (${columnNames})
2177
+ VALUES ${questMarks};
2178
+ `;
2179
+ sqls.push({ sql, params });
2180
+ }
2181
+ case InsertMode.InsertWithTempTable: {
2182
+ const tableTemp = `${option?.tableName}_${Math.random()}`.replace(/\./, '');
2183
+ const tableTempESC = Sqlstring.escapeId(tableTemp);
2184
+ sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
2185
+ const finalColumns = new Set<string>();
2186
+ const params = datas
2187
+ .map(data => this[_transformer]!(data, { ...option, finalColumns, def: true }))
2188
+ .flatMap(data => {
2189
+ const result: any[] = [];
2190
+ const questMark = new Array<string>();
2191
+ for (const column of finalColumns) {
2192
+ questMark.push('?');
2193
+ result.push(
2194
+ data.hasOwnProperty(column)
2195
+ ? data[column]
2196
+ : this[_def] && this[_def].hasOwnProperty(column)
2197
+ ? this[_def][column]
2198
+ : null
2199
+ );
2200
+ }
2201
+ return result;
2202
+ });
2203
+ const _sqls = this._createTable({ tableName: tableTemp, temp: true, columns: Array.from(finalColumns) })!;
2204
+ sqls.push(..._sqls);
2205
+
2206
+ const quests = new Array<string>(finalColumns.size).fill('?').join(',');
2207
+ const columnNames = iterare(finalColumns).map(i => this[_fields]![i]?.esName).join(',');
2208
+ const questMarks = iterare(new Array<string>(datas.length)).map(() => `(${quests})`).join(',');
2209
+ sqls.push({
2210
+ sql: `
2211
+ INSERT INTO
2212
+ ${tableTemp}
2213
+ (${columnNames})
2214
+ VALUES ${questMarks};
2215
+ `, params
2216
+ });
2217
+ sqls.push({
2218
+ sql: `INSERT INTO ${Sqlstring.escapeId(option.tableName)} (${columnNames})
2219
+ SELECT ${columnNames} FROM ${tableTemp};`
2220
+ });
2221
+ sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
2222
+ }
2223
+ }
2224
+ return sqls;
2225
+ }
2226
+ /**
2227
+ 0. `sync`: 同步(sqlite)或者异步(mysql、remote),影响返回值类型,默认 `异步`
2228
+ 1. `data`:可是数组或者单对象
2229
+ 2. `skipUndefined`: boolean; 是否不处理值为undefined的字段,默认 true
2230
+ 3. `skipNull`: boolean; 是否不处理值为null的字段,默认 true
2231
+ 4. `skipEmptyString`: boolean; 是否不处理值为空字符串(`注意:多个空格也算空字符串`)的字段,默认 true
2232
+ 5. `maxDeal`: number; 批量处理时,每次处理多少个?默认500
2233
+ 6. `tableName`: 默认使用service注解的`tableName`,可以在某个方法中覆盖
2234
+ 7. `dbName`: 默认使用service注解的`dbName`,可以在某个方法中覆盖
2235
+ 8. `conn`: 仅在开启事务时需要主动传入,传入示例:
2236
+ ```
2237
+ service.transaction(async conn => {
2238
+ service.insert({conn});
2239
+ });
2240
+ ```
2241
+ 9. `dao`: 永远不需要传入该值
2242
+ 10. `mode` :默认`insert`,可选如下
2243
+ 1. `insert`: 默认
2244
+ 2. `insertIfNotExists`: 通过主键或者existConditionOtherThanIds字段判断数据是否存在,不存在才插入,存在则不执行
2245
+ 3. `replace`: 只支持用主键判断, 存在更新, 不存在插入
2246
+ 11. `existConditionOtherThanIds`: insertIfNotExists时判断同一记录的字段名称,默认情况下按照ID判断,设置existConditionOtherThanIds后,不用id
2247
+ ### 返回值是最后一次插入的主键ID,对于自增ID表适用
2248
+ 1. 如果主键是自增批量操作,且期望返回所有记录的ID,那么需要设置 `option 中的 every = true`,此时效率降低
2249
+ * @param {{[P in keyof T]?: T[P]}} data
2250
+ * @param {MethodOption} [option]
2251
+ * @memberof SqlServer
2252
+ */
2253
+ insert(option: MethodOption & { data: Partial<T>; sync?: SyncMode.Async; mode?: InsertMode; existConditionOtherThanIds?: (keyof T)[]; every?: boolean; temp?: boolean; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): Promise<bigint>;
2254
+ insert(option: MethodOption & { data: Partial<T>[]; sync?: SyncMode.Async; mode?: InsertMode; existConditionOtherThanIds?: (keyof T)[]; every?: boolean; temp?: boolean; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): Promise<bigint[]>;
2255
+ insert(option: MethodOption & { data: Partial<T>; sync: SyncMode.Sync; mode?: InsertMode; existConditionOtherThanIds?: (keyof T)[]; every?: boolean; temp?: boolean; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): bigint;
2256
+ insert(option: MethodOption & { data: Partial<T>[]; sync: SyncMode.Sync; mode?: InsertMode; existConditionOtherThanIds?: (keyof T)[]; every?: boolean; temp?: boolean; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): bigint[];
2257
+ @P<T>()
2258
+ insert(option: MethodOption & { data: Partial<T> | Array<Partial<T>>; sync?: SyncMode; mode?: InsertMode; existConditionOtherThanIds?: (keyof T)[]; every?: boolean; temp?: boolean; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): bigint | bigint[] | Promise<bigint> | Promise<bigint[]> {
2259
+ option.mode ??= InsertMode.Insert;
2260
+ const isArray = option.data instanceof Array;
2261
+ const datas = option.data instanceof Array ? option.data : [option.data];
2262
+ if (option.sync === SyncMode.Sync) {
2263
+ const fn = () => {
2264
+ const result = excuteSplit<Partial<T>, bigint>(
2265
+ ExcuteSplitMode.SyncTrust,
2266
+ datas,
2267
+ _data => {
2268
+ const sqls = this._insert(_data, option);
2269
+ let result = 0n;
2270
+ for (const { sql, params } of sqls) {
2271
+ const dd = option!.conn!.execute(SyncMode.Sync, sql, params);
2272
+ if (dd.insertId) { result += dd.insertId; }
2273
+ }
2274
+ return result;
2275
+ },
2276
+ { everyLength: option?.every === true ? 1 : option?.maxDeal }
2277
+ );
2278
+ if (isArray) return result;
2279
+ else return result[0]!;
2280
+ };
2281
+ if (option?.conn?.[_inTransaction] === true) {
2282
+ return fn();
2283
+ } else {
2284
+ return option?.dao?.transaction(SyncMode.Sync, fn, option?.conn)!;
2285
+ }
2286
+ } else if (isArray) {
2287
+ const fn = async () => {
2288
+ return await option?.dao?.transaction(SyncMode.Async, async () => {
2289
+ const result = await excuteSplit<Partial<T>, bigint>(
2290
+ ExcuteSplitMode.AsyncTrust,
2291
+ datas,
2292
+ async _data => {
2293
+ const sqls = this._insert(_data, option);
2294
+ let result = 0n;
2295
+ for (const { sql, params } of sqls) {
2296
+ const dd = await option?.conn!.execute(SyncMode.Async, sql, params);
2297
+ if (dd.insertId) { result += dd.insertId; }
2298
+ }
2299
+ return result;
2300
+ },
2301
+ { everyLength: option?.every === true ? 1 : option?.maxDeal }
2302
+ );
2303
+ return result;
2304
+ }, option?.conn);
2305
+ };
2306
+ return new Promise<bigint[]>(async (resolve) => {
2307
+ if (option?.conn?.[_inTransaction] === true) {
2308
+ resolve((await fn())!);
2309
+ } else {
2310
+ await option?.dao?.transaction(SyncMode.Async, async () => resolve((await fn())!), option?.conn);
2311
+ }
2312
+ });
2313
+ } else {
2314
+ const fn = async () => {
2315
+ const result = await excuteSplit<Partial<T>, bigint>(
2316
+ ExcuteSplitMode.AsyncTrust,
2317
+ datas,
2318
+ async _data => {
2319
+ const sqls = this._insert(_data, option);
2320
+ let result = 0n;
2321
+ for (const { sql, params } of sqls) {
2322
+ const dd = await option!.conn!.execute(SyncMode.Async, sql, params);
2323
+ if (dd.insertId) { result += dd.insertId; }
2324
+ }
2325
+ return result;
2326
+ },
2327
+ { everyLength: 1 }
2328
+ );
2329
+ return result[0]!;
2330
+ };
2331
+ return new Promise<bigint>(async (resolve) => {
2332
+ if (option?.conn?.[_inTransaction] === true) {
2333
+ resolve((await fn())!);
2334
+ } else {
2335
+ await option?.dao?.transaction(SyncMode.Async, async () => resolve((await fn())!), option?.conn);
2336
+ }
2337
+ });
2338
+ }
2339
+ }
2340
+
2341
+ private _update(datas: Array<Partial<T>>, option: MethodOption): { sql: string; params?: any[] }[] {
2342
+ const sqls: { sql: string; params?: any[] }[] = [];
2343
+ const tableName = Sqlstring.escapeId(option?.tableName);
2344
+ const where = `WHEN ${iterare(this[_ids]!).map(c => `${this[_fields]![c]?.esName} = ?`).join(' AND ')} THEN ?`;
2345
+ const columnMaps: Record<string, {
2346
+ where: string[];
2347
+ params: any[];
2348
+ }> = Object.fromEntries(this[_columnsNoId]!.map(c => [c, {
2349
+ where: new Array<string>(),
2350
+ params: []
2351
+ }]));
2352
+ const params: any[] = [];
2353
+ for (const data of datas) {
2354
+ const ids = this[_ids]!.map(i => {
2355
+ Throw.if(!data[i], `UPDATE ID NOT EXISTS!${JSON.stringify(data)}`);
2356
+ return data[i];
2357
+ });
2358
+ this[_transformer]!(data,
2359
+ {
2360
+ ...option,
2361
+ skipId: true,
2362
+ onFieldExists: (K, V) => {
2363
+ columnMaps[K]?.where.push(where);
2364
+ columnMaps[K]?.params.push(...ids, V);
2365
+ }
2366
+ }
2367
+ );
2368
+ }
2369
+ const sql = `UPDATE ${tableName} SET ${iterare(this[_columnsNoId]!)
2370
+ .filter(K => columnMaps[K]!.where.length > 0)
2371
+ .map(K => {
2372
+ params.push(...columnMaps[K]!.params);
2373
+ return `${this[_fields]![K]?.esName} = CASE ${columnMaps[K]!.where.join(' ')} ELSE ${this[_fields]![K]?.esName} END`
2374
+ })
2375
+ .join(',')};`
2376
+ sqls.push({ sql, params });
2377
+ return sqls;
2378
+ }
2379
+ /**
2380
+ ## 根据主键修改
2381
+ 0. `sync`: 同步(sqlite)或者异步(mysql、remote),影响返回值类型,默认`异步模式`
2382
+ 1. `data`:可是数组或者单对象
2383
+ 2. `skipUndefined`: boolean; 是否不处理值为undefined的字段,默认 true
2384
+ 3. `skipNull`: boolean; 是否不处理值为null的字段,默认 true
2385
+ 4. `skipEmptyString`: boolean; 是否不处理值为空字符串(`注意:多个空格也算空字符串`)的字段,默认 true
2386
+ 5. `maxDeal`: number; 批量处理时,每次处理多少个?默认500
2387
+ 6. `tableName`: 默认使用service注解的`tableName`,可以在某个方法中覆盖
2388
+ 7. `dbName`: 默认使用service注解的`dbName`,可以在某个方法中覆盖
2389
+ 8. `conn`: 仅在开启事务时需要主动传入,传入示例:
2390
+ ```
2391
+ service.transaction(async conn => {
2392
+ service.insert({conn});
2393
+ });
2394
+ ```
2395
+ 9. `dao`: 永远不需要传入该值
2396
+ */
2397
+ update(option: MethodOption & { data: Partial<T> | Array<Partial<T>>; sync?: SyncMode.Async; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): Promise<number>;
2398
+ update(option: MethodOption & { data: Partial<T> | Array<Partial<T>>; sync: SyncMode.Sync; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): number;
2399
+ @P<T>()
2400
+ update(option: MethodOption & { data: Partial<T> | Array<Partial<T>>; sync?: SyncMode; skipUndefined?: boolean; skipNull?: boolean; skipEmptyString?: boolean; maxDeal?: number; }): Promise<number> | number {
2401
+ Throw.if(!this[_ids] || this[_ids].length === 0, 'not found id')
2402
+ const datas = option.data instanceof Array ? option.data : [option.data];
2403
+ if (option.sync === SyncMode.Sync) {
2404
+ const fn = () => {
2405
+ const result = excuteSplit<Partial<T>, number>(
2406
+ ExcuteSplitMode.SyncTrust,
2407
+ datas,
2408
+ _data => {
2409
+ const sqls = this._update(_data, option);
2410
+ let result = 0;
2411
+ for (const { sql, params } of sqls) {
2412
+ const dd = option!.conn!.execute(SyncMode.Sync, sql, params);
2413
+ if (dd.affectedRows) { result += dd.affectedRows; }
2414
+ }
2415
+ return result;
2416
+ },
2417
+ { everyLength: option?.maxDeal }
2418
+ );
2419
+ return result.reduce((a, b) => a + b);
2420
+ };
2421
+ if (option?.conn?.[_inTransaction] === true) {
2422
+ return fn();
2423
+ } else {
2424
+ return option?.dao?.transaction(SyncMode.Sync, fn, option?.conn)!;
2425
+ }
2426
+ } else {
2427
+ const fn = async () => {
2428
+ const result = await excuteSplit<Partial<T>, number>(
2429
+ ExcuteSplitMode.AsyncTrust,
2430
+ datas,
2431
+ async _data => {
2432
+ const sqls = this._update(_data, option);
2433
+ let result = 0;
2434
+ for (const { sql, params } of sqls) {
2435
+ const dd = await option!.conn!.execute(SyncMode.Async, sql, params);
2436
+ if (dd.affectedRows) { result += dd.affectedRows; }
2437
+ }
2438
+ return result;
2439
+ },
2440
+ { everyLength: option?.maxDeal }
2441
+ );
2442
+ return result.reduce((a, b) => a + b);
2443
+ };
2444
+ return new Promise<number>(async (resolve) => {
2445
+ if (option?.conn?.[_inTransaction] === true) {
2446
+ resolve((await fn())!);
2447
+ } else {
2448
+ await option?.dao?.transaction(SyncMode.Async, async () => resolve((await fn())!), option?.conn);
2449
+ }
2450
+ });
2451
+ }
2452
+ }
2453
+
2454
+ /**
2455
+ ## 删除
2456
+ 0. `sync`: 同步(sqlite)或者异步(mysql、remote),影响返回值类型,默认`异步模式`
2457
+ 1. 支持按ID删除:可以单个ID或者ID数组 `需要实体类只有一个ID`
2458
+ 2. 支持实体类删除: 用于多个ID或者按实体类某些字段删除
2459
+ 3. 两种模式:`mode`=`Common` 或者 `TempTable`
2460
+ 3. 如果数据多,使用 `TempTable`模式
2461
+ 4. 当设置实体类的字段有 `logicDelete` ,将进行逻辑删除,除非设置 forceDelete = true
2462
+ 6. `tableName`: 默认使用service注解的`tableName`,可以在某个方法中覆盖
2463
+ 7. `dbName`: 默认使用service注解的`dbName`,可以在某个方法中覆盖
2464
+ 8. `conn`: 仅在开启事务时需要主动传入,传入示例:
2465
+ ```
2466
+ service.transaction(async conn => {
2467
+ service.insert({conn});
2468
+ });
2469
+ ```
2470
+ 9. `dao`: 永远不需要传入该值
2471
+ */
2472
+ delete(option: MethodOption & { sync?: SyncMode.Async; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: DeleteMode; forceDelete?: boolean; }): Promise<number>;
2473
+ delete(option: MethodOption & { sync: SyncMode.Sync; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: DeleteMode; forceDelete?: boolean; }): number;
2474
+ @P<T>()
2475
+ delete(
2476
+ option: MethodOption & {
2477
+ sync?: SyncMode,
2478
+ id?: string | number | Array<string | number>;
2479
+ where?: Partial<T> | Array<Partial<T>>;
2480
+ mode?: DeleteMode;
2481
+ forceDelete?: boolean;
2482
+ }
2483
+ ): Promise<number> | number {
2484
+ Throw.if(!!this[_ids] && this[_ids].length > 1 && !option.where, 'muit id must set where!');
2485
+ Throw.if((!this[_ids] || this[_ids].length === 0) && !option.where, 'if not set id on class, must set where!');
2486
+ Throw.if(!option.id && !option.where, 'not found id or where!');
2487
+ Throw.if(!!option.id && !!this[_ids] && this[_ids].length > 1, 'muit id must set where!');
2488
+ Throw.if(!!option.id && !!option.where, 'id and where only one can set!');
2489
+
2490
+ option.mode ??= DeleteMode.Common;
2491
+ const tableTemp = `${option?.tableName}_${Math.random()}`.replace(/\./, '');
2492
+ const tableTempESC = Sqlstring.escapeId(tableTemp);
2493
+ const tableNameESC = Sqlstring.escapeId(option?.tableName);
2494
+
2495
+ if (option.id) {
2496
+ const idName = this[_ids]![0]!;
2497
+ const ids = option.id instanceof Array ? option.id : [option.id];
2498
+ option.where = ids.map(i => ({ [idName]: i })) as Array<Partial<T>>;
2499
+ }
2500
+
2501
+ const wheres = option.where instanceof Array ? option.where : [option.where!];
2502
+
2503
+ const sqls: { sql: string; params?: any[] }[] = [];
2504
+ if (option.mode === DeleteMode.Common) {
2505
+ const params = new Array<any>();
2506
+ const whereSql = iterare(wheres).map(where => {
2507
+ return `(
2508
+ ${Object.entries(where).map(([K, V]) => {
2509
+ params.push(V);
2510
+ return `${K} = ?`;
2511
+ }).join(' AND ')}
2512
+ )`;
2513
+ }).join(' OR ');
2514
+ if (this[_stateFileName] !== undefined && option.forceDelete !== true) {
2515
+ sqls.push({
2516
+ sql: `
2517
+ UPDATE ${tableNameESC} SET ${this[_fields]![this[_stateFileName]]?.esName} = ${Sqlstring.escape(this[_deleteState])}
2518
+ WHERE ${whereSql};
2519
+ `, params
2520
+ });
2521
+ } else {
2522
+ sqls.push({ sql: `DELETE FROM ${tableNameESC} WHERE ${whereSql};`, params });
2523
+ }
2524
+ } else {
2525
+ sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
2526
+ const delWhere = Object.keys(wheres[0] as unknown as any);
2527
+ const _sqls = this._createTable({ tableName: tableTemp, temp: true, columns: delWhere, data: wheres, index: 'all', id: 'none' })!;
2528
+ sqls.push(..._sqls);
2529
+ switch (this[_dbType]) {
2530
+ case DBType.Mysql: {
2531
+ if (this[_stateFileName] !== undefined && option.forceDelete !== true) {
2532
+ sqls.push({
2533
+ sql: `UPDATE ${tableNameESC} a INNER JOIN ${tableTempESC} b ON ${delWhere.map(K => `a.${this[_fields]![K]?.esName} = b.${this[_fields]![K]?.esName}`).join(' AND ')}
2534
+ SET a.${this[_fields]![this[_stateFileName]]?.esName} = ${Sqlstring.escape(this[_deleteState])};`
2535
+ });
2536
+ } else {
2537
+ sqls.push({
2538
+ sql: `DELETE a.* FROM ${tableNameESC} a INNER JOIN ${tableTempESC} b ON ${delWhere.map(K => `a.${this[_fields]![K]?.esName} = b.${this[_fields]![K]?.esName}`).join(' AND ')};`
2539
+ });
2540
+ }
2541
+ }
2542
+ case DBType.Sqlite:
2543
+ case DBType.SqliteRemote: {
2544
+ const columnNames = iterare(delWhere).map(K => this[_fields]![K]?.esName).join(',');
2545
+ if (this[_stateFileName] !== undefined && option.forceDelete !== true) {
2546
+ sqls.push({
2547
+ sql: `UPDATE ${tableNameESC} SET ${this[_fields]![this[_stateFileName]]?.esName} = ${Sqlstring.escape(this[_deleteState])}
2548
+ WHERE (${columnNames}) IN (SELECT ${columnNames} FROM ${tableTempESC});`
2549
+ });
2550
+ } else {
2551
+ sqls.push({ sql: `DELETE FROM ${tableNameESC} WHERE (${columnNames}) IN (SELECT ${columnNames} FROM ${tableTempESC});` });
2552
+ }
2553
+ }
2554
+ }
2555
+ sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
2556
+ }
2557
+
2558
+ if (option.sync === SyncMode.Sync) {
2559
+ const fn = () => {
2560
+ let result = 0;
2561
+ for (const { sql, params } of sqls) {
2562
+ const dd = option!.conn!.execute(SyncMode.Sync, sql, params);
2563
+ result += dd.affectedRows;
2564
+ }
2565
+ return result;
2566
+ };
2567
+ if (option?.conn?.[_inTransaction] === true) {
2568
+ return fn();
2569
+ } else {
2570
+ return option?.dao?.transaction(SyncMode.Sync, fn, option?.conn)!;
2571
+ }
2572
+ } else {
2573
+ const fn = async () => {
2574
+ let result = 0;
2575
+ for (const { sql, params } of sqls) {
2576
+ const dd = await option!.conn!.execute(SyncMode.Async, sql, params);
2577
+ result += dd.affectedRows;
2578
+ }
2579
+ return result;
2580
+ };
2581
+ return new Promise<number>(async (resolve) => {
2582
+ if (option?.conn?.[_inTransaction] === true) {
2583
+ resolve((await fn())!);
2584
+ } else {
2585
+ await option?.dao?.transaction(SyncMode.Async, async () => resolve((await fn())!), option?.conn);
2586
+ }
2587
+ });
2588
+ }
2589
+ }
2590
+
2591
+ private _template(templateResult: TemplateResult, result: any, error?: string) {
2592
+ switch (templateResult) {
2593
+ case TemplateResult.AssertOne: {
2594
+ Throw.if(!result || result.length !== 1, error);
2595
+ return result[0] as T;
2596
+ }
2597
+ case TemplateResult.NotSureOne: {
2598
+ Throw.if(!result, error);
2599
+ return (result[0] as T) ?? null;
2600
+ }
2601
+ case TemplateResult.Many: {
2602
+ return result;
2603
+ }
2604
+ case TemplateResult.Count: {
2605
+ return result[0].ct;
2606
+ }
2607
+ }
2608
+ }
2609
+ /**
2610
+ #根据条件查询对象
2611
+ ## 特点:快速、简单,可快速根据某些字段是否等于来查询返回,可以查询记录和记录数
2612
+ 0. `sync`: 同步(sqlite)或者异步(mysql、remote),影响返回值类型,默认`异步模式`
2613
+ 1. `templateResult`: 返回值类型断言,4种
2614
+ 1. `AssertOne` 确定返回一个,如果不是一个,将报错,返回类型是T `默认`
2615
+ 2. `NotSureOne` 可能返回一个,返回类型是T|null
2616
+ 3. `Many` 返回多个
2617
+ 4. `Count` 返回记录数
2618
+ 2. 支持按ID查询:可以单个ID或者ID数组 `需要实体类只有一个ID`
2619
+ 3. 支持实体类查询: 用于多个ID或者按实体类某些字段查询
2620
+ 4. 两种查询方式:`mode`=`Common`(默认) 或者 `TempTable`
2621
+ 5. `tableName`: 默认使用service注解的`tableName`,可以在某个方法中覆盖
2622
+ 6. `dbName`: 默认使用service注解的`dbName`,可以在某个方法中覆盖
2623
+ 7. `conn`: 仅在开启事务时需要主动传入,传入示例:
2624
+ ```
2625
+ service.transaction(async conn => {
2626
+ service.insert({conn});
2627
+ });
2628
+ ```
2629
+ 8. `dao`: 永远不需要传入该值
2630
+
2631
+ */
2632
+ template(option: MethodOption & { templateResult?: TemplateResult.AssertOne; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<T>;
2633
+ template(option: MethodOption & { sync: SyncMode.Sync; templateResult?: TemplateResult.AssertOne; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): T;
2634
+ template(option: MethodOption & { sync: SyncMode.Async; templateResult?: TemplateResult.AssertOne; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<T>;
2635
+ template(option: MethodOption & { templateResult: TemplateResult.Count; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<number>;
2636
+ template(option: MethodOption & { sync: SyncMode.Sync; templateResult: TemplateResult.Count; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): number;
2637
+ template(option: MethodOption & { sync: SyncMode.Async; templateResult: TemplateResult.Count; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<number>;
2638
+ template(option: MethodOption & { templateResult: TemplateResult.NotSureOne; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<T | null>;
2639
+ template(option: MethodOption & { sync: SyncMode.Sync; templateResult: TemplateResult.NotSureOne; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): T | null;
2640
+ template(option: MethodOption & { sync: SyncMode.Async; templateResult: TemplateResult.NotSureOne; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<T | null>;
2641
+ template(option: MethodOption & { templateResult: TemplateResult.Many; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<T[]>;
2642
+ template(option: MethodOption & { sync: SyncMode.Sync; templateResult: TemplateResult.Many; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): T[];
2643
+ template(option: MethodOption & { sync: SyncMode.Async; templateResult: TemplateResult.Many; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): Promise<T[]>;
2644
+ @P<T>()
2645
+ template(option: MethodOption & { sync?: SyncMode; templateResult?: TemplateResult; id?: string | number | Array<string | number>; where?: Partial<T> | Array<Partial<T>>; mode?: SelectMode; error?: string; columns?: (keyof T)[]; }): number | T | null | T[] | Promise<number | T | null | T[]> {
2646
+ Throw.if(!!this[_ids] && this[_ids].length > 1 && !option.where, 'muit id must set where!');
2647
+ Throw.if((!this[_ids] || this[_ids].length === 0) && !option.where, 'if not set id on class, must set where!');
2648
+ Throw.if(!option.id && !option.where, 'not found id or where!');
2649
+ Throw.if(!!option.id && !!this[_ids] && this[_ids].length > 1, 'muit id must set where!');
2650
+ Throw.if(!!option.id && !!option.where, 'id and where only one can set!');
2651
+
2652
+ option.mode ??= SelectMode.Common;
2653
+ option.templateResult ??= TemplateResult.AssertOne;
2654
+ option.error ??= 'error data!';
2655
+ const tableTemp = `${option?.tableName}_${Math.random()}`.replace(/\./, '');
2656
+ const tableTempESC = Sqlstring.escapeId(tableTemp);
2657
+ const tableNameESC = Sqlstring.escapeId(option?.tableName);
2658
+
2659
+ if (option.id) {
2660
+ const idName = this[_ids]![0]!;
2661
+ const ids = option.id instanceof Array ? option.id : [option.id];
2662
+ option.where = ids.map(i => ({ [idName]: i })) as Array<Partial<T>>;
2663
+ }
2664
+ const columns = option.templateResult === TemplateResult.Count ? 'COUNT(1) ct' : iterare((option.columns as unknown as string[] ?? this[_columns])!).map((K: any) => `a.${this[_fields]![K]?.esName}`).join(',');
2665
+ const wheres = option.where instanceof Array ? option.where : [option.where!];
2666
+ const sqls: { sql: string; params?: any[] }[] = [];
2667
+ let resultIndex = -1;
2668
+ if (option.mode === SelectMode.Common) {
2669
+ const params = new Array<any>();
2670
+ const whereSql = iterare(wheres).map(where => {
2671
+ return `SELECT ${columns} FROM ${tableNameESC} a WHERE
2672
+ ${Object.entries(where).map(([K, V]) => {
2673
+ params.push(V);
2674
+ return `${K} = ?`;
2675
+ }).join(' AND ')}`;
2676
+ }).join(' UNION ALL ');
2677
+ sqls.push({ sql: whereSql, params });
2678
+ resultIndex = 0;
2679
+ } else {
2680
+ sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
2681
+ const delWhere = Object.keys(wheres[0] as unknown as any);
2682
+ const _sqls = this._createTable({ tableName: tableTemp, temp: true, columns: delWhere, data: wheres, index: 'all', id: 'none' })!;
2683
+ sqls.push(..._sqls);
2684
+ resultIndex = sqls.length;
2685
+ sqls.push({ sql: `SELECT ${columns} FROM ${tableNameESC} a INNER JOIN ${tableTempESC} b ON ${delWhere.map(K => `a.${this[_fields]![K]?.esName} = b.${this[_fields]![K]?.esName}`).join(' AND ')};` });
2686
+ sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
2687
+ }
2688
+
2689
+ if (option.sync === SyncMode.Sync) {
2690
+ let result: any;
2691
+ for (let i = 0; i < sqls.length; i++) {
2692
+ if (i === resultIndex) {
2693
+ result = option!.conn!.query(SyncMode.Sync, sqls[i]?.sql, sqls[i]?.params);
2694
+ } else {
2695
+ option!.conn!.execute(SyncMode.Sync, sqls[i]?.sql, sqls[i]?.params);
2696
+ }
2697
+ }
2698
+ return this._template(option.templateResult, result, option.error);
2699
+ } else {
2700
+ return new Promise<T | null | T[]>(async (resolve) => {
2701
+ let result: any;
2702
+ for (let i = 0; i < sqls.length; i++) {
2703
+ if (i === resultIndex) {
2704
+ result = await option!.conn!.query(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
2705
+ } else {
2706
+ await option!.conn!.execute(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
2707
+ }
2708
+ }
2709
+ resolve(this._template(option.templateResult!, result, option.error));
2710
+ });
2711
+ }
2712
+ }
2713
+
2714
+ private _select<L = T>(templateResult: SelectResult, result: any, def: L | null, errorMsg?: string, multiple?: boolean) {
2715
+ if (multiple === true) {
2716
+ switch (templateResult) {
2717
+ case SelectResult.One_Row_One_Column_NotSure: {
2718
+ try {
2719
+ return result.map((r: any) => Object.values(r)[0] as L);
2720
+ } catch (error) {
2721
+ }
2722
+ }
2723
+ case SelectResult.One_Row_One_Column_Assert: {
2724
+ try {
2725
+ return iterare(result).map((r: any) => Object.values(r)[0] as L).filter((r: L) => r !== null).toArray();
2726
+ } catch (error) {
2727
+ }
2728
+ }
2729
+ case SelectResult.One_Row_Many_Column_NotSure: {
2730
+ try {
2731
+ return result.map((r: any) => r[0] as L);
2732
+ } catch (error) {
2733
+ }
2734
+ }
2735
+ case SelectResult.One_Row_Many_Column_Assert: {
2736
+ try {
2737
+ return iterare(result).map((r: any) => r[0] as L).filter((r: L) => r !== null).toArray();
2738
+ } catch (error) {
2739
+ }
2740
+ }
2741
+ case SelectResult.Many_Row_One_Column: {
2742
+ try {
2743
+ return result.map((rx: any) => rx.map((r: any) => Object.values(r)[0] as L));
2744
+ } catch (error) {
2745
+ }
2746
+ }
2747
+ case SelectResult.Many_Row_Many_Column: {
2748
+ return result as L[];
2749
+ }
2750
+ }
2751
+ } else {
2752
+ switch (templateResult) {
2753
+ case SelectResult.One_Row_One_Column_NotSure: {
2754
+ try {
2755
+ return Object.values(result[0])[0] as L;
2756
+ } catch (error) {
2757
+ return def;
2758
+ }
2759
+ }
2760
+ case SelectResult.One_Row_One_Column_Assert: {
2761
+ try {
2762
+ return Object.values(result[0])[0] as L;
2763
+ } catch (error) {
2764
+ if (def !== undefined) return def;
2765
+ Throw.now(errorMsg ?? 'not found data!');
2766
+ }
2767
+ }
2768
+ case SelectResult.One_Row_Many_Column_NotSure: {
2769
+ return (result[0] as L) ?? null;
2770
+ }
2771
+ case SelectResult.One_Row_Many_Column_Assert: {
2772
+ const data = result[0] as L;
2773
+ Throw.if(data === null, errorMsg ?? 'not found data!');
2774
+ return data ?? null;
2775
+ }
2776
+ case SelectResult.Many_Row_One_Column: {
2777
+ try {
2778
+ return result.map((r: any) => Object.values(r)[0] as L);
2779
+ } catch (error) {
2780
+ return def;
2781
+ }
2782
+ }
2783
+ case SelectResult.Many_Row_Many_Column: {
2784
+ return result as L[];
2785
+ }
2786
+ }
2787
+ }
2788
+ }
2789
+ /**
2790
+ # 自由查询
2791
+ 0. `sync`: 同步(sqlite)或者异步(mysql、remote),影响返回值类型,默认`异步模式`
2792
+ 1. `templateResult`: 返回值类型断言,三种
2793
+ 1. One_Row_One_Column_Assert,
2794
+ 2. One_Row_One_Column_NotSure,
2795
+ 3. One_Row_Many_Column_Assert,
2796
+ 4. One_Row_Many_Column_NotSure,
2797
+ 5. Many_Row_One_Column,
2798
+ 6. Many_Row_Many_Column[默认]
2799
+ 2. `sql` 或者 `sqlid`
2800
+ 3. `params`
2801
+ 4. `defValue`: One_Row_One_Column 时有效
2802
+ 5. `multiple`: = true 时表示多重sql查询,且每个sql的结果都是相同的resultMode, 此时 `One_Row_One_Column` `Many_Row_One_Column` `One_Row_Many_Column` 将按多重数组解析,`Many_Row_Many_Column` 不变,此时需要自己手动指定返回类型.
2803
+ 此时 `One_Row_One_Column` 将返回多个单值组成的数组: `[1, 'ok', 3]`
2804
+ `One_Row_Many_Column` 将返回多个对象组成的数组: `[{ob1}, {ob2}, {ob3}]`
2805
+ `Many_Row_One_Column` 将返回多个单值数组组成的数组: `[[1,2,3], ['a', 'b']]`
2806
+ 6. `dbName`: 默认使用service注解的`dbName`,可以在某个方法中覆盖
2807
+ 7. `conn`: 仅在开启事务时需要主动传入,传入示例:
2808
+ ```
2809
+ service.transaction(async conn => {
2810
+ service.insert({conn});
2811
+ });
2812
+ ```
2813
+ 9. `dao`: 永远不需要传入该值
2814
+
2815
+ */
2816
+ select<L = T>(option: MethodOption & { sync?: SyncMode.Async; selectResult?: SelectResult.Many_Row_Many_Column | SelectResult.Many_Row_One_Column; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; isCount?: boolean; defValue?: L | null; multiple?: boolean; errorMsg?: string }): Promise<L[]>;
2817
+ select<L = T>(option: MethodOption & { sync?: SyncMode.Async; selectResult: SelectResult.One_Row_Many_Column_Assert | SelectResult.One_Row_One_Column_Assert; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; isCount?: boolean; defValue?: L | null; multiple?: boolean; errorMsg?: string; }): Promise<L>;
2818
+ select<L = T>(option: MethodOption & { sync?: SyncMode.Async; selectResult: SelectResult.One_Row_Many_Column_NotSure | SelectResult.One_Row_One_Column_NotSure; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; isCount?: boolean; defValue?: L | null; multiple?: boolean; errorMsg?: string }): Promise<L | null>;
2819
+ select<L = T>(option: MethodOption & { sync: SyncMode.Sync; selectResult?: SelectResult.Many_Row_Many_Column | SelectResult.Many_Row_One_Column; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; isCount?: boolean; defValue?: L | null; multiple?: boolean; errorMsg?: string }): L[];
2820
+ select<L = T>(option: MethodOption & { sync: SyncMode.Sync; selectResult: SelectResult.One_Row_Many_Column_Assert | SelectResult.One_Row_One_Column_Assert; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; isCount?: boolean; defValue?: L | null; multiple?: boolean; errorMsg?: string; }): L;
2821
+ select<L = T>(option: MethodOption & { sync: SyncMode.Sync; selectResult: SelectResult.One_Row_Many_Column_NotSure | SelectResult.One_Row_One_Column_NotSure; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; isCount?: boolean; defValue?: L | null; multiple?: boolean; errorMsg?: string }): L | null;
2822
+ @P<T>()
2823
+ select<L = T>(option: MethodOption & { sync?: SyncMode; selectResult?: SelectResult; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; isCount?: boolean; defValue?: L | null; multiple?: boolean; errorMsg?: string; }): null | L | L[] | Promise<null | L | L[]> {
2824
+ Throw.if(!option.sqlId && !option.sql, 'not found sql!');
2825
+ option.selectResult ??= SelectResult.Many_Row_Many_Column;
2826
+ option.defValue ??= null;
2827
+ const _params = Object.assign({}, option.context, option.params);
2828
+ option.sql ??= globalThis[_sqlCache].load(option.sqlId, { ctx: option.context, isCount: option.isCount, ..._params });
2829
+ logger.debug(option.sql);
2830
+ const params: any[] = [];
2831
+ const sql = option.sql?.replace(/\:(\w+)/g, (txt, key) => {
2832
+ if (_params.hasOwnProperty(key)) {
2833
+ const V = _params[key];
2834
+ params.push(V);
2835
+ return Sqlstring.escape(_params[key]);
2836
+ }
2837
+ return txt;
2838
+ });
2839
+ if (option.sync === SyncMode.Sync) {
2840
+ const result = option!.conn!.query(SyncMode.Sync, sql, params);
2841
+ return this._select<L>(option.selectResult, result, option.defValue, option.errorMsg, option.multiple);
2842
+ } else {
2843
+ return new Promise<L | null | L[]>(async (resolve) => {
2844
+ const result = await option!.conn!.query(SyncMode.Async, sql, params);
2845
+ resolve(this._select<L>(option.selectResult!, result, option.defValue!, option.errorMsg, option.multiple));
2846
+ });
2847
+ }
2848
+ }
2849
+
2850
+ /**
2851
+ # 自由执行sql
2852
+ 0. `sync`: 同步(sqlite)或者异步(mysql、remote),影响返回值类型,默认`异步模式`
2853
+ 1. `sql` 或者 `sqlid`
2854
+ 2. `params`
2855
+ 3. `dbName`: 默认使用service注解的`dbName`,可以在某个方法中覆盖
2856
+ 4. `conn`: 仅在开启事务时需要主动传入,传入示例:
2857
+ ```
2858
+ service.transaction(async conn => {
2859
+ service.insert({conn});
2860
+ });
2861
+ ```
2862
+ 5. `dao`: 永远不需要传入该值
2863
+
2864
+ */
2865
+ excute<L = T>(option: MethodOption & { sync?: SyncMode.Async; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; }): Promise<number>;
2866
+ excute<L = T>(option: MethodOption & { sync: SyncMode.Sync; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; }): number;
2867
+ @P<T>()
2868
+ excute<L = T>(option: MethodOption & { sync?: SyncMode; sqlId?: string; sql?: string; params?: Record<string, any>; context?: any; }): number | Promise<number> {
2869
+ Throw.if(!option.sqlId && !option.sql, 'not found sql!');
2870
+ const _params = Object.assign({}, option.context, option.params);
2871
+ option.sql ??= globalThis[_sqlCache].load(option.sqlId, { ctx: option.context, ..._params });
2872
+ logger.debug(option.sql);
2873
+ const params: any[] = [];
2874
+ const sql = option.sql?.replace(/\:(\w+)/g, (txt, key) => {
2875
+ if (_params.hasOwnProperty(key)) {
2876
+ const V = _params[key];
2877
+ params.push(V);
2878
+ return Sqlstring.escape(_params[key]);
2879
+ }
2880
+ return txt;
2881
+ });
2882
+ if (option.sync === SyncMode.Sync) {
2883
+ const result = option!.conn!.execute(SyncMode.Sync, sql, params);
2884
+ return result.affectedRows;
2885
+ } else {
2886
+ return new Promise<number>(async (resolve) => {
2887
+ const result = await option!.conn!.execute(SyncMode.Async, sql, params);
2888
+ resolve(result.affectedRows);
2889
+ });
2890
+ }
2891
+ }
2892
+
2893
+ /**
2894
+ ### 开启事务
2895
+ ### 这里面的所有数据方法,都必须传递CONN,否则会引起
2896
+ # 死锁
2897
+ ### 举例说明:
2898
+ #### 假设有两条代码,都操作同一个表A,其中代码1传了conn,但代码2没有传
2899
+ #### 代码1:插入数据,代码2:更新数据
2900
+ #### 二者操作的是不同的数据
2901
+ #### 以上为前提,开始分析:
2902
+ ** 当事务打开后,会创建一个连接1,开始执行代码1
2903
+ ** 代码1执行完毕,由于`transaction`方法尚未结束,所以不会提交事务。
2904
+ ** 代码1是插入数据,因此会导致全表锁
2905
+ ** 代码2开始执行,由于没有传入conn,所以会创建一个新的连接2
2906
+ ** 代码2执行时,会等待连接1的锁释放
2907
+ ** 但是连接1的锁是在`transaction`方法执行完后才会提交并释放锁,这导致死循环,开启死锁
2908
+ **
2909
+ */
2910
+ transaction<L = T>(option: MethodOption & { sync?: SyncMode.Async; fn: (conn: Connection) => Promise<L>; }): Promise<L | null>;
2911
+ transaction<L = T>(option: MethodOption & { sync: SyncMode.Sync; fn: (conn: Connection) => L; }): L | null;
2912
+ @P<T>(true)
2913
+ transaction<L = T>(option: MethodOption & { sync?: SyncMode; fn: (conn: Connection) => L | Promise<L>; }): L | null | Promise<L | null> {
2914
+ if (option.sync === SyncMode.Sync) {
2915
+ return option!.dao!.transaction(SyncMode.Sync, option.fn as (conn: Connection) => L)!;
2916
+ } else {
2917
+ return new Promise(async (resolve) => {
2918
+ const rt = await option!.dao!.transaction(SyncMode.Async, option.fn as (conn: Connection) => Promise<L>);
2919
+ resolve(rt);
2920
+ });
2921
+ }
2922
+ }
2923
+
2924
+ stream(option?: MethodOption) {
2925
+ return new StreamQuery(option?.tableName ?? this[_tableName]!, this);
2926
+ }
2927
+
2928
+ /**
2929
+ #创建表
2930
+ ** `tableName` 表名称
2931
+ ** `temp` 是否是临时表,默认true
2932
+ ** `columns` 字符串数组,默认是当前实体类全部字段,通过`columns` 可以创建部分字段临时表
2933
+ ** `id` 表的主键设置 4种:
2934
+ 1. `auto`: `columns`中已经在当前实体类配置的ID作为主键 `默认`
2935
+ 2. `all`: `columns`中所有字段全部当主键
2936
+ 3. `none`: 没有主键
2937
+ 4. 自定义字段名称:字符串数组
2938
+ ** `index` 表的索引,设置方式同ID
2939
+ */
2940
+ private _createTable({ tableName, temp = true, columns, data, id = 'auto', index = 'auto' }: {
2941
+ tableName?: string;
2942
+ temp?: boolean,
2943
+ columns?: string[];
2944
+ data?: Array<Partial<T>>;
2945
+ id?: 'auto' | 'all' | 'none' | string[];
2946
+ index?: 'auto' | 'all' | 'none' | string[];
2947
+ } = {}): { sql: string; params?: any[] }[] {
2948
+ const sqls: { sql: string; params?: any[] }[] = [];
2949
+ columns = columns || this[_columns]!;
2950
+ let ids: string[] | undefined;
2951
+ if (id === 'auto') {
2952
+ ids = this[_ids]?.filter(i => columns?.includes(i));
2953
+ } else if (id === 'all') {
2954
+ ids = columns;
2955
+ } else if (id === 'none') {
2956
+ ids = undefined;
2957
+ } else {
2958
+ ids = id;
2959
+ }
2960
+ let indexs: string[] | undefined;
2961
+ if (index === 'auto') {
2962
+ indexs = this[_index]?.filter(i => columns?.includes(i));
2963
+ } else if (index === 'all') {
2964
+ indexs = columns;
2965
+ } else if (index === 'none') {
2966
+ indexs = undefined;
2967
+ } else {
2968
+ indexs = index;
2969
+ }
2970
+ tableName = Sqlstring.escapeId(tableName ?? this[_tableName]);
2971
+ switch (this[_dbType]) {
2972
+ case DBType.Mysql: {
2973
+ let sql = `CREATE ${temp === true ? 'TEMPORARY' : ''} TABLE IF NOT EXISTS ${tableName}(
2974
+ ${columns.map(K => this[_fields]![K]![DBType.Mysql]).join(',')}
2975
+ ${ids && ids.length ? `,PRIMARY KEY (${ids.map(i => this[_fields]![i]?.esName).join(',')}) USING BTREE ` : ''}
2976
+ ${indexs && indexs.length ? `,${indexs.map(i => `KEY ${this[_fields]![i]?.esName} (${this[_fields]![i]?.esName})`).join(',')} ` : ''}
2977
+ ) ENGINE=MEMORY;`;
2978
+ sqls.push({ sql });
2979
+ if (data && data.length > 0) {
2980
+ const params: any[] = [];
2981
+ let first = true;
2982
+ sql = `INSERT INTO ${tableName} (${columns.map(c => Sqlstring.escapeId(c)).join(',')})
2983
+ ${(data).map(d => {
2984
+ const r = `SELECT ${Object.entries(d).map(([K, V]) => {
2985
+ params.push(V);
2986
+ return `? ${first ? this[_fields]![K]?.esName : ''}`;
2987
+ }).join(',')}`;
2988
+ first = false;
2989
+ return r;
2990
+ }).join(' UNION ALL ')}`;
2991
+ sqls.push({ sql, params });
2992
+ }
2993
+ break;
2994
+ }
2995
+ case DBType.Sqlite:
2996
+ case DBType.SqliteRemote: {
2997
+ let sql = `CREATE ${temp === true ? 'TEMPORARY' : ''} TABLE IF NOT EXISTS ${tableName}(
2998
+ ${columns.map(K => this[_fields]![K]![DBType.Sqlite]).join(',')}
2999
+ ${ids && ids.length ? `,PRIMARY KEY (${ids.map(i => this[_fields]![i]?.esName).join(',')}) ` : ''}
3000
+ );`;
3001
+ sqls.push({ sql });
3002
+ if (indexs) {
3003
+ for (const index of indexs) {
3004
+ sql = `CREATE INDEX ${Sqlstring.escapeId(`${index}_${Math.random()}`.replace(/\./, ''))} ON ${tableName} (${this[_fields]![index]?.esName});`;
3005
+ sqls.push({ sql });
3006
+ }
3007
+ }
3008
+ if (data && data.length > 0) {
3009
+ const params: any[] = [];
3010
+ let first = true;
3011
+ sql = `INSERT INTO ${tableName} (${columns.map(c => Sqlstring.escapeId(c)).join(',')})
3012
+ ${(data).map(d => {
3013
+ const r = `SELECT ${Object.entries(d).map(([K, V]) => {
3014
+ params.push(V);
3015
+ return `? ${first ? this[_fields]![K]?.esName : ''}`;
3016
+ }).join(',')}`;
3017
+ first = false;
3018
+ return r;
3019
+ }).join(' UNION ALL ')}`;
3020
+ sqls.push({ sql, params });
3021
+ }
3022
+ }
3023
+ }
3024
+ return sqls;
3025
+ }
3026
+
3027
+ }
3028
+ /** 是否进行下一个动作 */
3029
+ const IF_PROCEED = function <T extends object>() {
3030
+ return function (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
3031
+ const fn = descriptor.value;
3032
+ descriptor.value = function (this: StreamCondition<T>) {
3033
+ if (this.if_proceed === true) {
3034
+ // eslint-disable-next-line prefer-rest-params
3035
+ const args = Array.from(arguments);
3036
+ fn.call(this, ...args);
3037
+ } else {
3038
+ this.if_proceed = true;
3039
+ }
3040
+ return this;
3041
+ };
3042
+ };
3043
+ };
3044
+ /*** 是否执行最终查询/操作*/
3045
+ const IF_EXEC = function <T extends object>(def: any) {
3046
+ return function (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
3047
+ const fn = descriptor.value;
3048
+ descriptor.value = async function (this: StreamQuery<T>) {
3049
+ if (this.if_proceed === true && this.if_exec === true) {
3050
+ // eslint-disable-next-line prefer-rest-params
3051
+ const args = Array.from(arguments);
3052
+ return await fn.call(this, ...args);
3053
+ } else {
3054
+ return def;
3055
+ }
3056
+ };
3057
+ };
3058
+ };
3059
+ class StreamCondition<T extends object> {
3060
+ protected _prefix = 0;
3061
+ protected _index = 0;
3062
+
3063
+ protected _wheres: string[] = [];
3064
+
3065
+ protected _andQuerys: StreamCondition<T>[] = [];
3066
+ protected _orQuerys: StreamCondition<T>[] = [];
3067
+
3068
+ protected _paramKeys: Record<string, string[] | Record<string, string> | string> = {};
3069
+ protected _param: Record<string, any> = {};
3070
+ public if_proceed = true;
3071
+ public if_exec = true;
3072
+ constructor() {
3073
+ this._prefix = parseInt(`${Math.random() * 1000}`);
3074
+ }
3075
+ /** 将当前stream重置 */
3076
+ reset() {
3077
+ this._index = 0;
3078
+ this._wheres.length = 0;
3079
+ this._param = {};
3080
+ this._paramKeys = {};
3081
+ return this;
3082
+ }
3083
+ /** 为下次链条执行提供条件判断:非异步方法跳过,异步方法不执行并返回默认值 */
3084
+ @IF_PROCEED<T>()
3085
+ if(condition: boolean) {
3086
+ this.if_proceed = condition;
3087
+ return this;
3088
+ }
3089
+ @IF_PROCEED<T>()
3090
+ eq(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._(key, value, '=', { name }); }
3091
+ @IF_PROCEED<T>()
3092
+ eqT(t: Partial<T>, { name }: { name?: string } = {}) {
3093
+ if (name && this._paramKeys[name]) {
3094
+ for (const [key, pname] of Object.entries(this._paramKeys[name] as Record<string, string>)) {
3095
+ this._param[pname as string] = t[key];
3096
+ }
3097
+ } else {
3098
+ const paramKeys: Record<string, string> = {};
3099
+ for (const [key, value] of Object.entries(t)) {
3100
+ const pkey = `p${this._prefix}${this._index++}`;
3101
+ this._wheres.push(`AND ${String(key)} = :${pkey} `);
3102
+ this._param[pkey] = value;
3103
+ if (name) {
3104
+ paramKeys[key] = pkey;
3105
+ }
3106
+ }
3107
+ if (name) {
3108
+ this._paramKeys[name] = paramKeys;
3109
+ }
3110
+ }
3111
+ return this;
3112
+ }
3113
+ @IF_PROCEED<T>()
3114
+ notEq(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._(key, value, '<>', { name }); }
3115
+ @IF_PROCEED<T>()
3116
+ eqWith(key1: keyof T, key2: keyof T) { return this._key(key1, key2, '='); }
3117
+ @IF_PROCEED<T>()
3118
+ notEqWith(key1: keyof T, key2: keyof T) { return this._key(key1, key2, '<>'); }
3119
+ @IF_PROCEED<T>()
3120
+ regexp(key: keyof T, regexp: string, { name }: { name?: string } = {}) { return this._(key, regexp, 'REGEXP', { name }); }
3121
+ @IF_PROCEED<T>()
3122
+ notRegexp(key: keyof T, regexp: string, { name }: { name?: string } = {}) { return this._(key, regexp, 'REGEXP', { name, not: 'NOT' }); }
3123
+ /** (key1 << 8) + key2 = value */
3124
+ @IF_PROCEED<T>()
3125
+ shiftEq(key1: keyof T, key2: keyof T, value: number, { name }: { name?: string } = {}) { return this._shift(key1, key2, value, '=', { name }); }
3126
+ /** (key1 << 8) + key2 <> value */
3127
+ @IF_PROCEED<T>()
3128
+ shiftNotEq(key1: keyof T, key2: keyof T, value: number, { name }: { name?: string } = {}) { return this._shift(key1, key2, value, '<>', { name }); }
3129
+ @IF_PROCEED<T>()
3130
+ grate(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._(key, value, '>', { name }); }
3131
+ @IF_PROCEED<T>()
3132
+ grateEq(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._(key, value, '>=', { name }); }
3133
+ @IF_PROCEED<T>()
3134
+ grateWith(key1: keyof T, key2: keyof T) { return this._key(key1, key2, '>'); }
3135
+ @IF_PROCEED<T>()
3136
+ grateEqWith(key1: keyof T, key2: keyof T) { return this._key(key1, key2, '>='); }
3137
+ @IF_PROCEED<T>()
3138
+ less(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._(key, value, '<', { name }); }
3139
+ @IF_PROCEED<T>()
3140
+ lessEq(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._(key, value, '<=', { name }); }
3141
+ @IF_PROCEED<T>()
3142
+ lessWith(key1: keyof T, key2: keyof T) { return this._key(key1, key2, '<'); }
3143
+ @IF_PROCEED<T>()
3144
+ lessEqWith(key1: keyof T, key2: keyof T) { return this._key(key1, key2, '<='); }
3145
+ @IF_PROCEED<T>()
3146
+ like(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', right: '%', force }); }
3147
+ @IF_PROCEED<T>()
3148
+ notLike(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', right: '%', not: 'NOT', force }); }
3149
+ @IF_PROCEED<T>()
3150
+ leftLike(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', force }); }
3151
+ @IF_PROCEED<T>()
3152
+ notLeftLike(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', not: 'NOT', force }); }
3153
+ @IF_PROCEED<T>()
3154
+ rightLike(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, right: '%', force }); }
3155
+ @IF_PROCEED<T>()
3156
+ notRightLike(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, right: '%', not: 'NOT', force }); }
3157
+ @IF_PROCEED<T>()
3158
+ PreciseLike(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, force }); }
3159
+ @IF_PROCEED<T>()
3160
+ notPreciseLike(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, not: 'NOT', force }); }
3161
+ @IF_PROCEED<T>()
3162
+ glob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', right: '%', force, op: 'glob' }); }
3163
+ @IF_PROCEED<T>()
3164
+ notGlob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', right: '%', not: 'NOT', force, op: 'glob' }); }
3165
+ @IF_PROCEED<T>()
3166
+ leftGlob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', force, op: 'glob' }); }
3167
+ @IF_PROCEED<T>()
3168
+ notLeftGlob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, left: '%', not: 'NOT', force, op: 'glob' }); }
3169
+ @IF_PROCEED<T>()
3170
+ rightGlob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, right: '%', force, op: 'glob' }); }
3171
+ @IF_PROCEED<T>()
3172
+ notRightGlob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, right: '%', not: 'NOT', force, op: 'glob' }); }
3173
+ @IF_PROCEED<T>()
3174
+ PreciseGlob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, force, op: 'glob' }); }
3175
+ @IF_PROCEED<T>()
3176
+ notPreciseGlob(key: keyof T, value: string | number, { name, force }: { name?: string; force?: boolean; } = {}) { return this._like(key, value, { name, not: 'NOT', force, op: 'glob' }); }
3177
+ @IF_PROCEED<T>()
3178
+ in(key: keyof T, value: Array<string | number>, { name, force }: { name?: string; force?: boolean; } = {}) { return this._in(key, value, { name, force }); }
3179
+ @IF_PROCEED<T>()
3180
+ notIn(key: keyof T, value: Array<string | number>, { name, force }: { name?: string; force?: boolean; } = {}) { return this._in(key, value, { name, not: 'NOT', force }); }
3181
+ @IF_PROCEED<T>()
3182
+ isNULL(key: keyof T) { return this._null(key); }
3183
+ @IF_PROCEED<T>()
3184
+ isNotNULL(key: keyof T) { return this._null(key, 'NOT'); }
3185
+ @IF_PROCEED<T>()
3186
+ between(key: keyof T, value1: string | number, value2: string | number, { name }: { name?: string } = {}) { return this._between(key, value1, value2, { name }); }
3187
+ @IF_PROCEED<T>()
3188
+ notBetween(key: keyof T, value1: string | number, value2: string | number, { name }: { name?: string } = {}) { return this._between(key, value1, value2, { name, not: 'NOT' }); }
3189
+ @IF_PROCEED<T>()
3190
+ pow(key: keyof T, value: number, { name }: { name?: string } = {}) { return this._pow(key, value, { name }); }
3191
+ @IF_PROCEED<T>()
3192
+ notPow(key: keyof T, value: number, { name }: { name?: string } = {}) { return this._pow(key, value, { name, not: 'NOT' }); }
3193
+ @IF_PROCEED<T>()
3194
+ powWith(key: keyof T, values: Array<number | string>, { name }: { name?: string } = {}) { return this._pow(key, add(...values.map(value => Math.pow(2, +value))), { name }); }
3195
+ @IF_PROCEED<T>()
3196
+ notPowWith(key: keyof T, values: Array<number | string>, { name }: { name?: string } = {}) { return this._pow(key, add(...values.map(value => Math.pow(2, +value))), { name, not: 'NOT' }); }
3197
+ /** MATCH(key1, key2, key3) AGAINST (value) */
3198
+ @IF_PROCEED<T>()
3199
+ match(value: string, keys: (keyof T)[], { name }: { name?: string } = {}) { return this._match(value, keys, { name }); }
3200
+ /** NOT MATCH(key1, key2, key3) AGAINST (value) */
3201
+ @IF_PROCEED<T>()
3202
+ notMatch(value: string, keys: (keyof T)[], { name }: { name?: string } = {}) { return this._match(value, keys, { name, not: 'NOT' }); }
3203
+ /** MATCH(key1, key2, key3) AGAINST (value) IN BOOLEAN MODE*/
3204
+ @IF_PROCEED<T>()
3205
+ matchBoolean(value: string, keys: (keyof T)[], { name }: { name?: string } = {}) { return this._match(value, keys, { name, append: 'IN BOOLEAN MODE' }); }
3206
+ /** NOT MATCH(key1, key2, key3) AGAINST (value) IN BOOLEAN MODE */
3207
+ @IF_PROCEED<T>()
3208
+ notMatchBoolean(value: string, keys: (keyof T)[], { name }: { name?: string } = {}) { return this._match(value, keys, { name, not: 'NOT', append: 'IN BOOLEAN MODE' }); }
3209
+ /** MATCH(key1, key2, key3) AGAINST (value) WITH QUERY EXPANSION*/
3210
+ @IF_PROCEED<T>()
3211
+ matchQuery(value: string, keys: (keyof T)[], { name }: { name?: string } = {}) { return this._match(value, keys, { name, append: 'WITH QUERY EXPANSION' }); }
3212
+ /** NOT MATCH(key1, key2, key3) AGAINST (value) WITH QUERY EXPANSION*/
3213
+ @IF_PROCEED<T>()
3214
+ notMatchQuery(value: string, keys: (keyof T)[], { name }: { name?: string } = {}) { return this._match(value, keys, { name, not: 'NOT', append: 'WITH QUERY EXPANSION' }); }
3215
+ @IF_PROCEED<T>()
3216
+ includes(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._includes(key, value, { name }); }
3217
+ @IF_PROCEED<T>()
3218
+ notIncludes(key: keyof T, value: string | number, { name }: { name?: string } = {}) { return this._includes(key, value, { name, not: 'NOT' }); }
3219
+ @IF_PROCEED<T>()
3220
+ and(fn: (stream: StreamCondition<T>) => boolean | void) { const stream = new StreamCondition<T>(); const ret = fn(stream); if (ret !== false) { this._andQuerys.push(stream); } return this; }
3221
+ @IF_PROCEED<T>()
3222
+ or(fn: (stream: StreamCondition<T>) => boolean | void) { const stream = new StreamCondition<T>(); const ret = fn(stream); if (ret !== false) { this._orQuerys.push(stream); } return this; }
3223
+ protected where() {
3224
+ const wheres = new Array<string>();
3225
+ const sql = this._wheres.join(' ');
3226
+ if (sql) {
3227
+ wheres.push(`(${sql})`);
3228
+ }
3229
+ if (this._orQuerys.length > 0) {
3230
+ for (const query of this._orQuerys) {
3231
+ const { where, params } = query.where();
3232
+ if (where) {
3233
+ wheres.push(` OR (${where}) `);
3234
+ }
3235
+ Object.assign(this._param, params);
3236
+ }
3237
+ }
3238
+ if (this._andQuerys.length > 0) {
3239
+ for (const query of this._andQuerys) {
3240
+ const { where, params } = query.where();
3241
+ if (where) {
3242
+ wheres.push(` AND (${where}) `);
3243
+ }
3244
+ Object.assign(this._param, params);
3245
+ }
3246
+ }
3247
+ return { where: wheres.join(' '), params: this._param };
3248
+ }
3249
+ private _(key: keyof T, value: any, op: string, { not = '', name = '' } = {}) {
3250
+ if (name !== undefined && this._paramKeys.hasOwnProperty(name)) {
3251
+ this._param[this._paramKeys[name] as string] = value;
3252
+ } else {
3253
+ const pkey = `p${this._prefix}${this._index++}`;
3254
+ this._wheres.push(`AND ${String(key)} ${not} ${op} :${pkey} `);
3255
+ this._param[pkey] = value;
3256
+ if (name) {
3257
+ this._paramKeys[name] = pkey;
3258
+ }
3259
+ }
3260
+ return this;
3261
+ }
3262
+ private _null(key: keyof T, not = ''): this {
3263
+ this._wheres.push(`AND ${String(key)} is ${not} null`);
3264
+ return this;
3265
+ }
3266
+ private _key(key1: keyof T, key2: keyof T, op: string, not = '') {
3267
+ this._wheres.push(`AND ${String(key1)} ${not} ${op} ${String(key2)} `);
3268
+ return this;
3269
+ }
3270
+ private _between(key: keyof T, value1: string | number, value2: string | number, { not = '', name = '' } = {}) {
3271
+ if (name && this._paramKeys[name]) {
3272
+ this._param[this._paramKeys[name]![0]] = value1;
3273
+ this._param[this._paramKeys[name]![1]] = value2;
3274
+ } else {
3275
+ const [pkey1, pkey2] = [`p${this._prefix}${this._index++}`, `p${this._prefix}${this._index++}`];
3276
+ this._wheres.push(`AND ${String(key)} ${not} BETWEEN :${pkey1} AND :${pkey2}`);
3277
+ this._param[pkey1] = value1;
3278
+ this._param[pkey2] = value2;
3279
+ if (name) {
3280
+ this._paramKeys[name] = [pkey1, pkey2];
3281
+ }
3282
+ }
3283
+ return this;
3284
+ }
3285
+ private _in(key: keyof T, value: any, { not = '', name = '', force = false } = {}) {
3286
+ if (value && value.length > 0) {
3287
+ if (name !== undefined && this._paramKeys.hasOwnProperty(name)) {
3288
+ this._param[this._paramKeys[name] as string] = value;
3289
+ } else {
3290
+ const pkey = `p${this._prefix}${this._index++}`;
3291
+ this._wheres.push(`AND ${String(key)} ${not} IN (:${pkey}) `);
3292
+ this._param[pkey] = value;
3293
+ if (name) {
3294
+ this._paramKeys[name] = pkey;
3295
+ }
3296
+ }
3297
+ } else if (force !== true) {
3298
+ this.if_exec = false;
3299
+ }
3300
+ return this;
3301
+ }
3302
+ private _shift(key1: keyof T, key2: keyof T, value: number, op: string, { not = '', name = '' } = {}) {
3303
+ if (name !== undefined && this._paramKeys.hasOwnProperty(name)) {
3304
+ this._param[this._paramKeys[name] as string] = value;
3305
+ } else {
3306
+ const pkey = `p${this._prefix}${this._index++}`;
3307
+ this._wheres.push(`AND (${String(key1)} << 8) + ${String(key2)} ${not} ${op} :${pkey} `);
3308
+ this._param[pkey] = value;
3309
+ if (name) {
3310
+ this._paramKeys[name] = pkey;
3311
+ }
3312
+ }
3313
+ return this;
3314
+ }
3315
+ private _match(value: string, keys: (keyof T)[], { name, not, append }: { name?: string; not?: string; append?: string; } = {}) {
3316
+ if (name !== undefined && this._paramKeys.hasOwnProperty(name)) {
3317
+ this._param[this._paramKeys[name] as string] = value;
3318
+ } else {
3319
+ const pkey = `p${this._prefix}${this._index++}`;
3320
+ this._wheres.push(`AND MATCH(${keys.join(',')}) AGAINST (:${pkey} ${append ?? ''})`);
3321
+ this._param[pkey] = value;
3322
+ if (name) {
3323
+ this._paramKeys[name] = pkey;
3324
+ }
3325
+ }
3326
+ return this;
3327
+ }
3328
+ private _pow(key: keyof T, value: number, { name }: { name?: string; not?: string; } = {}) {
3329
+ if (name !== undefined && this._paramKeys.hasOwnProperty(name)) {
3330
+ this._param[this._paramKeys[name] as string] = value;
3331
+ } else {
3332
+ const pkey = `p${this._prefix}${this._index++}`;
3333
+ this._wheres.push(`AND NOT POW(2, ${String(key)}) & :${pkey}`);
3334
+ this._param[pkey] = value;
3335
+ if (name) {
3336
+ this._paramKeys[name] = pkey;
3337
+ }
3338
+ }
3339
+ return this;
3340
+ }
3341
+ private _like(key: keyof T, value: any, { not = '', left = '', right = '', name = '', op = 'LIKE', force = false } = {}) {
3342
+ if (value !== null && value !== undefined && value !== '') {
3343
+ if (name !== undefined && this._paramKeys.hasOwnProperty(name)) {
3344
+ this._param[this._paramKeys[name] as string] = value;
3345
+ } else {
3346
+ const pkey = `p${this._prefix}${this._index++}`;
3347
+ this._wheres.push(`AND ${String(key)} ${not} ${op} CONCAT('${left}', :${pkey}, '${right}') `);
3348
+ this._param[pkey] = value;
3349
+ if (name) {
3350
+ this._paramKeys[name] = pkey;
3351
+ }
3352
+ }
3353
+ } else if (force !== true) {
3354
+ this.if_exec = false;
3355
+ }
3356
+ return this;
3357
+ }
3358
+ private _includes(key: keyof T, value: any, { not = '', name = '' } = {}) {
3359
+ if (name !== undefined && this._paramKeys.hasOwnProperty(name)) {
3360
+ this._param[this._paramKeys[name] as string] = value;
3361
+ } else {
3362
+ const pkey = `p${this._prefix}${this._index++}`;
3363
+ this._wheres.push(`AND LOCATE(${String(key)}, :${pkey}) ${not ? '=' : ''} 0`);
3364
+ this._param[pkey] = value;
3365
+ if (name) {
3366
+ this._paramKeys[name] = pkey;
3367
+ }
3368
+ }
3369
+ return this;
3370
+ }
3371
+ }
3372
+ class StreamBuild<T extends object> extends StreamCondition<T>{
3373
+ protected _table: string;
3374
+
3375
+ protected _distinct = false;
3376
+ protected _columns: string[] = [];
3377
+
3378
+ protected _updates?: Partial<T>;
3379
+ protected _updateColumns: string[] = [];
3380
+
3381
+ protected _groups: (keyof T)[] = [];
3382
+ protected _orders: string[] = [];
3383
+
3384
+ protected _startRow = 0;
3385
+ protected _pageSize = 0;
3386
+
3387
+ constructor(table: string) {
3388
+ super();
3389
+ this._table = table;
3390
+ }
3391
+ /** 将当前stream重置 */
3392
+ reset() {
3393
+ super.reset();
3394
+ this._pageSize = 0;
3395
+ this._startRow = 0;
3396
+ this._orders.length = 0;
3397
+ this._groups.length = 0;
3398
+ this._columns.length = 0;
3399
+ this._updateColumns.length = 0;
3400
+ return this;
3401
+ }
3402
+ @IF_PROCEED<T>()
3403
+ groupBy(key: keyof T) { this._groups.push(key); return this; }
3404
+ @IF_PROCEED<T>()
3405
+ asc(...keys: (keyof T)[]) { this._orders.push(...keys.map(key => `${String(key)} ASC`)); return this; }
3406
+ @IF_PROCEED<T>()
3407
+ desc(...keys: (keyof T)[]) { this._orders.push(...keys.map(key => `${String(key)} DESC`)); return this; }
3408
+ @IF_PROCEED<T>()
3409
+ limit(startRow: number, pageSize: number) { this._startRow = startRow; this._pageSize = pageSize; return this; }
3410
+ @IF_PROCEED<T>()
3411
+ page(pageNumber: number, pageSize: number) { this._startRow = ((pageNumber || 1) - 1) * pageSize; this._pageSize = pageSize; return this; }
3412
+ @IF_PROCEED<T>()
3413
+ table(_table: string) { this._table = _table; return this; }
3414
+ @IF_PROCEED<T>()
3415
+ distinct(on = true) { this._distinct = on; return this; }
3416
+ @IF_PROCEED<T>()
3417
+ count(key: keyof T, countName?: string, distinct?: boolean) { this._columns.push(`COUNT(${distinct ? 'DISTINCT' : ''} ${String(key)}) ${countName || `${String(key)}`}`); return this; }
3418
+ @IF_PROCEED<T>()
3419
+ sum(key: keyof T, legName?: string, distinct?: boolean) { this._columns.push(`SUM(${distinct ? 'DISTINCT' : ''} ${String(key)}) ${legName || `${String(key)}`}`); return this; }
3420
+ @IF_PROCEED<T>()
3421
+ avg(key: keyof T, legName?: string, distinct?: boolean) { this._columns.push(`AVG(${distinct ? 'DISTINCT' : ''} ${String(key)}) ${legName || `${String(key)}`}`); return this; }
3422
+ @IF_PROCEED<T>()
3423
+ max(key: keyof T, legName?: string, distinct?: boolean) { this._columns.push(`MAX(${distinct ? 'DISTINCT' : ''} ${String(key)}) ${legName || `${String(key)}`}`); return this; }
3424
+ @IF_PROCEED<T>()
3425
+ min(key: keyof T, legName?: string, distinct?: boolean) { this._columns.push(`MIN(${distinct ? 'DISTINCT' : ''} ${String(key)}) ${legName || `${String(key)}`}`); return this; }
3426
+ @IF_PROCEED<T>()
3427
+ groupConcat(key: keyof T, param?: { distinct?: boolean, separator?: string, asc?: (keyof T)[], desc?: (keyof T)[], groupName?: string }): this {
3428
+ this._columns.push(`GROUP_CONCAT(
3429
+ ${param && param.distinct ? 'DISTINCT' : ''} ${String(key)}
3430
+ ${param && param.asc && param.asc.length > 0 ? `ORDER BY ${param.asc.map(i => `${String(i)} ASC`)} ` : ''}
3431
+ ${param && param.desc && param.desc.length > 0 ? `${param && param.asc && param.asc.length > 0 ? '' : 'ORDER BY'} ${param.desc.map(i => `${String(i)} DESC`)} ` : ''}
3432
+ SEPARATOR '${param && param.separator || ','}'
3433
+ ) ${param && param.groupName || `${String(key)}`}`);
3434
+ return this;
3435
+ }
3436
+ @IF_PROCEED<T>()
3437
+ selectColumn(...key: (keyof T)[]) { this._columns.push(...(key.map(k => k as string))); return this; }
3438
+ @IF_PROCEED<T>()
3439
+ updateColumn(key: keyof T, value: T[keyof T]) { this._updates ??= {}; this._updates[key] = value; return this; }
3440
+ @IF_PROCEED<T>()
3441
+ updateT(t: Partial<T>) { this._updates ??= {}; Object.assign(this._updates, t); return this; }
3442
+ @IF_PROCEED<T>()
3443
+ replace(key: keyof T, valueToFind: T[keyof T], valueToReplace: T[keyof T]) {
3444
+ const [pkey1, pkey2] = [`p${this._prefix}${this._index++}`, `p${this._prefix}${this._index++}`];
3445
+ this._updateColumns.push(` ${String(key)} = REPLACE(${String(key)}, :${pkey1}, :${pkey2}) `);
3446
+ this._param[pkey1] = valueToFind as any;
3447
+ this._param[pkey2] = valueToReplace as any;
3448
+ return this;
3449
+ }
3450
+ }
3451
+ class StreamQuery<T extends object> extends StreamBuild<T>{
3452
+ private _service: SqlService<T>;
3453
+ constructor(table: string, service: SqlService<T>) {
3454
+ super(table);
3455
+ this._service = service;
3456
+ }
3457
+ select<L = T>(option: { sync?: SyncMode.Async; selectResult: SelectResult.Many_Row_Many_Column | SelectResult.Many_Row_One_Column; }): Promise<L[]>;
3458
+ select<L = T>(option: { sync?: SyncMode.Async; selectResult: SelectResult.One_Row_Many_Column_Assert | SelectResult.One_Row_One_Column_Assert; errorMsg?: string; }): Promise<L>;
3459
+ select<L = T>(option: { sync?: SyncMode.Async; selectResult: SelectResult.One_Row_Many_Column_NotSure | SelectResult.One_Row_One_Column_NotSure; }): Promise<L | null>;
3460
+ select<L = T>(option: { sync: SyncMode.Sync; selectResult: SelectResult.Many_Row_Many_Column | SelectResult.Many_Row_One_Column; }): L[];
3461
+ select<L = T>(option: { sync: SyncMode.Sync; selectResult: SelectResult.One_Row_Many_Column_Assert | SelectResult.One_Row_One_Column_Assert; errorMsg?: string; }): L;
3462
+ select<L = T>(option: { sync: SyncMode.Sync; selectResult: SelectResult.One_Row_Many_Column_NotSure | SelectResult.One_Row_One_Column_NotSure; }): L | null;
3463
+ @IF_EXEC<T>(null)
3464
+ select<L = T>(option: { sync?: SyncMode; selectResult: SelectResult; errorMsg?: string; }): null | L | L[] | Promise<null | L | L[]> {
3465
+ option.sync ??= SyncMode.Async;
3466
+ const { where, params } = this.where();
3467
+ const sql = `
3468
+ SELECT
3469
+ ${this._columns && this._columns.length > 0 ? this._columns.join(',') : '*'}
3470
+ FROM ${this._table}
3471
+ ${where ? ' WHERE ' : ''}
3472
+ ${where}`;
3473
+
3474
+ if (option.sync === SyncMode.Async) {
3475
+ switch (option.selectResult) {
3476
+ case SelectResult.Many_Row_Many_Column: return this._service.select<L>({ sync: SyncMode.Async, selectResult: SelectResult.Many_Row_Many_Column, errorMsg: option.errorMsg, sql, params });
3477
+ case SelectResult.Many_Row_One_Column: return this._service.select<L>({ sync: SyncMode.Async, selectResult: SelectResult.Many_Row_One_Column, errorMsg: option.errorMsg, sql, params });
3478
+ case SelectResult.One_Row_Many_Column_Assert: return this._service.select<L>({ sync: SyncMode.Async, selectResult: SelectResult.One_Row_Many_Column_Assert, errorMsg: option.errorMsg, sql, params });
3479
+ case SelectResult.One_Row_One_Column_Assert: return this._service.select<L>({ sync: SyncMode.Async, selectResult: SelectResult.One_Row_One_Column_Assert, errorMsg: option.errorMsg, sql, params });
3480
+ case SelectResult.One_Row_Many_Column_NotSure: return this._service.select<L>({ sync: SyncMode.Async, selectResult: SelectResult.One_Row_Many_Column_NotSure, errorMsg: option.errorMsg, sql, params });
3481
+ case SelectResult.One_Row_One_Column_NotSure: return this._service.select<L>({ sync: SyncMode.Async, selectResult: SelectResult.One_Row_One_Column_NotSure, errorMsg: option.errorMsg, sql, params });
3482
+ }
3483
+ } else {
3484
+ switch (option.selectResult) {
3485
+ case SelectResult.Many_Row_Many_Column: return this._service.select<L>({ sync: SyncMode.Sync, selectResult: SelectResult.Many_Row_Many_Column, errorMsg: option.errorMsg, sql, params });
3486
+ case SelectResult.Many_Row_One_Column: return this._service.select<L>({ sync: SyncMode.Sync, selectResult: SelectResult.Many_Row_One_Column, errorMsg: option.errorMsg, sql, params });
3487
+ case SelectResult.One_Row_Many_Column_Assert: return this._service.select<L>({ sync: SyncMode.Sync, selectResult: SelectResult.One_Row_Many_Column_Assert, errorMsg: option.errorMsg, sql, params });
3488
+ case SelectResult.One_Row_One_Column_Assert: return this._service.select<L>({ sync: SyncMode.Sync, selectResult: SelectResult.One_Row_One_Column_Assert, errorMsg: option.errorMsg, sql, params });
3489
+ case SelectResult.One_Row_Many_Column_NotSure: return this._service.select<L>({ sync: SyncMode.Sync, selectResult: SelectResult.One_Row_Many_Column_NotSure, errorMsg: option.errorMsg, sql, params });
3490
+ case SelectResult.One_Row_One_Column_NotSure: return this._service.select<L>({ sync: SyncMode.Sync, selectResult: SelectResult.One_Row_One_Column_NotSure, errorMsg: option.errorMsg, sql, params });
3491
+ }
3492
+ }
3493
+ }
3494
+ update(option: { sync?: SyncMode.Async }): Promise<number>;
3495
+ update(option: { sync: SyncMode.Sync }): number;
3496
+ @IF_EXEC<T>(0)
3497
+ update(option: { sync?: SyncMode }): number | Promise<number> {
3498
+ option.sync ??= SyncMode.Async;
3499
+ const { where, params } = this.where();
3500
+ const sets = new Array<string>(...this._updateColumns);
3501
+ if (this._updates) {
3502
+ for (const [K, V] of Object.entries(this._updates)) {
3503
+ const pkey = `p${this._prefix}${this._index++}`;
3504
+ sets.push(` ${K} = :${pkey} `);
3505
+ params[pkey] = V;
3506
+ }
3507
+ }
3508
+ if (sets.length > 0) {
3509
+ const sql = `UPDATE ${this._table} SET ${sets.join(',')} ${where}`;
3510
+ if (option.sync === SyncMode.Async) {
3511
+ return this._service.excute({ sync: SyncMode.Async, sql, params });
3512
+ } else {
3513
+ return this._service.excute({ sync: SyncMode.Sync, sql, params });
3514
+ }
3515
+ } else {
3516
+ return 0;
3517
+ }
3518
+ }
3519
+ delete(option: { sync?: SyncMode.Async }): Promise<number>;
3520
+ delete(option: { sync: SyncMode.Sync }): number;
3521
+ @IF_EXEC<T>(0)
3522
+ delete(option: { sync?: SyncMode }): number | Promise<number> {
3523
+ option.sync ??= SyncMode.Async;
3524
+ const { where, params } = this.where();
3525
+ const sql = `DELETE FROM ${this._table} ${where}`;
3526
+ if (option.sync === SyncMode.Async) {
3527
+ return this._service.excute({ sync: SyncMode.Async, sql, params });
3528
+ } else {
3529
+ return this._service.excute({ sync: SyncMode.Sync, sql, params });
3530
+ }
3531
+ }
3532
+ }
3533
+ /**
3534
+ 获取REDIS客户端,
3535
+ # [查看库的API](https://github.com/redis/ioredis?tab=readme-ov-file)
3536
+ # [REDIS API](http://doc.redisfans.com/)
3537
+ REDIS 的API 可以直接用,将方法名转为小写
3538
+ */
3539
+ export function getRedisDB(db?: string) {
3540
+ const rd = globalThis[_dao][DBType.Redis][db ?? _primaryDB];
3541
+ Throw.if(!rd, 'not found redis!');
3542
+ return rd;
3543
+ }
3544
+ /**
3545
+ redlock
3546
+ */
3547
+ export async function GetRedisLock(key: string, lockMaxActive?: number) {
3548
+ const lock = globalThis[_dao][DBType.RedisLock];
3549
+ Throw.if(!lock, 'not found lock!');
3550
+ const db = getRedisDB();
3551
+ let initLock: any;
3552
+ try {
3553
+ initLock = await lock.acquire([`[lockex]${key}`], 5000);
3554
+ const count = await db.get(key);
3555
+ if (count === null || parseInt(count) < (lockMaxActive ?? 1)) {
3556
+ await db.incr(key);
3557
+ return true;
3558
+ } else {
3559
+ return false;
3560
+ }
3561
+ } catch (er: any) {
3562
+ return await GetRedisLock(key, lockMaxActive);
3563
+ } finally {
3564
+ if (initLock) {
3565
+ try {
3566
+ await initLock.release();
3567
+ // eslint-disable-next-line no-empty
3568
+ } catch (error: any) {
3569
+ }
3570
+ }
3571
+ }
3572
+ };
3573
+ export async function excuteWithLock<T>(config: {
3574
+ /** 返回缓存key,参数=方法的参数+当前用户对象,可以用来清空缓存。 */
3575
+ key: ((...args: any[]) => string) | string;
3576
+ /** 被锁定线程是否sleep直到解锁为止? 默认true */
3577
+ lockWait?: boolean;
3578
+ /** 当设置了lockWait=true时,等待多少【毫秒】进行一次锁查询? 默认:100MS */
3579
+ lockRetryInterval?: number;
3580
+ /** 当设置了lockWait=true时,等待多少【毫秒】即视为超时,放弃本次访问?默认:永不放弃 */
3581
+ lockMaxWaitTime?: number;
3582
+ /** 错误信息 */
3583
+ errorMessage?: string;
3584
+ /** 允许的并发数,默认:1 */
3585
+ lockMaxActive?: number;
3586
+ /** 单个锁多少【毫秒】后自动释放?默认:60*1000MS */
3587
+ lockMaxTime?: number;
3588
+ }, fn__: () => Promise<T>) {
3589
+ const key = `[lock]${typeof config.key === 'function' ? config.key() : config.key}`;
3590
+ const db = getRedisDB();
3591
+ let wait_time = 0;
3592
+ const fn = async () => {
3593
+ const lock = await GetRedisLock(key, config.lockMaxActive);
3594
+ if (lock === false) {
3595
+ if (config.lockWait !== false && ((config.lockMaxWaitTime ?? 0) === 0 || (wait_time + (config.lockRetryInterval ?? 100)) <= (config.lockMaxWaitTime ?? 0))) {
3596
+ logger.debug(`get lock ${key} fail, retry after ${config.lockRetryInterval ?? 100}ms...`);
3597
+ await sleep(config.lockRetryInterval ?? 100);
3598
+ wait_time += (config.lockRetryInterval ?? 100);
3599
+ return await fn();
3600
+ } else {
3601
+ logger.debug(`get lock ${key} fail`);
3602
+ throw new Error(config.errorMessage || `get lock fail: ${key}`);
3603
+ }
3604
+ } else {
3605
+ logger.debug(`get lock ${key} ok!`);
3606
+ await db.get('other').pexpire(key, config.lockMaxTime ?? 60000);
3607
+ try {
3608
+ return await fn__();
3609
+ } finally {
3610
+ logger.debug(`unlock ${key} ok!`);
3611
+ await db.get('other').decr(key);
3612
+ }
3613
+ }
3614
+ };
3615
+ return await fn();
3616
+ }
3617
+ /** 与缓存共用时,需要在缓存之前:有缓存则返回缓存,否则加锁执行并缓存,后续队列全部返回缓存,跳过执行 */
3618
+ export function MethodLock<T = any>(config: {
3619
+ /** 返回缓存key,参数=方法的参数[注意:必须和主方法的参数数量、完全一致,同时会追加一个当前用户对象]+当前用户对象,可以用来清空缓存。 */
3620
+ key: ((this: T, ...args: any[]) => string) | string;
3621
+ /** 被锁定线程是否sleep直到解锁为止? 默认true */
3622
+ lockWait?: boolean;
3623
+ /** 当设置了lockWait=true时,等待多少【毫秒】进行一次锁查询? 默认100ms */
3624
+ lockRetryInterval?: number;
3625
+ /** 当设置了lockWait=true时,等待多少【毫秒】即视为超时,放弃本次访问?默认永不放弃 */
3626
+ lockMaxWaitTime?: number;
3627
+ /** 错误信息 */
3628
+ errorMessage?: string;
3629
+ /** 允许的并发数,默认=1 */
3630
+ lockMaxActive?: number;
3631
+ /** 单个锁多少【毫秒】后自动释放?即时任务没有执行完毕或者没有主动释放锁? */
3632
+ lockMaxTime?: number;
3633
+ }) {
3634
+ return function (target: T, _propertyKey: string, descriptor: PropertyDescriptor) {
3635
+ const fn__ = descriptor.value;
3636
+ descriptor.value = async function (this: any, ...args: any[]) {
3637
+ config.key = typeof config.key === 'function' ? config.key.call(target, ...args) : config.key;
3638
+ return await excuteWithLock(config, async () => await fn__.call(this, ...args));
3639
+ };
3640
+ };
3641
+ }
3642
+ async function setMethodCache(
3643
+ config: {
3644
+ key: string;
3645
+ clearKey?: string[];
3646
+ /** 自动清空缓存的时间,单位分钟 */
3647
+ autoClearTime?: number;
3648
+ result: any;
3649
+ },
3650
+ devid?: string | false | undefined
3651
+ ) {
3652
+ const db = getRedisDB();
3653
+ if (config.result !== null && config.result !== undefined) {
3654
+ // 映射关系存放
3655
+ if (config.clearKey && config.clearKey.length > 0) {
3656
+ for (const clear of config.clearKey) {
3657
+ await db.sadd(`[cache-parent]${clear}`, config.key);
3658
+ await db.sadd(`[cache-child]${config.key}`, clear);
3659
+ }
3660
+ }
3661
+ if (config.autoClearTime) { // 自动清空
3662
+ await db.set(`[cache]${config.key}`, JSON.stringify(config.result), 'EX', config.autoClearTime * 60);
3663
+ // 订阅:清空 clear list
3664
+ if (config.clearKey && config.clearKey.length > 0) {
3665
+ globalThis[_EventBus].on(`[cache]${config.key}`, async (key: string) => {
3666
+ await clearChild(key, true);
3667
+ });
3668
+ }
3669
+ } else {
3670
+ await db.set(`[cache]${config.key}`, JSON.stringify(config.result));
3671
+ }
3672
+ if (devid) {
3673
+ // 订阅:清空 clear list
3674
+ globalThis[_EventBus].on(`user-${devid}`, async function (key: string) {
3675
+ await clearChild(key);
3676
+ });
3677
+ }
3678
+ }
3679
+ }
3680
+ export async function clearMethodCache(key: string) {
3681
+ const db = getRedisDB();
3682
+ let type = await db.type(`[cache-parent]${key}`);
3683
+ if (type === 'set') {
3684
+ await clearParent(key);
3685
+ }
3686
+ type = await db.type(`[cache]${key}`);
3687
+ if (type !== 'none') {
3688
+ await clearChild(key);
3689
+ }
3690
+ }
3691
+ async function clearChild(key: string, skipDel = false) {
3692
+ const db = getRedisDB();
3693
+ if (skipDel === false) {
3694
+ await db.del(`[cache]${key}`);
3695
+ }
3696
+ const childtype = await db.type(`[cache-child]${key}`);
3697
+ if (childtype === 'set') {
3698
+ const parentKeys = await db.smembers(`[cache-child]${key}`);
3699
+ for (const clear of parentKeys) {
3700
+ const type = await db.type(`[cache-parent]${clear}`);
3701
+ if (type === 'set') {
3702
+ await db.srem(`[cache-parent]${clear}`, key);
3703
+ }
3704
+ }
3705
+ await db.del(`[cache-child]${key}`);
3706
+ }
3707
+ }
3708
+ async function clearParent(clearKey: string) {
3709
+ const db = getRedisDB();
3710
+ const keys = await db.smembers(`[cache-parent]${clearKey}`);
3711
+ if (keys) {
3712
+ for (const key of keys) {
3713
+ logger.debug(`cache ${key} cleared!`);
3714
+ await clearChild(key);
3715
+ }
3716
+ }
3717
+ }
3718
+ export async function excuteWithCache<T>(config: {
3719
+ /** 返回缓存key,参数=方法的参数+当前用户对象,可以用来清空缓存。 */
3720
+ key: string;
3721
+ /** 返回缓存清除key,参数=方法的参数+当前用户对象,可以用来批量清空缓存 */
3722
+ clearKey?: string[];
3723
+ /** 自动清空缓存的时间,单位分钟 */
3724
+ autoClearTime?: number;
3725
+ /** 随着当前用户sesion的清空而一起清空 */
3726
+ clearWithSession?: boolean;
3727
+ }, fn: () => Promise<T>) {
3728
+ const db = getRedisDB();
3729
+ const cache = await db.get(`[cache]${config.key}`);
3730
+ if (cache) {
3731
+ logger.debug(`cache ${config.key} hit!`);
3732
+ return JSON.parse(cache as string);
3733
+ } else {
3734
+ logger.debug(`cache ${config.key} miss!`);
3735
+ const result = await fn();
3736
+ await setMethodCache({
3737
+ key: config.key,
3738
+ clearKey: config.clearKey,
3739
+ autoClearTime: config.autoClearTime,
3740
+ result
3741
+ });
3742
+ return result;
3743
+ }
3744
+ }
3745
+ export function MethodCache<T = any>(config: {
3746
+ /** 返回缓存key,参数=方法的参数[注意:必须和主方法的参数数量、完全一致,同时会追加一个当前用户对象]+当前用户对象,可以用来清空缓存。 */
3747
+ key: ((this: T, ...args: any[]) => string) | string;
3748
+ /** 返回缓存清除key,参数=方法的参数[注意:必须和主方法的参数数量、完全一致,同时会追加一个当前用户对象]+当前用户对象,可以用来批量清空缓存 */
3749
+ clearKey?: ((this: T, ...args: any[]) => string[]) | string[];
3750
+ /** 自动清空缓存的时间,单位分钟 */
3751
+ autoClearTime?: number;
3752
+ /** 随着当前用户sesion的清空而一起清空 */
3753
+ clearWithSession?: boolean;
3754
+ }) {
3755
+ return function (target: T, _propertyKey: string, descriptor: PropertyDescriptor) {
3756
+ const fn = descriptor.value;
3757
+ descriptor.value = async function (this: any, ...args: any[]) {
3758
+ const key = typeof config.key === 'function' ? config.key.call(this, ...args) : config.key;
3759
+ const db = getRedisDB();
3760
+ const cache = await db.get('other').get(`[cache]${key}`);
3761
+ if (cache) {
3762
+ logger.debug(`cache ${key} hit!`);
3763
+ return JSON.parse(cache);
3764
+ } else {
3765
+ logger.debug(`cache ${key} miss!`);
3766
+ const result = await fn.call(this, ...args);
3767
+ const clearKey = config.clearKey ? typeof config.clearKey === 'function' ? config.clearKey.call(this, ...args) : config.clearKey : undefined;
3768
+ await setMethodCache({
3769
+ key,
3770
+ clearKey,
3771
+ autoClearTime: config.autoClearTime,
3772
+ result
3773
+ }, config.clearWithSession && this.ctx.me && this.ctx.me.devid);
3774
+ return result;
3775
+ }
3776
+ };
3777
+ };
3778
+ }
3779
+
3780
+ class MUParser {
3781
+ static END = 1;
3782
+ private modelName: string;
3783
+ private linNumber = 0;
3784
+ private lastLine?: string;
3785
+ private lastlastLine?: string;
3786
+ private status = 0;
3787
+ private lineSeparator = '\n';
3788
+ private files: string[];
3789
+ constructor(modelName: string, file: string) {
3790
+ this.modelName = modelName;
3791
+ this.files = file.replace(/\r/g, '').split(this.lineSeparator);
3792
+ this.skipHeader();
3793
+ }
3794
+ next(): [string, string] | null {
3795
+ let sqlId: string | undefined = this.readSqlId();
3796
+ if (this.status === MUParser.END || !sqlId) {
3797
+ return null;
3798
+ }
3799
+ // 去掉可能的尾部空格
3800
+ sqlId = sqlId.trim();
3801
+ this.skipComment();
3802
+ if (this.status === MUParser.END) {
3803
+ return null;
3804
+ }
3805
+ const sql: string = this.readSql();
3806
+ return [`${this.modelName}.${sqlId}`, sql];
3807
+ }
3808
+ private skipHeader(): void {
3809
+ while (true) {
3810
+ const line: string | undefined = this.nextLine();
3811
+ if (!line) { return; }
3812
+ if (this.status === MUParser.END) { return; }
3813
+ if (line.startsWith('===')) { return; }
3814
+ }
3815
+ }
3816
+ private nextLine(): string | undefined {
3817
+ const line: string | undefined = this.files[this.linNumber];
3818
+ this.linNumber++;
3819
+ if (line === undefined) {
3820
+ this.status = MUParser.END;
3821
+ }
3822
+ // 保存最后读的俩行
3823
+ this.lastlastLine = this.lastLine;
3824
+ this.lastLine = line;
3825
+ return line;
3826
+ }
3827
+ private readSqlId(): string | undefined {
3828
+ return this.lastlastLine;
3829
+ }
3830
+ private skipComment(): void {
3831
+ let findComment = false;
3832
+ while (true) {
3833
+ let line: string | undefined = this.nextLine();
3834
+ if (this.status === MUParser.END || !line) {
3835
+ return;
3836
+ }
3837
+ line = line.trim();
3838
+ if (!findComment && line.length === 0) {
3839
+ continue;
3840
+ }
3841
+ if (line.startsWith('*')) {
3842
+ // 注释符号
3843
+ findComment = true;
3844
+ continue;
3845
+ } else {
3846
+ if (line.length === 0) {
3847
+ continue;
3848
+ } else if (line.startsWith('```') || line.startsWith('~~~')) {
3849
+ // 忽略以code block开头的符号
3850
+ continue;
3851
+ } else {
3852
+ // 注释结束
3853
+ return;
3854
+ }
3855
+ }
3856
+ }
3857
+ }
3858
+ private readSql(): string {
3859
+ const list: string[] = [];
3860
+ if (this.lastLine) {
3861
+ list.push(this.lastLine);
3862
+ while (true) {
3863
+ const line: string | undefined = this.nextLine();
3864
+ if (line) {
3865
+ if (this.status === MUParser.END) {
3866
+ return this.getBuildSql(list);
3867
+ }
3868
+ if (line.startsWith('===')) {
3869
+ // 删除下一个sqlId表示
3870
+ list.pop();
3871
+ return this.getBuildSql(list);
3872
+ }
3873
+ list.push(line);
3874
+ } else {
3875
+ return '';
3876
+ }
3877
+ }
3878
+ } else {
3879
+ return '';
3880
+ }
3881
+ }
3882
+ private getBuildSql(list: string[]): string {
3883
+ const sb: string[] = [];
3884
+ for (const str of list) {
3885
+ const s: string = str.trim();
3886
+ if (s.startsWith('```') || s.startsWith('~~~')) {
3887
+ // 忽略以code block开头的符号
3888
+ continue;
3889
+ }
3890
+ sb.push(str);
3891
+ }
3892
+ return sb.join(this.lineSeparator);
3893
+ }
3894
+ }
3895
+
3896
+ export const Boot = async function (options: GlobalSqlOption) {
3897
+ globalThis[_GlobalSqlOption] = Object.assign({}, _defOption, options);
3898
+ if (options.sqlDir) {
3899
+ globalThis[_path] = await import('path');
3900
+ globalThis[_fs] = await import('fs');
3901
+ }
3902
+ logger.level = options.log ?? 'info';
3903
+ globalThis[_sqlCache] = new SqlCache();
3904
+ if (options.sqlMap || options.sqlDir) {
3905
+ await globalThis[_sqlCache].init(options);
3906
+ }
3907
+ globalThis[_dao] = {
3908
+ [DBType.Mongo]: {},
3909
+ [DBType.Mysql]: {},
3910
+ [DBType.Sqlite]: {},
3911
+ [DBType.SqliteRemote]: {},
3912
+ [DBType.Redis]: {}
3913
+ };
3914
+ if (options.Mysql) {
3915
+ const { createPool } = await import('mysql2/promise');
3916
+ if (options.Mysql['host']) {
3917
+ globalThis[_dao][DBType.Mysql][_primaryDB] = new Mysql(createPool({
3918
+ ...options.Mysql,
3919
+ multipleStatements: true,
3920
+ decimalNumbers: true,
3921
+ supportBigNumbers: true
3922
+ }));
3923
+ } else {
3924
+ let flag = false;
3925
+ for (const [key, option] of Object.entries(options.Mysql)) {
3926
+ const db = new Mysql(createPool({
3927
+ ...option,
3928
+ multipleStatements: true,
3929
+ decimalNumbers: true,
3930
+ supportBigNumbers: true
3931
+ }));
3932
+ if (flag === false) {
3933
+ globalThis[_dao][DBType.Mysql][_primaryDB] = db;
3934
+ flag = true;
3935
+ }
3936
+ globalThis[_dao][DBType.Mysql][key] = db;
3937
+ }
3938
+ }
3939
+ }
3940
+ if (options.Sqlite) {
3941
+ const BetterSqlite3 = await import('better-sqlite3');
3942
+ if (typeof options.Sqlite === 'string') {
3943
+ globalThis[_dao][DBType.Sqlite][_primaryDB] = new Sqlite(new BetterSqlite3.default(options.Sqlite, { fileMustExist: false }));
3944
+ } else {
3945
+ let flag = false;
3946
+ for (const [key, fileName] of Object.entries(options.Sqlite)) {
3947
+ const db = new Sqlite(new BetterSqlite3.default(fileName, { fileMustExist: false }));
3948
+ if (flag === false) {
3949
+ globalThis[_dao][DBType.Sqlite][_primaryDB] = db;
3950
+ flag = true;
3951
+ }
3952
+ globalThis[_dao][DBType.Sqlite][key] = db;
3953
+ }
3954
+ }
3955
+ }
3956
+ if (options.SqliteRemote) {
3957
+ if (typeof options.SqliteRemote.db === 'string') {
3958
+ await options.SqliteRemote.service.initDB(options.SqliteRemote.db);
3959
+ globalThis[_dao][DBType.SqliteRemote][_primaryDB] = new SqliteRemote(options.SqliteRemote.service, options.SqliteRemote.db);
3960
+ } else {
3961
+ let flag = false;
3962
+ for (const [key, fileName] of Object.entries(options.SqliteRemote.db)) {
3963
+ await options.SqliteRemote.service.initDB(fileName);
3964
+ const db = new SqliteRemote(options.SqliteRemote.service, fileName);
3965
+ if (flag === false) {
3966
+ globalThis[_dao][DBType.SqliteRemote][_primaryDB] = db;
3967
+ flag = true;
3968
+ }
3969
+ globalThis[_dao][DBType.SqliteRemote][key] = db;
3970
+ }
3971
+ }
3972
+ }
3973
+ if (options.Redis) {
3974
+ const { Redis } = await import('ioredis');
3975
+ if (options.Redis['host']) {
3976
+ globalThis[_dao][DBType.Redis][_primaryDB] = new Redis(options.Redis);
3977
+ } else {
3978
+ let flag = false;
3979
+ for (const [key, option] of Object.entries(options.Redis)) {
3980
+ const db = new Redis(option);
3981
+ if (flag === false) {
3982
+ globalThis[_dao][DBType.Redis][_primaryDB] = db;
3983
+ flag = true;
3984
+ }
3985
+ globalThis[_dao][DBType.Redis][key] = db;
3986
+ }
3987
+ }
3988
+ const clients = Object.values(globalThis[_dao][DBType.Redis]) as any;
3989
+ const Redlock = await import('redlock');
3990
+ globalThis[_dao][DBType.RedisLock] = new Redlock.default(
3991
+ clients,
3992
+ {
3993
+ // The expected clock drift; for more details see:
3994
+ // http://redis.io/topics/distlock
3995
+ driftFactor: 0.01, // multiplied by lock ttl to determine drift time
3996
+
3997
+ // The max number of times Redlock will attempt to lock a resource
3998
+ // before erroring.
3999
+ retryCount: 10,
4000
+
4001
+ // the time in ms between attempts
4002
+ retryDelay: 200, // time in ms
4003
+
4004
+ // the max time in ms randomly added to retries
4005
+ // to improve performance under high contention
4006
+ // see https://www.awsarchitectureblog.com/2015/03/backoff.html
4007
+ retryJitter: 200, // time in ms
4008
+
4009
+ // The minimum remaining time on a lock before an extension is automatically
4010
+ // attempted with the `using` API.
4011
+ automaticExtensionThreshold: 500, // time in ms
4012
+ }
4013
+ );
4014
+ const { EventEmitter } = await import('events');
4015
+ const event = new EventEmitter({ captureRejections: true });
4016
+ event.on('error', error => {
4017
+ logger.error('event-bus', error);
4018
+ });
4019
+ globalThis[_EventBus] = event;
4020
+ }
4021
+ }