mm_mysql 2.1.1 → 2.2.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.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # mm_mysql
2
- 这是超级美眉mysql帮助函数模块,用于便捷操作mysql,使用await方式,可以避免嵌套函数
2
+ 一个简洁、高效的Node.js MySQL操作库,支持async/await语法,提供直观的API和强大的功能扩展。
3
3
 
4
4
  [![npm version](https://badge.fury.io/js/mm_mysql.svg)](https://www.npmjs.com/package/mm_mysql)
5
5
  [![GitHub issues](https://img.shields.io/github/issues/qiuwenwu91/mm_mysql.svg)](https://gitee.com/qiuwenwu91/mm_mysql/issues)
@@ -37,6 +37,16 @@ const db = mysql.db().new('users', 'id');
37
37
  await mysql.close();
38
38
  ```
39
39
 
40
+ ## 主要特性
41
+
42
+ - 支持async/await语法,避免回调地狱
43
+ - 提供简洁直观的API接口
44
+ - 内置事务支持
45
+ - 自动类型推断的表创建功能(支持int、float、varchar、text、datetime、date、time、boolean等多种数据类型)
46
+ - 灵活的查询条件支持
47
+ - 支持连接池管理
48
+ - 调试模式支持
49
+
40
50
  ## API文档
41
51
 
42
52
  ### Mysql类
@@ -118,6 +128,21 @@ const config = {
118
128
 
119
129
  ### DB类方法
120
130
 
131
+ #### createTable(model, options)
132
+ 自动创建数据库表,支持多种数据类型推断
133
+ - `model` {Object} 模型对象,包含表名和字段定义
134
+ - `table` {String} 表名
135
+ - `fields` {Object} 字段定义,支持以下数据类型:
136
+ - 数字类型(自动推断为INT/FLOAT)
137
+ - 字符串类型(自动推断为VARCHAR/TEXT/DATE/DATETIME/TIME)
138
+ - 布尔类型(自动推断为TINYINT(1))
139
+ - Date对象(自动推断为DATETIME)
140
+ - 时间戳(自动推断为DATETIME)
141
+ - `options` {Object} 选项配置(可选)
142
+ - `primary_key` {String} 主键字段名(可选)
143
+ - `if_not_exists` {Boolean} 是否仅当表不存在时创建(可选,默认为true)
144
+ - 返回:{Promise<Object>} 执行结果
145
+
121
146
  #### new(table, key)
122
147
  创建数据库表管理器
123
148
  - `table` {String} 表名
@@ -172,6 +197,61 @@ const config = {
172
197
 
173
198
  ### 使用示例
174
199
 
200
+ #### 表创建示例
201
+
202
+ ```javascript
203
+ const { Mysql } = require('mm_mysql');
204
+
205
+ async function createTableExample() {
206
+ const mysql = new Mysql({
207
+ host: "localhost",
208
+ user: "root",
209
+ password: "123456",
210
+ database: "test"
211
+ });
212
+
213
+ await mysql.open();
214
+
215
+ try {
216
+ // 创建数据库表管理器
217
+ const db = mysql.db();
218
+
219
+ // 定义表模型
220
+ const userModel = {
221
+ table: 'users',
222
+ fields: {
223
+ id: 1, // 整数类型 -> INT
224
+ username: 'test', // 字符串类型 -> VARCHAR(255)
225
+ email: 'test@example.com', // 字符串类型 -> VARCHAR(255)
226
+ age: 25, // 整数类型 -> INT
227
+ salary: 5000.50, // 浮点数类型 -> FLOAT
228
+ active: true, // 布尔类型 -> TINYINT(1)
229
+ birth_date: '2000-01-01', // 日期字符串 -> DATE
230
+ bio: '这是一个很长的个人简介...', // 长字符串 -> TEXT
231
+ created_at: '2023-01-01 12:00:00', // 日期时间字符串 -> DATETIME
232
+ login_time: new Date(), // Date对象 -> DATETIME
233
+ last_active: Date.now() // 时间戳 -> DATETIME
234
+ }
235
+ };
236
+
237
+ // 创建表
238
+ const result = await db.createTable(userModel, {
239
+ primary_key: 'id',
240
+ if_not_exists: true
241
+ });
242
+
243
+ console.log('表创建成功:', result);
244
+
245
+ } catch (error) {
246
+ console.error('创建表失败:', error);
247
+ } finally {
248
+ await mysql.close();
249
+ }
250
+ }
251
+
252
+ createTableExample().catch(console.error);
253
+ ```
254
+
175
255
  #### 基本查询操作
176
256
 
177
257
  ```javascript
@@ -302,7 +382,28 @@ transactionExample().catch(console.error);
302
382
  - `_not`: 不等于
303
383
  - `_has`: IN查询
304
384
  - `_like`: LIKE查询
385
+ 6. createTable方法会根据字段值自动推断数据类型,如需更精确的类型控制,建议直接使用SQL语句
386
+
387
+ ## 更新日志
388
+
389
+ ### v2.2.0
390
+ - 新增createTable方法,支持多种数据类型自动推断
391
+ - 支持boolean类型自动转换为TINYINT(1)
392
+ - 支持date字符串自动识别为DATE类型
393
+ - 支持Date对象自动识别为DATETIME类型
394
+ - 支持时间戳自动识别为DATETIME类型
305
395
 
306
396
  ## 许可证
307
397
 
308
398
  ISC License
399
+
400
+ ## 贡献
401
+
402
+ 欢迎提交Issue和Pull Request!
403
+
404
+ ## 联系方式
405
+
406
+ 如有问题或建议,请通过以下方式联系:
407
+
408
+ - 项目地址:https://gitee.com/qiuwenwu91/mm_mysql
409
+ - 邮箱:qiuwenwu91@163.com
package/db.js CHANGED
@@ -23,7 +23,7 @@ class DB extends Sql {
23
23
  * 获取上级
24
24
  * @return {Object} 返回上级管理器
25
25
  */
26
- this.parent = function() {
26
+ this.parent = function () {
27
27
  return mysql;
28
28
  };
29
29
  }
@@ -36,7 +36,7 @@ class DB extends Sql {
36
36
  * @param {String} message - 错误信息
37
37
  * @returns {Promise<Error>}
38
38
  */
39
- DB.prototype._createTimeoutPromise = function(timeout, message) {
39
+ DB.prototype._createTimeoutPromise = function (timeout, message) {
40
40
  return new Promise((_, reject) => {
41
41
  setTimeout(() => {
42
42
  reject(new Error(message));
@@ -48,7 +48,7 @@ DB.prototype._createTimeoutPromise = function(timeout, message) {
48
48
  * 获取数据库名
49
49
  * @return {String} 数据库
50
50
  */
51
- DB.prototype.database = function() {
51
+ DB.prototype.database = function () {
52
52
  return this.parent().config.database;
53
53
  };
54
54
 
@@ -58,7 +58,7 @@ DB.prototype.database = function() {
58
58
  * @param {String} key 键名
59
59
  * @return {Object} 返回管理器
60
60
  */
61
- DB.prototype.new = function(table, key) {
61
+ DB.prototype.new = function (table, key) {
62
62
  const db = this.parent().db();
63
63
  db.table = table;
64
64
  if (key) {
@@ -77,7 +77,7 @@ DB.prototype.new = function(table, key) {
77
77
  * @param {Number} timeout 超时时间(毫秒)
78
78
  * @returns {Promise<Object>} 查询结果
79
79
  */
80
- DB.prototype.run = async function(sql, params = [], timeout = 30000) {
80
+ DB.prototype.run = async function (sql, params = [], timeout = 30000) {
81
81
  if (!this._mysql) {
82
82
  throw new Error('MySQL实例未初始化');
83
83
  }
@@ -103,7 +103,7 @@ DB.prototype.run = async function(sql, params = [], timeout = 30000) {
103
103
  * @param {Number} timeout 超时时间(毫秒)
104
104
  * @returns {Promise<Object>} 执行结果
105
105
  */
106
- DB.prototype.exec = async function(sql, timeout = 60000) {
106
+ DB.prototype.exec = async function (sql, timeout = 60000) {
107
107
  if (!this._mysql) {
108
108
  throw new Error('MySQL实例未初始化');
109
109
  }
@@ -124,7 +124,7 @@ DB.prototype.exec = async function(sql, timeout = 60000) {
124
124
  * @param {Number} timeout 超时时间(毫秒)
125
125
  * @returns {Promise<Object>} 数据库连接对象
126
126
  */
127
- DB.prototype.getConn = async function(timeout = 60000) {
127
+ DB.prototype.getConn = async function (timeout = 60000) {
128
128
  if (!this._mysql) {
129
129
  throw new Error('MySQL实例未初始化');
130
130
  }
@@ -145,7 +145,7 @@ DB.prototype.getConn = async function(timeout = 60000) {
145
145
  * @param {Number} timeout 超时时间(毫秒)
146
146
  * @return {Promise<Object>} 执行结果
147
147
  */
148
- DB.prototype.start = async function(identifier = "point_1", timeout = 15000) {
148
+ DB.prototype.start = async function (identifier = "point_1", timeout = 15000) {
149
149
  this.task = 1;
150
150
  try {
151
151
  return await this.exec("SET AUTOCOMMIT=0;BEGIN;", timeout);
@@ -161,7 +161,7 @@ DB.prototype.start = async function(identifier = "point_1", timeout = 15000) {
161
161
  * @param {Number} timeout 超时时间(毫秒)
162
162
  * @return {Promise<Object>} 执行结果
163
163
  */
164
- DB.prototype.commit = async function(timeout = 15000) {
164
+ DB.prototype.commit = async function (timeout = 15000) {
165
165
  if (this.task !== 1) {
166
166
  $.log.warn("[DB] [commit] 没有活跃的事务可提交");
167
167
  return 0;
@@ -190,7 +190,7 @@ DB.prototype.commit = async function(timeout = 15000) {
190
190
  /**
191
191
  * 事务结束
192
192
  */
193
- DB.prototype.end = function() {
193
+ DB.prototype.end = function () {
194
194
  // 确保事务状态被重置
195
195
  this.task = 0;
196
196
  this.task_sql = '';
@@ -202,7 +202,7 @@ DB.prototype.end = function() {
202
202
  * @param {Number} timeout 超时时间(毫秒)
203
203
  * @return {Promise<Object>} 执行结果
204
204
  */
205
- DB.prototype.back = async function(identifier = "point_1", timeout = 15000) {
205
+ DB.prototype.back = async function (identifier = "point_1", timeout = 15000) {
206
206
  if (this.task !== 1) {
207
207
  $.log.warn("[DB] [back] 没有活跃的事务可回滚");
208
208
  this.task = 0;
@@ -231,7 +231,7 @@ DB.prototype.back = async function(identifier = "point_1", timeout = 15000) {
231
231
  * @param {Number} timeout 超时时间(毫秒)
232
232
  * @return {Promise|Array} 表名数组
233
233
  */
234
- DB.prototype.tables = async function(table, timeout = 15000) {
234
+ DB.prototype.tables = async function (table, timeout = 15000) {
235
235
  try {
236
236
  const list = await this.run("show tables", [], timeout);
237
237
  const key = 'Tables_in_' + this.database();
@@ -261,7 +261,7 @@ DB.prototype.tables = async function(table, timeout = 15000) {
261
261
  * @param {Number} timeout 超时时间(毫秒)
262
262
  * @return {Promise|Array} 字段信息列表
263
263
  */
264
- DB.prototype.fields = async function(table, field_name, timeout = 15000) {
264
+ DB.prototype.fields = async function (table, field_name, timeout = 15000) {
265
265
  try {
266
266
  const targetTable = table || this.table;
267
267
  if (!targetTable) {
@@ -297,7 +297,7 @@ DB.prototype.fields = async function(table, field_name, timeout = 15000) {
297
297
  * @param {Number} timeout 超时时间(毫秒)
298
298
  * @return {Promise|Number} 创建成功返回1,失败返回0
299
299
  */
300
- DB.prototype.addTable = async function(table, field, type = 'int', auto = true, commit = '', timeout = 15000) {
300
+ DB.prototype.addTable = async function (table, field, type = 'int', auto = true, commit = '', timeout = 15000) {
301
301
  try {
302
302
  if (!table || typeof table !== 'string') {
303
303
  $.log.error("[DB] [addTable] 表名无效");
@@ -316,10 +316,10 @@ DB.prototype.addTable = async function(table, field, type = 'int', auto = true,
316
316
  fieldDef += " COMMENT '" + commit + "'";
317
317
  }
318
318
  fieldDef += " PRIMARY KEY";
319
-
319
+
320
320
  let sql = "CREATE TABLE IF NOT EXISTS \`" + table + "\` (" + fieldDef + ")";
321
321
  sql += " ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
322
-
322
+
323
323
  // 执行SQL并设置表名
324
324
  const result = await this.exec(sql, timeout);
325
325
  // 设置实例的表名属性,以便后续操作使用
@@ -337,7 +337,7 @@ DB.prototype.addTable = async function(table, field, type = 'int', auto = true,
337
337
  * @param {Number} timeout 超时时间(毫秒)
338
338
  * @return {Promise|Number} 操作结果
339
339
  */
340
- DB.prototype.dropTable = function(table, timeout = 15000) {
340
+ DB.prototype.dropTable = function (table, timeout = 15000) {
341
341
  if (!table || typeof table !== 'string') {
342
342
  $.log.error("[DB] [dropTable] 表名无效");
343
343
  return 0;
@@ -352,7 +352,7 @@ DB.prototype.dropTable = function(table, timeout = 15000) {
352
352
  * @param {Number} timeout 超时时间(毫秒)
353
353
  * @return {Promise|Number} 操作结果
354
354
  */
355
- DB.prototype.renameTable = function(table, new_table, timeout = 15000) {
355
+ DB.prototype.renameTable = function (table, new_table, timeout = 15000) {
356
356
  if (!table || !new_table) {
357
357
  $.log.error("[DB] [renameTable] 表名参数不完整");
358
358
  return 0;
@@ -371,27 +371,27 @@ DB.prototype.renameTable = function(table, new_table, timeout = 15000) {
371
371
  * @param {Number} timeout 超时时间(毫秒)
372
372
  * @return {Promise|Number} 添加成功返回1,失败返回0
373
373
  */
374
- DB.prototype.addField = async function(field, type, value = '', not_null = true, auto = false, comment = '', timeout = 15000) {
374
+ DB.prototype.addField = async function (field, type, value = '', not_null = true, auto = false, comment = '', timeout = 15000) {
375
375
  try {
376
376
  // 确保表名已设置
377
377
  if (!this.table || !field || !type) {
378
378
  $.log.error("[DB] [addField] 表名、字段名或类型未指定");
379
379
  return 0;
380
380
  }
381
-
381
+
382
382
  // 构建字段定义
383
383
  let fieldDef = `\`${field}\` ${type}`;
384
-
384
+
385
385
  // 添加非空约束
386
386
  if (not_null) {
387
387
  fieldDef += " NOT NULL";
388
388
  }
389
-
389
+
390
390
  // 添加自增属性
391
391
  if (auto) {
392
392
  fieldDef += " AUTO_INCREMENT";
393
393
  }
394
-
394
+
395
395
  // 添加默认值
396
396
  if (value !== undefined && value !== null && value !== '') {
397
397
  if (typeof value === 'string') {
@@ -400,12 +400,12 @@ DB.prototype.addField = async function(field, type, value = '', not_null = true,
400
400
  fieldDef += " DEFAULT " + value;
401
401
  }
402
402
  }
403
-
403
+
404
404
  // 添加注释
405
405
  if (comment) {
406
406
  fieldDef += " COMMENT '" + comment + "'";
407
407
  }
408
-
408
+
409
409
  // 使用ADD COLUMN而不是CHANGE COLUMN
410
410
  const sql = `ALTER TABLE \`${this.table}\` ADD COLUMN ${fieldDef};`;
411
411
  const result = await this.exec(sql, timeout);
@@ -428,7 +428,7 @@ DB.prototype.addField = async function(field, type, value = '', not_null = true,
428
428
  * @param {Number} timeout 超时时间(毫秒)
429
429
  * @return {Promise|Number} 修改成功返回1,失败返回0
430
430
  */
431
- DB.prototype.setField = async function(field, type, value = '', not_null = true, auto = false, isKey = false, new_name = '', timeout = 15000) {
431
+ DB.prototype.setField = async function (field, type, value = '', not_null = true, auto = false, isKey = false, new_name = '', timeout = 15000) {
432
432
  try {
433
433
  if (!field || !type || !this.table) {
434
434
  $.log.error("[DB] [setField] 参数不完整或表名未设置");
@@ -470,7 +470,7 @@ DB.prototype.setField = async function(field, type, value = '', not_null = true,
470
470
  * @param {Number} timeout 超时时间(毫秒)
471
471
  * @return {Promise|Number} 操作结果
472
472
  */
473
- DB.prototype.editField = function(table, field, type, timeout = 15000) {
473
+ DB.prototype.editField = function (table, field, type, timeout = 15000) {
474
474
  if (!table || !field || !type) {
475
475
  $.log.error("[DB] [editField] 参数不完整");
476
476
  return 0;
@@ -485,7 +485,7 @@ DB.prototype.editField = function(table, field, type, timeout = 15000) {
485
485
  * @param {Number} timeout 超时时间(毫秒)
486
486
  * @return {Promise|Number} 操作结果
487
487
  */
488
- DB.prototype.delField = function(table, field, timeout = 15000) {
488
+ DB.prototype.delField = function (table, field, timeout = 15000) {
489
489
  if (!table || !field) {
490
490
  $.log.error("[DB] [delField] 参数不完整");
491
491
  return 0;
@@ -502,7 +502,7 @@ DB.prototype.delField = function(table, field, timeout = 15000) {
502
502
  * @param {Number} timeout 超时时间(毫秒)
503
503
  * @return {Promise|Number} 操作结果
504
504
  */
505
- DB.prototype.renameField = function(table, field, new_field, type, timeout = 15000) {
505
+ DB.prototype.renameField = function (table, field, new_field, type, timeout = 15000) {
506
506
  if (!table || !field || !new_field) {
507
507
  $.log.error("[DB] [renameField] 参数不完整");
508
508
  return 0;
@@ -516,7 +516,7 @@ DB.prototype.renameField = function(table, field, new_field, type, timeout = 150
516
516
  * @param {Number} timeout 超时时间(毫秒)
517
517
  * @return {Promise|String} 创建语句
518
518
  */
519
- DB.prototype.getCreateTable = async function(table, timeout = 15000) {
519
+ DB.prototype.getCreateTable = async function (table, timeout = 15000) {
520
520
  if (!table) {
521
521
  $.log.error("[DB] [getCreateTable] 表名不能为空");
522
522
  return '';
@@ -540,7 +540,7 @@ DB.prototype.getCreateTable = async function(table, timeout = 15000) {
540
540
  * @param {Number} timeout 超时时间(毫秒)
541
541
  * @return {Promise|String} SQL插入语句
542
542
  */
543
- DB.prototype.getTableData = async function(table, batchSize = 100, timeout = 60000) {
543
+ DB.prototype.getTableData = async function (table, batchSize = 100, timeout = 60000) {
544
544
  if (!table) {
545
545
  $.log.error("[DB] [getTableData] 表名不能为空");
546
546
  return '';
@@ -549,10 +549,10 @@ DB.prototype.getTableData = async function(table, batchSize = 100, timeout = 600
549
549
  // 获取表数据
550
550
  const rows = await this.run(`SELECT * FROM \`${table}\``, [], timeout);
551
551
  let sql = '';
552
-
552
+
553
553
  if (rows && rows.length > 0) {
554
554
  sql += `-- 表数据: ${table}\n`;
555
-
555
+
556
556
  // 批量处理大数据量
557
557
  for (let j = 0; j < rows.length; j += batchSize) {
558
558
  const batch = rows.slice(j, j + batchSize);
@@ -598,7 +598,7 @@ DB.prototype.getTableData = async function(table, batchSize = 100, timeout = 600
598
598
  * @param {Number} timeout 超时时间(毫秒)
599
599
  * @return {Promise|Number} 操作结果
600
600
  */
601
- DB.prototype.emptyTable = function(table, timeout = 15000) {
601
+ DB.prototype.emptyTable = function (table, timeout = 15000) {
602
602
  if (!table || typeof table !== 'string') {
603
603
  $.log.error("[DB] [emptyTable] 表名无效");
604
604
  return 0;
@@ -612,7 +612,7 @@ DB.prototype.emptyTable = function(table, timeout = 15000) {
612
612
  * @param {Number} timeout 超时时间(毫秒)
613
613
  * @return {Promise|Boolean} 是否存在
614
614
  */
615
- DB.prototype.hasTable = async function(table, timeout = 15000) {
615
+ DB.prototype.hasTable = async function (table, timeout = 15000) {
616
616
  if (!table || typeof table !== 'string') {
617
617
  return false;
618
618
  }
@@ -632,7 +632,7 @@ DB.prototype.hasTable = async function(table, timeout = 15000) {
632
632
  * @param {Number} timeout 超时时间(毫秒)
633
633
  * @return {Promise|Boolean} 是否存在
634
634
  */
635
- DB.prototype.hasField = async function(table, field, timeout = 15000) {
635
+ DB.prototype.hasField = async function (table, field, timeout = 15000) {
636
636
  if (!table || !field) {
637
637
  return false;
638
638
  }
@@ -652,7 +652,7 @@ DB.prototype.hasField = async function(table, field, timeout = 15000) {
652
652
  * @param {Number} timeout 超时时间(毫秒)
653
653
  * @return {Promise|Number} 操作结果
654
654
  */
655
- DB.prototype.backupTable = function(table, backup, timeout = 60000) {
655
+ DB.prototype.backupTable = function (table, backup, timeout = 60000) {
656
656
  if (!table || !backup) {
657
657
  $.log.error("[DB] [backupTable] 参数不完整");
658
658
  return 0;
@@ -660,6 +660,87 @@ DB.prototype.backupTable = function(table, backup, timeout = 60000) {
660
660
  return this.exec("CREATE TABLE \`" + backup + "\` LIKE \`" + table + "\`; INSERT INTO \`" + backup + "\` SELECT * FROM \`" + table + "\`;", timeout);
661
661
  };
662
662
 
663
+
664
+ /**
665
+ * @description 创建表
666
+ * @param {String} table 表名
667
+ * @param {Object} model 表模型,键值对,根据值类型创建字段类型,根据键名创建字段名
668
+ * @param {String} key 主键
669
+ * @return {Promise|Number} 操作结果
670
+ */
671
+ DB.prototype.createTable = function (table, model, key = 'id', timeout = 15000) {
672
+ if (!table || !model || !model.fields) {
673
+ $.log.error("[DB] [createTable] 参数不完整");
674
+ return 0;
675
+ }
676
+ var fields = '';
677
+ for (const field in model.fields) {
678
+ const value = model.fields[field];
679
+ let type = '';
680
+ if (field === key) {
681
+ type = 'INT AUTO_INCREMENT PRIMARY KEY';
682
+ } else if (typeof value === 'number') {
683
+ if (value % 1 === 0) {
684
+ // 判断是否时间戳(10位或13位数字)
685
+ const now = Date.now();
686
+ const isTimestamp = (value >= 1000000000 && value <= now * 1.1); // 10位或13位时间戳
687
+ if (isTimestamp) {
688
+ type = 'DATETIME';
689
+ } else {
690
+ type = 'INT';
691
+ }
692
+ } else {
693
+ type = 'FLOAT';
694
+ }
695
+ } else if (typeof value === 'string') {
696
+ type = 'TEXT';
697
+ if (value.length <= 255) {
698
+ // 正则判断是否日期时间型
699
+ if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(value)) {
700
+ type = 'DATETIME';
701
+ }
702
+ // 正则判断是否日期型
703
+ else if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
704
+ type = 'DATE';
705
+ }
706
+ // 正则判断是否时间型
707
+ else if (/^\d{2}:\d{2}:\d{2}$/.test(value)) {
708
+ type = 'TIME';
709
+ }
710
+ // 正则判断是否JSON字符串
711
+ else if (/^[{[]/.test(value) && /[}]]$/.test(value)) {
712
+ type = 'TEXT';
713
+ }
714
+ // 正则判断是否html
715
+ else if (/^<[a-z][\s\S]*>/i.test(value)) {
716
+ type = 'TEXT';
717
+ }
718
+ // 正则判断是否xml
719
+ else if (/^<\?xml[\s\S]*\?>/.test(value)) {
720
+ type = 'TEXT';
721
+ }
722
+ else {
723
+ type = 'VARCHAR(255)';
724
+ }
725
+ }
726
+ }
727
+ else if (typeof value === 'boolean') {
728
+ type = 'tinyint(1)';
729
+ }
730
+ else if (value instanceof Date) {
731
+ // 判断是否Date对象,类型为datetime
732
+ type = 'DATETIME';
733
+ }
734
+ else {
735
+ type = 'BLOB';
736
+ }
737
+ fields += `\`${field}\` ${type}, `;
738
+ }
739
+ fields = fields.slice(0, -2);
740
+ var sql = `CREATE TABLE IF NOT EXISTS \`${table}\` (${fields});`;
741
+ return this.exec(sql, [], timeout);
742
+ }
743
+
663
744
  module.exports = {
664
745
  DB
665
746
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_mysql",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "这是超级美眉mysql帮助函数模块,用于便捷操作mysql,使用await方式,可以避免嵌套函数",
5
5
  "main": "index.js",
6
6
  "dependencies": {
@@ -0,0 +1,193 @@
1
+ const { Mysql } = require('./index');
2
+
3
+ /**
4
+ * 测试 createTable 方法功能
5
+ */
6
+ async function testCreateTable() {
7
+ console.log('开始测试 createTable 方法...');
8
+
9
+ let mysql = null;
10
+ const testDatabase = 'test_mm_mysql';
11
+ const testTable = 'test_users';
12
+
13
+ try {
14
+ // 初始化 MySQL 连接
15
+ mysql = new Mysql({
16
+ host: '127.0.0.1',
17
+ port: 3306,
18
+ user: 'root',
19
+ password: 'Asd159357',
20
+ database: testDatabase,
21
+ connectionLimit: 1
22
+ });
23
+
24
+ // 打开连接
25
+ await mysql.open();
26
+ console.log('✅ 数据库连接成功');
27
+
28
+ // 获取 DB 实例
29
+ const db = mysql.db();
30
+
31
+ // 定义测试表模型
32
+ const userModel = {
33
+ fields: {
34
+ id: 0, // 主键,会被自动设置为 INT AUTO_INCREMENT PRIMARY KEY
35
+ username: 'admin', // 字符串,长度 <= 255,会被设置为 VARCHAR(255)
36
+ email: 'test@example.com', // 字符串,会被设置为 VARCHAR(255)
37
+ age: 25, // 整数,会被设置为 INT
38
+ salary: 5000.50, // 浮点数,会被设置为 FLOAT
39
+ active: true, // boolean 类型,会被设置为 INT
40
+ birth_date: '1990-01-01', // date 类型,会被设置为 DATE
41
+ bio: 'This is a very long bio about the user that exceeds 255 characters in length. ' +
42
+ 'This is necessary to test the text type functionality in the createTable method. ' +
43
+ 'By making this string longer than 255 characters, we can ensure that the field ' +
44
+ 'is created as a TEXT type instead of a VARCHAR(255).', // 字符串,长度 > 255,会被设置为 TEXT
45
+ created_at: '2023-10-25 10:30:00', // 日期时间字符串,会被设置为 DATETIME
46
+ login_time: new Date(), // Date 对象,会被设置为 DATETIME
47
+ last_active: Date.now() // 时间戳,会被设置为 DATETIME
48
+ }
49
+ };
50
+
51
+ // 创建表
52
+ console.log('开始创建测试表...');
53
+ const createResult = await db.createTable(testTable, userModel, 'id', 15000);
54
+
55
+ if (createResult && createResult.affectedRows !== undefined) {
56
+ console.log('✅ 测试表创建成功');
57
+ console.log(' 影响行数:', createResult.affectedRows);
58
+ } else {
59
+ console.log('⚠️ 表可能已存在(CREATE TABLE IF NOT EXISTS)');
60
+ }
61
+
62
+ // 验证表是否存在
63
+ console.log('\n验证表结构...');
64
+ const tableExistsSql = `SHOW TABLES LIKE '${testTable}'`;
65
+ const tables = await mysql.run(tableExistsSql);
66
+
67
+ if (tables.length > 0) {
68
+ console.log('✅ 测试表确实存在');
69
+ } else {
70
+ throw new Error('测试表不存在');
71
+ }
72
+
73
+ // 查看表结构
74
+ const describeSql = `DESCRIBE ${testTable}`;
75
+ const tableStructure = await mysql.run(describeSql);
76
+
77
+ console.log('\n表结构:');
78
+ tableStructure.forEach(row => {
79
+ console.log(` ${row.Field}: ${row.Type} ${row.Null} ${row.Key} ${row.Default} ${row.Extra}`);
80
+ });
81
+
82
+ // 验证字段类型是否正确
83
+ const fieldTypes = {
84
+ id: 'int',
85
+ username: 'varchar(255)',
86
+ email: 'varchar(255)',
87
+ age: 'int',
88
+ salary: 'float',
89
+ active: 'int', // MySQL 中 boolean 会被转换为 tinyint 或 int
90
+ birth_date: 'date',
91
+ bio: 'text',
92
+ created_at: 'datetime',
93
+ login_time: 'datetime', // Date 对象会被设置为 DATETIME
94
+ last_active: 'datetime' // 时间戳会被设置为 DATETIME
95
+ };
96
+
97
+ let allFieldsCorrect = true;
98
+ tableStructure.forEach(row => {
99
+ const expectedType = fieldTypes[row.Field];
100
+ if (expectedType && !row.Type.toLowerCase().includes(expectedType)) {
101
+ console.log(`❌ 字段 ${row.Field} 类型不正确,预期: ${expectedType},实际: ${row.Type}`);
102
+ allFieldsCorrect = false;
103
+ }
104
+ });
105
+
106
+ if (allFieldsCorrect) {
107
+ console.log('✅ 所有字段类型符合预期');
108
+ }
109
+
110
+ // 测试插入数据
111
+ console.log('\n测试插入数据...');
112
+ const currentTime = new Date();
113
+ const insertSql = `INSERT INTO ${testTable} (username, email, age, salary, active, birth_date, bio, created_at, login_time, last_active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
114
+ const insertResult = await mysql.exec(insertSql, ['testuser', 'testuser@example.com', 30, 6000.75, true, '1995-05-15', 'Test user bio', '2023-10-25 11:00:00', currentTime, currentTime]);
115
+
116
+ if (insertResult.insertId) {
117
+ console.log('✅ 数据插入成功,插入ID:', insertResult.insertId);
118
+ } else {
119
+ throw new Error('数据插入失败');
120
+ }
121
+
122
+ // 测试查询数据
123
+ console.log('\n测试查询数据...');
124
+ const selectSql = `SELECT * FROM ${testTable} WHERE id = ?`;
125
+ const user = await mysql.run(selectSql, [insertResult.insertId]);
126
+
127
+ if (user && user.length > 0) {
128
+ console.log('✅ 数据查询成功');
129
+ console.log(' 查询结果:', user[0]);
130
+ } else {
131
+ throw new Error('数据查询失败');
132
+ }
133
+
134
+ // 测试使用自定义主键
135
+ console.log('\n测试使用自定义主键...');
136
+ const customKeyTable = 'test_custom_key';
137
+ const customModel = {
138
+ fields: {
139
+ user_id: 'u001', // 自定义主键,会被设置为 INT AUTO_INCREMENT PRIMARY KEY
140
+ name: 'test', // 字符串
141
+ value: 100, // 整数
142
+ is_active: true, // boolean 类型
143
+ join_date: '2023-01-01' // date 类型
144
+ }
145
+ };
146
+
147
+ const customCreateResult = await db.createTable(customKeyTable, customModel, 'user_id', 15000);
148
+ if (customCreateResult || customCreateResult === 0) {
149
+ console.log('✅ 使用自定义主键创建表成功');
150
+
151
+ // 验证自定义主键表结构
152
+ const customDescribeSql = `DESCRIBE ${customKeyTable}`;
153
+ const customStructure = await mysql.run(customDescribeSql);
154
+ const customKeyField = customStructure.find(field => field.Field === 'user_id');
155
+
156
+ if (customKeyField && customKeyField.Key === 'PRI') {
157
+ console.log('✅ 自定义主键设置正确');
158
+ } else {
159
+ console.log('⚠️ 自定义主键可能未设置为 PRIMARY KEY');
160
+ }
161
+ }
162
+
163
+ // 清理测试表
164
+ console.log('\n清理测试环境...');
165
+ await mysql.exec(`DROP TABLE IF EXISTS ${testTable}`);
166
+ await mysql.exec(`DROP TABLE IF EXISTS ${customKeyTable}`);
167
+ console.log('✅ 测试表已删除');
168
+
169
+ console.log('\n🎉 所有测试通过!createTable 方法功能正常');
170
+
171
+ } catch (error) {
172
+ console.error('❌ 测试失败:', error.message);
173
+ console.error('错误详情:', error);
174
+ return false;
175
+ } finally {
176
+ // 关闭连接
177
+ if (mysql) {
178
+ try {
179
+ await mysql.close();
180
+ console.log('✅ 数据库连接已关闭');
181
+ } catch (closeError) {
182
+ console.error('⚠️ 关闭数据库连接失败:', closeError.message);
183
+ }
184
+ }
185
+ }
186
+
187
+ return true;
188
+ }
189
+
190
+ // 运行测试
191
+ testCreateTable().then(success => {
192
+ process.exit(success ? 0 : 1);
193
+ });