mm_mysql 2.1.1 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. package/README.md +202 -35
  2. package/db.js +253 -198
  3. package/index.js +403 -158
  4. package/package.json +2 -2
  5. package/sql.js +190 -349
  6. package/test.js +218 -128
package/db.js CHANGED
@@ -1,4 +1,6 @@
1
- const Sql = require('./sql');
1
+ const {
2
+ Sql
3
+ } = require('./sql');
2
4
 
3
5
  /**
4
6
  * @class 数据库操作类
@@ -10,7 +12,11 @@ class DB extends Sql {
10
12
  * @param {Object} mysql - MySQL实例
11
13
  */
12
14
  constructor(mysql) {
13
- super(mysql.run, mysql.exec);
15
+ // 正确绑定runexec方法的上下文
16
+ super(
17
+ mysql.run ? mysql.run.bind(mysql) : null,
18
+ mysql.exec ? mysql.exec.bind(mysql) : null
19
+ );
14
20
  // 保存mysql实例引用
15
21
  this._mysql = mysql;
16
22
  // 事务中
@@ -23,7 +29,7 @@ class DB extends Sql {
23
29
  * 获取上级
24
30
  * @return {Object} 返回上级管理器
25
31
  */
26
- this.parent = function() {
32
+ this.parent = function () {
27
33
  return mysql;
28
34
  };
29
35
  }
@@ -36,7 +42,7 @@ class DB extends Sql {
36
42
  * @param {String} message - 错误信息
37
43
  * @returns {Promise<Error>}
38
44
  */
39
- DB.prototype._createTimeoutPromise = function(timeout, message) {
45
+ DB.prototype._createTimeoutPromise = function (timeout, message) {
40
46
  return new Promise((_, reject) => {
41
47
  setTimeout(() => {
42
48
  reject(new Error(message));
@@ -48,7 +54,7 @@ DB.prototype._createTimeoutPromise = function(timeout, message) {
48
54
  * 获取数据库名
49
55
  * @return {String} 数据库
50
56
  */
51
- DB.prototype.database = function() {
57
+ DB.prototype.database = function () {
52
58
  return this.parent().config.database;
53
59
  };
54
60
 
@@ -58,7 +64,7 @@ DB.prototype.database = function() {
58
64
  * @param {String} key 键名
59
65
  * @return {Object} 返回管理器
60
66
  */
61
- DB.prototype.new = function(table, key) {
67
+ DB.prototype.new = function (table, key) {
62
68
  const db = this.parent().db();
63
69
  db.table = table;
64
70
  if (key) {
@@ -72,157 +78,139 @@ DB.prototype.new = function(table, key) {
72
78
 
73
79
  /**
74
80
  * 执行SQL查询
75
- * @param {string} sql SQL语句
76
- * @param {Array} params 参数数组
77
- * @param {Number} timeout 超时时间(毫秒)
78
- * @returns {Promise<Object>} 查询结果
81
+ * @param {String} sql - SQL语句
82
+ * @param {Array} params - 参数数组
83
+ * @returns {Promise<Array>}
84
+ * @throws {TypeError} 当sql参数无效时
79
85
  */
80
- DB.prototype.run = async function(sql, params = [], timeout = 30000) {
81
- if (!this._mysql) {
82
- throw new Error('MySQL实例未初始化');
83
- }
84
- try {
85
- // 使用Promise.race实现超时控制
86
- return await Promise.race([
87
- this._mysql.run(sql, params),
88
- this._createTimeoutPromise(timeout, `查询执行超时: ${sql.substring(0, 100)}...`)
89
- ]);
90
- } catch (error) {
91
- $.log.error(`[DB] [run] 查询执行失败`, {
92
- error: error.message,
93
- sql: sql.substring(0, 200),
94
- params: JSON.stringify(params.slice(0, 5))
95
- });
96
- throw error;
97
- }
86
+ DB.prototype.run = async function(sql, params = []) {
87
+ // 参数校验
88
+ if (typeof sql !== 'string' || sql.trim() === '') {
89
+ throw new TypeError('sql must be non-empty string');
90
+ }
91
+ if (!Array.isArray(params)) {
92
+ throw new TypeError('params must be array');
93
+ }
94
+
95
+ try {
96
+ return await this._mysql.run(sql, params);
97
+ } catch (error) {
98
+ this.logger('error', 'SQL执行失败', error);
99
+ // 返回空数组作为默认值,保持返回值类型一致
100
+ return [];
101
+ }
98
102
  };
99
103
 
100
104
  /**
101
- * 执行SQL命令
102
- * @param {string} sql SQL语句
103
- * @param {Number} timeout 超时时间(毫秒)
104
- * @returns {Promise<Object>} 执行结果
105
+ * 执行SQL语句
106
+ * @param {String} sql - SQL语句
107
+ * @param {Array} params - 参数数组
108
+ * @returns {Promise<Object>}
109
+ * @throws {TypeError} 当sql参数无效时
105
110
  */
106
- DB.prototype.exec = async function(sql, timeout = 60000) {
107
- if (!this._mysql) {
108
- throw new Error('MySQL实例未初始化');
109
- }
110
- try {
111
- // 增加超时时间到60秒,避免连接超时问题
112
- return await this._mysql.exec(sql, []);
113
- } catch (error) {
114
- $.log.error(`[DB] [exec] 命令执行失败`, {
115
- error: error.message,
116
- sql: sql.substring(0, 200)
117
- });
118
- throw error;
119
- }
111
+ DB.prototype.exec = async function(sql, params = []) {
112
+ // 参数校验
113
+ if (typeof sql !== 'string' || sql.trim() === '') {
114
+ throw new TypeError('sql must be non-empty string');
115
+ }
116
+ if (!Array.isArray(params)) {
117
+ throw new TypeError('params must be array');
118
+ }
119
+
120
+ try {
121
+ return await this._mysql.exec(sql, params);
122
+ } catch (error) {
123
+ this.logger('error', 'SQL执行失败', error);
124
+ // 返回空数组作为默认值,保持返回值类型一致
125
+ return 0;
126
+ }
120
127
  };
121
128
 
122
129
  /**
123
130
  * 获取数据库连接
124
- * @param {Number} timeout 超时时间(毫秒)
125
- * @returns {Promise<Object>} 数据库连接对象
131
+ * @returns {Promise<Object>}
126
132
  */
127
- DB.prototype.getConn = async function(timeout = 60000) {
128
- if (!this._mysql) {
129
- throw new Error('MySQL实例未初始化');
130
- }
131
- try {
132
- // 增加超时时间到60秒,直接调用mysql实例的getConn方法
133
- return await this._mysql.getConn();
134
- } catch (error) {
135
- $.log.error(`[DB] [getConn] 获取连接失败`, {
136
- error: error.message
137
- });
138
- throw error;
139
- }
133
+ DB.prototype.getConn = async function() {
134
+ try {
135
+ return await this._mysql.getConn();
136
+ } catch (error) {
137
+ this.logger('error', '获取连接失败', error);
138
+ // 返回null作为默认值,保持返回值类型一致
139
+ return null;
140
+ }
140
141
  };
141
142
 
142
143
  /**
143
- * 事务开始
144
- * @param {String} identifier 事务标识符
145
- * @param {Number} timeout 超时时间(毫秒)
146
- * @return {Promise<Object>} 执行结果
144
+ * 开始事务
145
+ * @returns {Promise<Object>}
147
146
  */
148
- DB.prototype.start = async function(identifier = "point_1", timeout = 15000) {
149
- this.task = 1;
150
- try {
151
- return await this.exec("SET AUTOCOMMIT=0;BEGIN;", timeout);
152
- } catch (err) {
153
- this.task = 0;
154
- $.log.error(`[DB] [start] 事务开始失败: ${err.message}`);
155
- throw err;
156
- }
147
+ DB.prototype.start = async function() {
148
+ try {
149
+ return await this._mysql.beginTransaction();
150
+ } catch (error) {
151
+ this.logger('error', '开始事务失败', error);
152
+ // 返回null作为默认值,保持返回值类型一致
153
+ return null;
154
+ }
157
155
  };
158
156
 
159
157
  /**
160
- * 提交
161
- * @param {Number} timeout 超时时间(毫秒)
162
- * @return {Promise<Object>} 执行结果
158
+ * 提交事务
159
+ * @param {Object} transaction - 事务对象
160
+ * @returns {Promise<void>}
161
+ * @throws {TypeError} 当transaction参数无效时
163
162
  */
164
- DB.prototype.commit = async function(timeout = 15000) {
165
- if (this.task !== 1) {
166
- $.log.warn("[DB] [commit] 没有活跃的事务可提交");
167
- return 0;
168
- }
169
- this.task = 2;
170
- try {
171
- const result = await this.exec("COMMIT;", timeout);
172
- // 重置事务状态
173
- this.task = 0;
174
- this.task_sql = '';
175
- return result;
176
- } catch (err) {
177
- $.log.error(`[DB] [commit] 事务提交失败: ${err.message}`);
178
- // 尝试回滚
179
- try {
180
- await this.exec("ROLLBACK;", timeout);
181
- } catch (rollbackErr) {
182
- $.log.error(`[DB] [commit] 事务回滚也失败: ${rollbackErr.message}`);
183
- }
184
- this.task = 0;
185
- this.task_sql = '';
186
- throw err;
187
- }
163
+ DB.prototype.commit = async function(transaction) {
164
+ // 参数校验
165
+ if (!transaction || typeof transaction !== 'object') {
166
+ throw new TypeError('transaction must be object');
167
+ }
168
+
169
+ try {
170
+ await transaction.commit();
171
+ } catch (error) {
172
+ this.logger('error', '提交事务失败', error);
173
+ }
188
174
  };
189
175
 
190
176
  /**
191
- * 事务结束
177
+ * 回滚事务
178
+ * @param {Object} transaction - 事务对象
179
+ * @returns {Promise<void>}
180
+ * @throws {TypeError} 当transaction参数无效时
192
181
  */
193
- DB.prototype.end = function() {
194
- // 确保事务状态被重置
195
- this.task = 0;
196
- this.task_sql = '';
182
+ DB.prototype.back = async function(transaction) {
183
+ // 参数校验
184
+ if (!transaction || typeof transaction !== 'object') {
185
+ throw new TypeError('transaction must be object');
186
+ }
187
+
188
+ try {
189
+ await transaction.rollback();
190
+ } catch (error) {
191
+ this.logger('error', '回滚事务失败', error);
192
+ }
197
193
  };
198
194
 
199
195
  /**
200
- * 滚回
201
- * @param {String} identifier 事务标识符
202
- * @param {Number} timeout 超时时间(毫秒)
203
- * @return {Promise<Object>} 执行结果
196
+ * 在事务中执行多个操作
197
+ * @param {Function} callback - 包含事务操作的回调函数
198
+ * @returns {Promise<*>}
199
+ * @throws {TypeError} 当callback参数无效时
204
200
  */
205
- DB.prototype.back = async function(identifier = "point_1", timeout = 15000) {
206
- if (this.task !== 1) {
207
- $.log.warn("[DB] [back] 没有活跃的事务可回滚");
208
- this.task = 0;
209
- this.task_sql = '';
210
- return 0;
211
- }
212
- this.task = 3;
213
- try {
214
- await this.exec("ROLLBACK;", timeout);
215
- const result = await this.exec("SET AUTOCOMMIT=1;", timeout);
216
- // 重置事务状态
217
- this.task = 0;
218
- this.task_sql = '';
219
- return result;
220
- } catch (err) {
221
- $.log.error(`[DB] [back] 事务回滚失败: ${err.message}`);
222
- this.task = 0;
223
- this.task_sql = '';
224
- throw err;
225
- }
201
+ DB.prototype.transaction = async function(callback) {
202
+ // 参数校验
203
+ if (typeof callback !== 'function') {
204
+ throw new TypeError('callback must be function');
205
+ }
206
+
207
+ try {
208
+ return await this._mysql.transaction(callback);
209
+ } catch (error) {
210
+ this.logger('error', '事务执行失败', error);
211
+ // 返回null作为默认值,保持返回值类型一致
212
+ return null;
213
+ }
226
214
  };
227
215
 
228
216
  /**
@@ -231,7 +219,7 @@ DB.prototype.back = async function(identifier = "point_1", timeout = 15000) {
231
219
  * @param {Number} timeout 超时时间(毫秒)
232
220
  * @return {Promise|Array} 表名数组
233
221
  */
234
- DB.prototype.tables = async function(table, timeout = 15000) {
222
+ DB.prototype.tables = async function (table, timeout = 15000) {
235
223
  try {
236
224
  const list = await this.run("show tables", [], timeout);
237
225
  const key = 'Tables_in_' + this.database();
@@ -249,7 +237,7 @@ DB.prototype.tables = async function(table, timeout = 15000) {
249
237
  // 使用原生方法提取表名
250
238
  return list.map(item => item[key]);
251
239
  } catch (err) {
252
- $.log.error(`[DB] [tables] 获取表名失败: ${err.message}`);
240
+ this.logger('error', '获取表名失败', err);
253
241
  return [];
254
242
  }
255
243
  };
@@ -261,12 +249,11 @@ DB.prototype.tables = async function(table, timeout = 15000) {
261
249
  * @param {Number} timeout 超时时间(毫秒)
262
250
  * @return {Promise|Array} 字段信息列表
263
251
  */
264
- DB.prototype.fields = async function(table, field_name, timeout = 15000) {
252
+ DB.prototype.fields = async function (table, field_name, timeout = 15000) {
265
253
  try {
266
254
  const targetTable = table || this.table;
267
255
  if (!targetTable) {
268
- $.log.error("[DB] [fields] 未指定表名");
269
- return [];
256
+ throw new TypeError('table must be specified');
270
257
  }
271
258
  const field = 'COLUMN_NAME as `name`,ORDINAL_POSITION as `cid`,COLUMN_DEFAULT as `dflt_value`,IS_NULLABLE as `notnull`,COLUMN_TYPE as `type`,COLUMN_KEY as `pk`,EXTRA as `auto`,COLUMN_COMMENT as `note`';
272
259
  let sql = "select " + field + " from information_schema.COLUMNS where `table_name` = '" + targetTable +
@@ -282,7 +269,7 @@ DB.prototype.fields = async function(table, field_name, timeout = 15000) {
282
269
  }
283
270
  return list;
284
271
  } catch (err) {
285
- $.log.error(`[DB] [fields] 获取字段信息失败: ${err.message}`);
272
+ this.logger('error', '获取字段信息失败', err);
286
273
  return [];
287
274
  }
288
275
  };
@@ -297,11 +284,10 @@ DB.prototype.fields = async function(table, field_name, timeout = 15000) {
297
284
  * @param {Number} timeout 超时时间(毫秒)
298
285
  * @return {Promise|Number} 创建成功返回1,失败返回0
299
286
  */
300
- DB.prototype.addTable = async function(table, field, type = 'int', auto = true, commit = '', timeout = 15000) {
287
+ DB.prototype.addTable = async function (table, field, type = 'int', auto = true, commit = '', timeout = 15000) {
301
288
  try {
302
289
  if (!table || typeof table !== 'string') {
303
- $.log.error("[DB] [addTable] 表名无效");
304
- return 0;
290
+ throw new TypeError('table must be a valid string');
305
291
  }
306
292
  if (!field) {
307
293
  field = "id";
@@ -316,17 +302,17 @@ DB.prototype.addTable = async function(table, field, type = 'int', auto = true,
316
302
  fieldDef += " COMMENT '" + commit + "'";
317
303
  }
318
304
  fieldDef += " PRIMARY KEY";
319
-
305
+
320
306
  let sql = "CREATE TABLE IF NOT EXISTS \`" + table + "\` (" + fieldDef + ")";
321
307
  sql += " ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
322
-
308
+
323
309
  // 执行SQL并设置表名
324
310
  const result = await this.exec(sql, timeout);
325
311
  // 设置实例的表名属性,以便后续操作使用
326
312
  this.table = table;
327
- return result && result.affectedRows !== undefined ? 1 : 0;
313
+ return result;
328
314
  } catch (err) {
329
- $.log.error(`[DB] [addTable] 创建表失败: ${err.message}`);
315
+ this.logger('error', '创建表失败', err);
330
316
  return 0;
331
317
  }
332
318
  };
@@ -337,10 +323,9 @@ DB.prototype.addTable = async function(table, field, type = 'int', auto = true,
337
323
  * @param {Number} timeout 超时时间(毫秒)
338
324
  * @return {Promise|Number} 操作结果
339
325
  */
340
- DB.prototype.dropTable = function(table, timeout = 15000) {
326
+ DB.prototype.dropTable = function (table, timeout = 15000) {
341
327
  if (!table || typeof table !== 'string') {
342
- $.log.error("[DB] [dropTable] 表名无效");
343
- return 0;
328
+ throw new TypeError('table must be a valid string');
344
329
  }
345
330
  return this.exec("DROP TABLE IF EXISTS \`" + table + "\`;", timeout);
346
331
  };
@@ -352,10 +337,9 @@ DB.prototype.dropTable = function(table, timeout = 15000) {
352
337
  * @param {Number} timeout 超时时间(毫秒)
353
338
  * @return {Promise|Number} 操作结果
354
339
  */
355
- DB.prototype.renameTable = function(table, new_table, timeout = 15000) {
340
+ DB.prototype.renameTable = function (table, new_table, timeout = 15000) {
356
341
  if (!table || !new_table) {
357
- $.log.error("[DB] [renameTable] 表名参数不完整");
358
- return 0;
342
+ throw new TypeError('table and new_table must be specified');
359
343
  }
360
344
  return this.exec("RENAME TABLE \`" + table + "\` TO \`" + new_table + "\`;", timeout);
361
345
  };
@@ -371,27 +355,26 @@ DB.prototype.renameTable = function(table, new_table, timeout = 15000) {
371
355
  * @param {Number} timeout 超时时间(毫秒)
372
356
  * @return {Promise|Number} 添加成功返回1,失败返回0
373
357
  */
374
- DB.prototype.addField = async function(field, type, value = '', not_null = true, auto = false, comment = '', timeout = 15000) {
358
+ DB.prototype.addField = async function (field, type, value = '', not_null = true, auto = false, comment = '', timeout = 15000) {
375
359
  try {
376
360
  // 确保表名已设置
377
361
  if (!this.table || !field || !type) {
378
- $.log.error("[DB] [addField] 表名、字段名或类型未指定");
379
- return 0;
362
+ throw new TypeError('table, field, and type must be specified');
380
363
  }
381
-
364
+
382
365
  // 构建字段定义
383
366
  let fieldDef = `\`${field}\` ${type}`;
384
-
367
+
385
368
  // 添加非空约束
386
369
  if (not_null) {
387
370
  fieldDef += " NOT NULL";
388
371
  }
389
-
372
+
390
373
  // 添加自增属性
391
374
  if (auto) {
392
375
  fieldDef += " AUTO_INCREMENT";
393
376
  }
394
-
377
+
395
378
  // 添加默认值
396
379
  if (value !== undefined && value !== null && value !== '') {
397
380
  if (typeof value === 'string') {
@@ -400,18 +383,18 @@ DB.prototype.addField = async function(field, type, value = '', not_null = true,
400
383
  fieldDef += " DEFAULT " + value;
401
384
  }
402
385
  }
403
-
386
+
404
387
  // 添加注释
405
388
  if (comment) {
406
389
  fieldDef += " COMMENT '" + comment + "'";
407
390
  }
408
-
391
+
409
392
  // 使用ADD COLUMN而不是CHANGE COLUMN
410
393
  const sql = `ALTER TABLE \`${this.table}\` ADD COLUMN ${fieldDef};`;
411
394
  const result = await this.exec(sql, timeout);
412
- return result && result.affectedRows !== undefined ? 1 : 0;
395
+ return result
413
396
  } catch (err) {
414
- $.log.error(`[DB] [addField] 添加字段失败: ${err.message}`);
397
+ this.logger('error', '添加字段失败', err);
415
398
  return 0;
416
399
  }
417
400
  };
@@ -428,11 +411,10 @@ DB.prototype.addField = async function(field, type, value = '', not_null = true,
428
411
  * @param {Number} timeout 超时时间(毫秒)
429
412
  * @return {Promise|Number} 修改成功返回1,失败返回0
430
413
  */
431
- DB.prototype.setField = async function(field, type, value = '', not_null = true, auto = false, isKey = false, new_name = '', timeout = 15000) {
414
+ DB.prototype.setField = async function (field, type, value = '', not_null = true, auto = false, isKey = false, new_name = '', timeout = 15000) {
432
415
  try {
433
416
  if (!field || !type || !this.table) {
434
- $.log.error("[DB] [setField] 参数不完整或表名未设置");
435
- return 0;
417
+ throw new TypeError('field, type, and table must be specified');
436
418
  }
437
419
  const targetName = new_name || field;
438
420
  let fieldDef = `\`${targetName}\` ${type}`;
@@ -455,9 +437,9 @@ DB.prototype.setField = async function(field, type, value = '', not_null = true,
455
437
  }
456
438
  sql += ";";
457
439
  const result = await this.exec(sql, timeout);
458
- return result && result.affectedRows !== undefined ? 1 : 0;
440
+ return result;
459
441
  } catch (err) {
460
- $.log.error(`[DB] [setField] 修改字段失败: ${err.message}`);
442
+ this.logger('error', '修改字段失败', err);
461
443
  return 0;
462
444
  }
463
445
  };
@@ -470,10 +452,9 @@ DB.prototype.setField = async function(field, type, value = '', not_null = true,
470
452
  * @param {Number} timeout 超时时间(毫秒)
471
453
  * @return {Promise|Number} 操作结果
472
454
  */
473
- DB.prototype.editField = function(table, field, type, timeout = 15000) {
455
+ DB.prototype.editField = function (table, field, type, timeout = 15000) {
474
456
  if (!table || !field || !type) {
475
- $.log.error("[DB] [editField] 参数不完整");
476
- return 0;
457
+ throw new TypeError('table, field, and type must be specified');
477
458
  }
478
459
  return this.exec("ALTER TABLE \`" + table + "\` MODIFY COLUMN \`" + field + "\` " + type + ";", timeout);
479
460
  };
@@ -485,10 +466,9 @@ DB.prototype.editField = function(table, field, type, timeout = 15000) {
485
466
  * @param {Number} timeout 超时时间(毫秒)
486
467
  * @return {Promise|Number} 操作结果
487
468
  */
488
- DB.prototype.delField = function(table, field, timeout = 15000) {
469
+ DB.prototype.delField = function (table, field, timeout = 15000) {
489
470
  if (!table || !field) {
490
- $.log.error("[DB] [delField] 参数不完整");
491
- return 0;
471
+ throw new TypeError('table and field must be specified');
492
472
  }
493
473
  return this.exec("ALTER TABLE \`" + table + "\` DROP COLUMN \`" + field + "\`;", timeout);
494
474
  };
@@ -502,10 +482,9 @@ DB.prototype.delField = function(table, field, timeout = 15000) {
502
482
  * @param {Number} timeout 超时时间(毫秒)
503
483
  * @return {Promise|Number} 操作结果
504
484
  */
505
- DB.prototype.renameField = function(table, field, new_field, type, timeout = 15000) {
485
+ DB.prototype.renameField = function (table, field, new_field, type, timeout = 15000) {
506
486
  if (!table || !field || !new_field) {
507
- $.log.error("[DB] [renameField] 参数不完整");
508
- return 0;
487
+ throw new TypeError('table, field, and new_field must be specified');
509
488
  }
510
489
  return this.exec("ALTER TABLE \`" + table + "\` CHANGE \`" + field + "\` \`" + new_field + "\` " + type + ";", timeout);
511
490
  };
@@ -516,10 +495,9 @@ DB.prototype.renameField = function(table, field, new_field, type, timeout = 150
516
495
  * @param {Number} timeout 超时时间(毫秒)
517
496
  * @return {Promise|String} 创建语句
518
497
  */
519
- DB.prototype.getCreateTable = async function(table, timeout = 15000) {
498
+ DB.prototype.getCreateTable = async function (table, timeout = 15000) {
520
499
  if (!table) {
521
- $.log.error("[DB] [getCreateTable] 表名不能为空");
522
- return '';
500
+ throw new TypeError('table must be specified');
523
501
  }
524
502
  try {
525
503
  const list = await this.run("SHOW CREATE TABLE \`" + table + "\`;", [], timeout);
@@ -528,7 +506,7 @@ DB.prototype.getCreateTable = async function(table, timeout = 15000) {
528
506
  }
529
507
  return '';
530
508
  } catch (err) {
531
- $.log.error(`[DB] [getCreateTable] 获取表结构失败: ${err.message}`);
509
+ this.logger('error', '获取表结构失败', err);
532
510
  return '';
533
511
  }
534
512
  };
@@ -540,19 +518,18 @@ DB.prototype.getCreateTable = async function(table, timeout = 15000) {
540
518
  * @param {Number} timeout 超时时间(毫秒)
541
519
  * @return {Promise|String} SQL插入语句
542
520
  */
543
- DB.prototype.getTableData = async function(table, batchSize = 100, timeout = 60000) {
521
+ DB.prototype.getTableData = async function (table, batchSize = 100, timeout = 60000) {
544
522
  if (!table) {
545
- $.log.error("[DB] [getTableData] 表名不能为空");
546
- return '';
523
+ throw new TypeError('table must be specified');
547
524
  }
548
525
  try {
549
526
  // 获取表数据
550
527
  const rows = await this.run(`SELECT * FROM \`${table}\``, [], timeout);
551
528
  let sql = '';
552
-
529
+
553
530
  if (rows && rows.length > 0) {
554
531
  sql += `-- 表数据: ${table}\n`;
555
-
532
+
556
533
  // 批量处理大数据量
557
534
  for (let j = 0; j < rows.length; j += batchSize) {
558
535
  const batch = rows.slice(j, j + batchSize);
@@ -579,15 +556,15 @@ DB.prototype.getTableData = async function(table, batchSize = 100, timeout = 600
579
556
  }).join(',');
580
557
  sql += `INSERT INTO \`${table}\` VALUES (${rowValues});\n`;
581
558
  } catch (rowErr) {
582
- $.log.error(`[DB] [getTableData] 处理行数据失败: ${rowErr.message}`);
583
- }
559
+ this.logger('error', '处理行数据失败', rowErr);
560
+ }
584
561
  }
585
562
  }
586
563
  sql += '\n';
587
564
  }
588
565
  return sql;
589
566
  } catch (err) {
590
- $.log.error(`[DB] [getTableData] 获取表数据失败: ${err.message}`);
567
+ this.logger('error', '获取表数据失败', err);
591
568
  return '';
592
569
  }
593
570
  };
@@ -598,10 +575,9 @@ DB.prototype.getTableData = async function(table, batchSize = 100, timeout = 600
598
575
  * @param {Number} timeout 超时时间(毫秒)
599
576
  * @return {Promise|Number} 操作结果
600
577
  */
601
- DB.prototype.emptyTable = function(table, timeout = 15000) {
578
+ DB.prototype.emptyTable = function (table, timeout = 15000) {
602
579
  if (!table || typeof table !== 'string') {
603
- $.log.error("[DB] [emptyTable] 表名无效");
604
- return 0;
580
+ throw new TypeError('table must be a valid string');
605
581
  }
606
582
  return this.exec("TRUNCATE TABLE \`" + table + "\`;", timeout);
607
583
  };
@@ -612,7 +588,7 @@ DB.prototype.emptyTable = function(table, timeout = 15000) {
612
588
  * @param {Number} timeout 超时时间(毫秒)
613
589
  * @return {Promise|Boolean} 是否存在
614
590
  */
615
- DB.prototype.hasTable = async function(table, timeout = 15000) {
591
+ DB.prototype.hasTable = async function (table, timeout = 15000) {
616
592
  if (!table || typeof table !== 'string') {
617
593
  return false;
618
594
  }
@@ -620,7 +596,7 @@ DB.prototype.hasTable = async function(table, timeout = 15000) {
620
596
  const list = await this.run("SHOW TABLES LIKE '" + table + "'", [], timeout);
621
597
  return list && list.length > 0;
622
598
  } catch (err) {
623
- $.log.error(`[DB] [hasTable] 检查表是否存在失败: ${err.message}`);
599
+ this.logger('error', '检查表是否存在失败', err);
624
600
  return false;
625
601
  }
626
602
  };
@@ -632,7 +608,7 @@ DB.prototype.hasTable = async function(table, timeout = 15000) {
632
608
  * @param {Number} timeout 超时时间(毫秒)
633
609
  * @return {Promise|Boolean} 是否存在
634
610
  */
635
- DB.prototype.hasField = async function(table, field, timeout = 15000) {
611
+ DB.prototype.hasField = async function (table, field, timeout = 15000) {
636
612
  if (!table || !field) {
637
613
  return false;
638
614
  }
@@ -640,7 +616,7 @@ DB.prototype.hasField = async function(table, field, timeout = 15000) {
640
616
  const list = await this.fields(table, field, timeout);
641
617
  return list && list.length > 0;
642
618
  } catch (err) {
643
- $.log.error(`[DB] [hasField] 检查字段是否存在失败: ${err.message}`);
619
+ this.logger('error', '检查字段是否存在失败', err);
644
620
  return false;
645
621
  }
646
622
  };
@@ -652,14 +628,93 @@ DB.prototype.hasField = async function(table, field, timeout = 15000) {
652
628
  * @param {Number} timeout 超时时间(毫秒)
653
629
  * @return {Promise|Number} 操作结果
654
630
  */
655
- DB.prototype.backupTable = function(table, backup, timeout = 60000) {
631
+ DB.prototype.backupTable = function (table, backup, timeout = 60000) {
656
632
  if (!table || !backup) {
657
- $.log.error("[DB] [backupTable] 参数不完整");
658
- return 0;
633
+ throw new TypeError('table and backup must be specified');
659
634
  }
660
635
  return this.exec("CREATE TABLE \`" + backup + "\` LIKE \`" + table + "\`; INSERT INTO \`" + backup + "\` SELECT * FROM \`" + table + "\`;", timeout);
661
636
  };
662
637
 
638
+
639
+ /**
640
+ * @description 创建表
641
+ * @param {String} table 表名
642
+ * @param {Object} model 表模型,键值对,根据值类型创建字段类型,根据键名创建字段名
643
+ * @param {String} key 主键
644
+ * @return {Promise|Number} 操作结果
645
+ */
646
+ DB.prototype.createTable = function (table, model, key = 'id', timeout = 15000) {
647
+ if (!table || !model || !model.fields) {
648
+ throw new TypeError('table, model, and model.fields must be specified');
649
+ }
650
+ var fields = '';
651
+ for (const field in model.fields) {
652
+ const value = model.fields[field];
653
+ let type = '';
654
+ if (field === key) {
655
+ type = 'INT AUTO_INCREMENT PRIMARY KEY';
656
+ } else if (typeof value === 'number') {
657
+ if (value % 1 === 0) {
658
+ // 判断是否时间戳(10位或13位数字)
659
+ const now = Date.now();
660
+ const isTimestamp = (value >= 1000000000 && value <= now * 1.1); // 10位或13位时间戳
661
+ if (isTimestamp) {
662
+ type = 'DATETIME';
663
+ } else {
664
+ type = 'INT';
665
+ }
666
+ } else {
667
+ type = 'FLOAT';
668
+ }
669
+ } else if (typeof value === 'string') {
670
+ type = 'TEXT';
671
+ if (value.length <= 255) {
672
+ // 正则判断是否日期时间型
673
+ if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(value)) {
674
+ type = 'DATETIME';
675
+ }
676
+ // 正则判断是否日期型
677
+ else if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
678
+ type = 'DATE';
679
+ }
680
+ // 正则判断是否时间型
681
+ else if (/^\d{2}:\d{2}:\d{2}$/.test(value)) {
682
+ type = 'TIME';
683
+ }
684
+ // 正则判断是否JSON字符串
685
+ else if (/^[{[]/.test(value) && /[}]]$/.test(value)) {
686
+ type = 'TEXT';
687
+ }
688
+ // 正则判断是否html
689
+ else if (/^<[a-z][\s\S]*>/i.test(value)) {
690
+ type = 'TEXT';
691
+ }
692
+ // 正则判断是否xml
693
+ else if (/^<\?xml[\s\S]*\?>/.test(value)) {
694
+ type = 'TEXT';
695
+ }
696
+ else {
697
+ type = 'VARCHAR(255)';
698
+ }
699
+ }
700
+ }
701
+ else if (typeof value === 'boolean') {
702
+ type = 'tinyint(1)';
703
+ }
704
+ else if (value instanceof Date) {
705
+ // 判断是否Date对象,类型为datetime
706
+ type = 'DATETIME';
707
+ }
708
+ else {
709
+ type = 'BLOB';
710
+ }
711
+ fields += `\`${field}\` ${type}, `;
712
+ }
713
+ fields = fields.slice(0, -2);
714
+ var sql = `CREATE TABLE IF NOT EXISTS \`${table}\` (${fields});`;
715
+ return this.exec(sql, [], timeout);
716
+ }
717
+
663
718
  module.exports = {
664
719
  DB
665
- };
720
+ };