jj.js 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,564 @@
1
+ const Sql = require('./sql');
2
+
3
+ /**
4
+ * @typedef {import('../../types').DbConfigItem} DbConfigItem
5
+ * @typedef {import('mongodb').MongoClient} MongoClient
6
+ * @typedef {import('mongodb').Db} Db
7
+ * @typedef {import('mongodb').Collection} Collection
8
+ * @typedef {import('mongodb').ObjectId} ObjectId
9
+ * @typedef {import('../../types').FieldInfo} FieldInfo
10
+ * @typedef {Map<any, MongoClient>} ClientMap
11
+ */
12
+
13
+ //数据库连接
14
+ /**
15
+ * @type {ClientMap}
16
+ */
17
+ const clients = new Map();
18
+ //事务连接
19
+ const trans = new Map();
20
+ //事务嵌套
21
+ const nest = new Map();
22
+
23
+ /**
24
+ * @extends Sql
25
+ */
26
+ class Mongodb extends Sql
27
+ {
28
+ /**
29
+ * 连接数据库
30
+ * @public
31
+ * @param {DbConfigItem} config - 数据库配置标识或连接参数
32
+ * @returns {Promise<this>}
33
+ */
34
+ async connect(config) {
35
+ this._config = config;
36
+
37
+ let client = clients.get(this._config);
38
+ if(!client) {
39
+ const { MongoClient } = require('mongodb');
40
+ const url = `mongodb://${config.user}:${config.password}@${config.host}:${config.port || 27017}`;
41
+ client = new MongoClient(url);
42
+ await client.connect();
43
+ clients.set(this._config, client);
44
+ this.logger.sql(`MONGODB数据库连接创建成功:{all: ${clients.size}}`);
45
+ }
46
+
47
+ this._db = client.db(config.database);
48
+ return this;
49
+ }
50
+
51
+ /**
52
+ * 关闭数据库连接
53
+ * @public
54
+ * @returns {Promise<this>}
55
+ */
56
+ async close() {
57
+ const that = this;
58
+ return new Promise((resolve, reject) => {
59
+ if(clients.has(this._config)) {
60
+ const client = clients.get(this._config);
61
+ // @ts-ignore
62
+ client.close().then(() => {
63
+ clients.delete(this._config);
64
+ this.logger.sql(`MONGODB数据库连接关闭成功:{connectionTotal: ${clients.size}}`);
65
+ resolve(that);
66
+ }).catch(err => {
67
+ const message = 'MONGODB数据库连接关闭失败:' + err.message;
68
+ this.logger.sql(message);
69
+ this.logger.error(message);
70
+ reject(new Error('DbError: ' + message));
71
+ });
72
+ } else {
73
+ resolve(that);
74
+ }
75
+ });
76
+ }
77
+
78
+ /**
79
+ * 获取数据库连接
80
+ * @private
81
+ * @returns {Promise<Db>}
82
+ */
83
+ async _getConnect() {
84
+ return trans.get(this.ctx) || this._db;
85
+ }
86
+
87
+ /**
88
+ * 获取集合
89
+ * @private
90
+ * @param {string} collectionName - 集合名称
91
+ * @returns {Promise<Collection>}
92
+ */
93
+ async _getCollection(collectionName) {
94
+ const db = await this._getConnect();
95
+ return db.collection(collectionName);
96
+ }
97
+
98
+ /**
99
+ * 开启事务
100
+ * @public
101
+ * @param {function} fun
102
+ * @returns {Promise<string>}
103
+ */
104
+ async startTrans(fun) {
105
+ const db = await this._getConnect();
106
+ trans.set(this.ctx, db);
107
+ let trans_nest = nest.get(db) || 0;
108
+ nest.set(db, ++trans_nest);
109
+ if(trans_nest > 1) {
110
+ const message = `开启事务成功:{nest:${trans_nest}}`;
111
+ this.logger.sql(message);
112
+ if(typeof fun === 'function') {
113
+ try {
114
+ await fun();
115
+ await this.commit();
116
+ } catch(e) {
117
+ await this.rollback();
118
+ throw e;
119
+ }
120
+ }
121
+ return message;
122
+ }
123
+
124
+ return new Promise((resolve, reject) => {
125
+ db.startSession().then(session => {
126
+ session.startTransaction();
127
+ trans.set(this.ctx, db);
128
+ this._session = session;
129
+ const message = '开启事务成功!';
130
+ this.logger.sql(message);
131
+ if(typeof fun === 'function') {
132
+ try {
133
+ fun().then(async () => {
134
+ await this.commit();
135
+ resolve('事务执行成功!');
136
+ }).catch(async (e) => {
137
+ await this.rollback();
138
+ reject(e);
139
+ });
140
+ } catch(e) {
141
+ this.rollback();
142
+ reject(e);
143
+ }
144
+ } else {
145
+ resolve(message);
146
+ }
147
+ }).catch(err => {
148
+ const message = '开启事务失败:' + err.message;
149
+ this.logger.sql(message);
150
+ this.logger.error(message);
151
+ reject(new Error('DbError: ' + message));
152
+ });
153
+ });
154
+ }
155
+
156
+ /**
157
+ * 事务回滚
158
+ * @public
159
+ * @returns {Promise<string>}
160
+ */
161
+ async rollback() {
162
+ const db = await this._getConnect();
163
+ const trans_nest = nest.get(db) || 0;
164
+ if(trans_nest > 1) {
165
+ nest.set(db, trans_nest - 1);
166
+ const message = `事务回滚成功:{nest:${trans_nest}}`;
167
+ this.logger.sql(message);
168
+ return message;
169
+ } else {
170
+ nest.delete(db);
171
+ }
172
+
173
+ return new Promise((resolve, reject) => {
174
+ if(this._session) {
175
+ this._session.abortTransaction().then(() => {
176
+ this._session.endSession();
177
+ trans.delete(this.ctx);
178
+ this._session = null;
179
+ const message = '事务回滚成功!';
180
+ this.logger.sql(message);
181
+ resolve(message);
182
+ }).catch(err => {
183
+ reject(new Error('DbError: ' + err.message));
184
+ });
185
+ } else {
186
+ trans.delete(this.ctx);
187
+ const message = '事务回滚成功!';
188
+ this.logger.sql(message);
189
+ resolve(message);
190
+ }
191
+ });
192
+ }
193
+
194
+ /**
195
+ * 提交事务
196
+ * @public
197
+ * @returns {Promise<string>}
198
+ */
199
+ async commit() {
200
+ const db = await this._getConnect();
201
+ const trans_nest = nest.get(db) || 0;
202
+ if(trans_nest > 1) {
203
+ nest.set(db, trans_nest - 1);
204
+ const message = `事务提交成功:{nest:${trans_nest}}`;
205
+ this.logger.sql(message);
206
+ return message;
207
+ } else {
208
+ nest.delete(db);
209
+ }
210
+
211
+ return new Promise((resolve, reject) => {
212
+ if(this._session) {
213
+ this._session.commitTransaction().then(() => {
214
+ this._session && this._session.endSession();
215
+ trans.delete(this.ctx);
216
+ this._session = null;
217
+ const message = '事务提交成功!';
218
+ this.logger.sql(message);
219
+ resolve(message);
220
+ }).catch(err => {
221
+ const message = '事务提交失败:' + err.message;
222
+ this.logger.sql(message);
223
+ this.logger.error(message);
224
+ this.rollback();
225
+ reject(new Error('DbError: ' + message));
226
+ });
227
+ } else {
228
+ trans.delete(this.ctx);
229
+ const message = '事务提交成功!';
230
+ this.logger.sql(message);
231
+ resolve(message);
232
+ }
233
+ });
234
+ }
235
+
236
+ /**
237
+ * 执行sql查询
238
+ * @public
239
+ * @param {string} sql - sql语句或参数
240
+ * @param {*} params - sql参数
241
+ * @returns {Promise<any>}
242
+ */
243
+ async query(sql, params) {
244
+ params || (params = []);
245
+
246
+ // MongoDB不支持SQL语句,这里需要解析SQL语句并转换为MongoDB操作
247
+ // 由于SQL语句的复杂性,这里只实现基本的SELECT、INSERT、UPDATE、DELETE操作
248
+ const sqlLower = sql.toLowerCase();
249
+
250
+ // 解析表名
251
+ let collectionName = '';
252
+ const tableMatch = sql.match(/from\s+([\w_]+)/i);
253
+ if(tableMatch) {
254
+ collectionName = tableMatch[1];
255
+ } else {
256
+ const insertMatch = sql.match(/insert\s+into\s+([\w_]+)/i);
257
+ if(insertMatch) {
258
+ collectionName = insertMatch[1];
259
+ } else {
260
+ const updateMatch = sql.match(/update\s+([\w_]+)/i);
261
+ if(updateMatch) {
262
+ collectionName = updateMatch[1];
263
+ } else {
264
+ const deleteMatch = sql.match(/delete\s+from\s+([\w_]+)/i);
265
+ if(deleteMatch) {
266
+ collectionName = deleteMatch[1];
267
+ }
268
+ }
269
+ }
270
+ }
271
+
272
+ // 移除表前缀
273
+ if(this._config && this._config.prefix) {
274
+ collectionName = collectionName.replace(this._config.prefix, '');
275
+ }
276
+
277
+ const collection = await this._getCollection(collectionName);
278
+
279
+ // 处理SELECT查询
280
+ if(sqlLower.startsWith('select')) {
281
+ return await this._handleSelect(sql, params, collection);
282
+ }
283
+ // 处理INSERT操作
284
+ else if(sqlLower.startsWith('insert')) {
285
+ return await this._handleInsert(sql, params, collection);
286
+ }
287
+ // 处理UPDATE操作
288
+ else if(sqlLower.startsWith('update')) {
289
+ return await this._handleUpdate(sql, params, collection);
290
+ }
291
+ // 处理DELETE操作
292
+ else if(sqlLower.startsWith('delete')) {
293
+ return await this._handleDelete(sql, params, collection);
294
+ }
295
+ // 处理其他操作
296
+ else {
297
+ const message = 'MongoDB不支持的SQL操作:' + sql;
298
+ this.logger.sql(message);
299
+ this.logger.error(message);
300
+ throw new Error('DbError: ' + message);
301
+ }
302
+ }
303
+
304
+ /**
305
+ * 处理SELECT查询
306
+ * @private
307
+ * @param {string} sql - sql语句
308
+ * @param {*} params - sql参数
309
+ * @param {Collection} collection - 集合
310
+ * @returns {Promise<any[]>}
311
+ */
312
+ async _handleSelect(sql, params, collection) {
313
+ // 解析查询字段
314
+ const fieldMatch = sql.match(/select\s+([\s\S]+?)\s+from/i);
315
+ /** @type {Record<string, number>} */
316
+ let fields = {};
317
+ if(fieldMatch) {
318
+ const fieldStr = fieldMatch[1];
319
+ if(fieldStr !== '*') {
320
+ fieldStr.split(',').forEach(field => {
321
+ field = field.trim();
322
+ if(field) {
323
+ fields[field] = 1;
324
+ }
325
+ });
326
+ }
327
+ }
328
+
329
+ // 解析查询条件
330
+ const whereMatch = sql.match(/where\s+([\s\S]+?)(?:\s+(group|order|limit))?/i);
331
+ /** @type {Record<string, any>} */
332
+ let filter = {};
333
+ if(whereMatch) {
334
+ // 这里简化处理,实际项目中需要更复杂的解析
335
+ filter = this._parseWhere(whereMatch[1], params);
336
+ }
337
+
338
+ // 解析排序
339
+ const orderMatch = sql.match(/order\s+by\s+([\s\S]+?)(?:\s+limit)?/i);
340
+ /** @type {Record<string, 1 | -1>} */
341
+ let sort = {};
342
+ if(orderMatch) {
343
+ const orderStr = orderMatch[1];
344
+ orderStr.split(',').forEach(item => {
345
+ item = item.trim();
346
+ if(item) {
347
+ const [field, direction] = item.split(/\s+/);
348
+ sort[field] = direction && direction.toLowerCase() === 'desc' ? -1 : 1;
349
+ }
350
+ });
351
+ }
352
+
353
+ // 解析限制
354
+ const limitMatch = sql.match(/limit\s+(\d+)(?:\s*,\s*(\d+))?/i);
355
+ let skip = 0;
356
+ let limit = 0;
357
+ if(limitMatch) {
358
+ skip = parseInt(limitMatch[1]) || 0;
359
+ limit = parseInt(limitMatch[2]) || 0;
360
+ }
361
+
362
+ // 执行查询
363
+ let query = collection.find(filter, { projection: fields });
364
+
365
+ // 应用排序
366
+ if(Object.keys(sort).length > 0) {
367
+ query = query.sort(sort);
368
+ }
369
+
370
+ // 应用限制
371
+ if(skip > 0) {
372
+ query = query.skip(skip);
373
+ }
374
+ if(limit > 0) {
375
+ query = query.limit(limit);
376
+ }
377
+
378
+ // 执行查询并返回结果
379
+ const result = await query.toArray();
380
+
381
+ // 转换ObjectId为字符串
382
+ return result.map(item => {
383
+ if(item._id) {
384
+ // @ts-ignore - 将 _id 转换为字符串
385
+ item._id = item._id.toString();
386
+ }
387
+ return item;
388
+ });
389
+ }
390
+
391
+ /**
392
+ * 处理INSERT操作
393
+ * @private
394
+ * @param {string} sql - sql语句
395
+ * @param {*} params - sql参数
396
+ * @param {Collection} collection - 集合
397
+ * @returns {Promise<{affectedRows: number, insertId: string}>}
398
+ */
399
+ async _handleInsert(sql, params, collection) {
400
+ // 解析插入数据
401
+ const valuesMatch = sql.match(/values\s*\(([^)]+)\)/i);
402
+ if(valuesMatch) {
403
+ // 构建插入数据
404
+ /** @type {Record<string, any>} */
405
+ const data = {};
406
+ const fieldsMatch = sql.match(/\(([^)]+)\)\s*values/i);
407
+ if(fieldsMatch) {
408
+ const fields = fieldsMatch[1].split(',').map(field => field.trim());
409
+ fields.forEach((field, index) => {
410
+ data[field] = params[index];
411
+ });
412
+ }
413
+
414
+ // 执行插入
415
+ const result = await collection.insertOne(data);
416
+
417
+ return {
418
+ affectedRows: 1,
419
+ insertId: result.insertedId.toString()
420
+ };
421
+ }
422
+
423
+ throw new Error('DbError: 无法解析INSERT语句');
424
+ }
425
+
426
+ /**
427
+ * 处理UPDATE操作
428
+ * @private
429
+ * @param {string} sql - sql语句
430
+ * @param {*} params - sql参数
431
+ * @param {Collection} collection - 集合
432
+ * @returns {Promise<{affectedRows: number}>}
433
+ */
434
+ async _handleUpdate(sql, params, collection) {
435
+ // 解析更新条件
436
+ const whereMatch = sql.match(/where\s+([\s\S]+)/i);
437
+ /** @type {Record<string, any>} */
438
+ let filter = {};
439
+ if(whereMatch) {
440
+ filter = this._parseWhere(whereMatch[1], params.slice(-1));
441
+ }
442
+
443
+ // 解析更新数据
444
+ const setMatch = sql.match(/set\s+([\s\S]+?)\s+where/i);
445
+ if(setMatch) {
446
+ const setStr = setMatch[1];
447
+ /** @type {Record<string, any>} */
448
+ const update = {};
449
+ const setFields = setStr.split(',').map(field => field.trim());
450
+ setFields.forEach((field, index) => {
451
+ const [key, value] = field.split('=');
452
+ update[key.trim()] = params[index];
453
+ });
454
+
455
+ // 执行更新
456
+ const result = await collection.updateMany(filter, { $set: update });
457
+
458
+ return {
459
+ affectedRows: result.modifiedCount
460
+ };
461
+ }
462
+
463
+ throw new Error('DbError: 无法解析UPDATE语句');
464
+ }
465
+
466
+ /**
467
+ * 处理DELETE操作
468
+ * @private
469
+ * @param {string} sql - sql语句
470
+ * @param {*} params - sql参数
471
+ * @param {Collection} collection - 集合
472
+ * @returns {Promise<{affectedRows: number}>}
473
+ */
474
+ async _handleDelete(sql, params, collection) {
475
+ // 解析删除条件
476
+ const whereMatch = sql.match(/where\s+([\s\S]+)/i);
477
+ /** @type {Record<string, any>} */
478
+ let filter = {};
479
+ if(whereMatch) {
480
+ filter = this._parseWhere(whereMatch[1], params);
481
+ }
482
+
483
+ // 执行删除
484
+ const result = await collection.deleteMany(filter);
485
+
486
+ return {
487
+ affectedRows: result.deletedCount
488
+ };
489
+ }
490
+
491
+ /**
492
+ * 解析WHERE条件
493
+ * @private
494
+ * @param {string} whereStr - WHERE条件字符串
495
+ * @param {*} params - sql参数
496
+ * @returns {Record<string, any>}
497
+ */
498
+ _parseWhere(whereStr, params) {
499
+ /** @type {Record<string, any>} */
500
+ const filter = {};
501
+
502
+ // 这里简化处理,实际项目中需要更复杂的解析
503
+ // 只处理基本的等于条件
504
+ const conditions = whereStr.split('and').map(condition => condition.trim());
505
+ conditions.forEach((condition, index) => {
506
+ const [field, value] = condition.split('=');
507
+ if(field && value) {
508
+ const fieldName = field.trim();
509
+ // 处理参数占位符
510
+ if(value.trim() === '?') {
511
+ filter[fieldName] = params[index];
512
+ } else {
513
+ // 处理字符串字面量
514
+ if(value.trim().startsWith("'")) {
515
+ filter[fieldName] = value.trim().replace(/'/g, '');
516
+ }
517
+ // 处理数字字面量
518
+ else if(!isNaN(Number(value.trim()))) {
519
+ filter[fieldName] = parseInt(value.trim());
520
+ }
521
+ }
522
+ }
523
+ });
524
+
525
+ return filter;
526
+ }
527
+
528
+ /**
529
+ * 序列化sql语句
530
+ * @public
531
+ * @param {string} sql - sql语句或参数
532
+ * @param {*} params - sql参数
533
+ * @returns {string}
534
+ */
535
+ format(sql, params) {
536
+ params || (params = []);
537
+
538
+ let formatted = sql;
539
+ for(let i = 0; i < params.length; i++) {
540
+ let value = params[i];
541
+ if (typeof value === 'string') {
542
+ value = "'" + value.replace(/'/g, "''") + "'";
543
+ } else if (value === null) {
544
+ value = 'NULL';
545
+ }
546
+ formatted = formatted.replace(/\?/, value);
547
+ }
548
+ return formatted;
549
+ }
550
+
551
+ /**
552
+ * 获取数据表信息
553
+ * @public
554
+ * @param {string} tableName - 表名字
555
+ * @returns {Promise<FieldInfo[]>}
556
+ */
557
+ async tableInfo(tableName) {
558
+ // MongoDB没有固定的表结构,这里返回一个空数组
559
+ // 实际项目中可以根据集合的文档结构动态生成
560
+ return [];
561
+ }
562
+ }
563
+
564
+ module.exports = Mongodb;