mm_os 4.2.6 → 4.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/README_EN.md +2 -2
  3. package/adapter/adapter.js +105 -52
  4. package/adapter/custom_persistence.js +32 -31
  5. package/adapter/mqtt.js +59 -79
  6. package/adapter/socket.js +188 -58
  7. package/adapter/web.js +92 -68
  8. package/adapter/websocket.js +43 -146
  9. package/com/api/drive.js +732 -708
  10. package/com/api/index.js +196 -198
  11. package/com/cmd/config.tpl.json +48 -122
  12. package/com/cmd/drive.js +176 -1548
  13. package/com/cmd/index.js +287 -1066
  14. package/com/cmd/job/config.json +66 -0
  15. package/com/cmd/job/index.js +241 -0
  16. package/com/cmd/job/test.js +188 -0
  17. package/com/cmd/script.tpl.js +12 -33
  18. package/com/db/drive.js +2206 -2019
  19. package/com/db/index.js +241 -242
  20. package/com/event/config.tpl.json +3 -1
  21. package/com/event/drive.js +3 -1
  22. package/com/event/index.js +73 -49
  23. package/com/event/script.tpl.js +1 -1
  24. package/com/mqtt/config.tpl.json +16 -8
  25. package/com/mqtt/drive.js +696 -676
  26. package/com/mqtt/index.js +1012 -822
  27. package/com/mqtt/index.test.js +503 -0
  28. package/com/mqtt/mm_mqtt.js +446 -420
  29. package/com/mqtt/script.tpl.js +3 -7
  30. package/com/nav/drive.js +7 -7
  31. package/com/nav/index.js +228 -231
  32. package/com/param/index.js +19 -17
  33. package/com/pendant/index.js +467 -441
  34. package/com/socket/drive.js +679 -616
  35. package/com/socket/index.js +94 -48
  36. package/com/sql/drive.js +1283 -1259
  37. package/com/sql/index.js +152 -150
  38. package/com/static/drive.js +1 -1
  39. package/com/static/index.js +232 -226
  40. package/com/task/drive.js +428 -405
  41. package/com/task/index.js +154 -148
  42. package/com/template/drive.js +1 -1
  43. package/com/template/index.js +141 -138
  44. package/com.js +87 -6
  45. package/common/middleware/web_after/index.js +4 -10
  46. package/common/middleware/web_base/index.js +1 -1
  47. package/common/middleware/web_cors/index.js +1 -1
  48. package/common/middleware/web_error/index.js +131 -119
  49. package/common/middleware/web_error/middleware.json +2 -1
  50. package/common/middleware/web_logger/index.js +1 -1
  51. package/common/middleware/web_main/index.js +2 -0
  52. package/common/middleware/web_static/index.js +2 -2
  53. package/common/middleware/web_waf/index.js +1 -1
  54. package/core/app/config.tpl.json +4 -4
  55. package/core/app/index.js +710 -534
  56. package/core/middleware/index.js +1 -9
  57. package/core/mod/config.tpl.json +1 -1
  58. package/core/model/config.tpl.json +1 -1
  59. package/core/plugin/config.tpl.json +15 -3
  60. package/core/plugin/index.js +31 -0
  61. package/core/pusher/index.js +158 -161
  62. package/core/scene/loop.js +1306 -1255
  63. package/core/store/index.js +1794 -1755
  64. package/core/store/sql.js +1488 -1464
  65. package/core/zone/bat/index.js +744 -728
  66. package/core/zone/config.tpl.json +3 -3
  67. package/core/zone/index.js +655 -616
  68. package/os.js +0 -4
  69. package/package.json +16 -16
  70. package/server.js +583 -427
  71. package/com/cmd/nlp.js +0 -525
  72. package/core/app/index copy.js +0 -611
  73. package/software.js +0 -194
  74. package/tcp.js +0 -279
package/com/db/drive.js CHANGED
@@ -1,2019 +1,2206 @@
1
- const Item = require('mm_machine').Drive;
2
- const fs = require('fs');
3
-
4
- if (!$.dict.user_id) {
5
- $.dict.user_id = 'user_id';
6
- }
7
-
8
- var keyword_default =
9
- "`user_id` in (SELECT `user_id` FROM `user_account` WHERE `nickname` LIKE '%{0}%' OR `phone` LIKE '%{0}%' OR `wallet_address` LIKE '%{0}%')";
10
-
11
- var keyword_default_tip = '昵称、手机号、钱包地址';
12
-
13
- /**
14
- * db数据库开发驱动类
15
- * @augments {Item}
16
- * @class
17
- */
18
- class Drive extends Item {
19
- static config = {
20
- // 库表名称(唯一标识)
21
- name: '',
22
- // 标题(中文名)
23
- title: '',
24
- // 表名
25
- table: '',
26
- // 主键,用于实体模型
27
- key: '',
28
- // 字段
29
- fields: [
30
- /* */
31
- ],
32
- index: [],
33
- // 主程序文件 - 默认为空
34
- main: ''
35
- };
36
-
37
- /**
38
- * 构造函数
39
- * @param {object} config 配置参数
40
- * @param {object} parent 父对象
41
- * @class
42
- */
43
- constructor(config, parent) {
44
- super({ ...Drive.config, ...config }, parent);
45
- }
46
- }
47
-
48
- /**
49
- * 重置配置参数
50
- */
51
- Drive.prototype._preset = function () {
52
- this.query_string = ['name', 'title', 'keywords', 'tag', 'description', 'content'];
53
-
54
- // 是否设置数值类型为可查询
55
- this.query_number = ['state', 'uin'];
56
-
57
- // 是否设置数值类型为关键词可查
58
- this.query_keyword = ['name', 'title', 'keywords', 'tag', 'description'];
59
-
60
- // 是否设置以下字段为get查列表SQL时不可见
61
- this.get_not = ['password', 'salt', 'content'];
62
-
63
- // 是否设置以下字段为getObj查对象SQL时不可见
64
- this.get_obj_not = ['password', 'salt', 'display'];
65
-
66
- // 是否设置默认该表仅用户可访问
67
- this.query_default_table = ['user'];
68
- };
69
-
70
- /**
71
- * 解析字段类型
72
- * @param {string} type 字段类型
73
- * @returns {object} 解析结果
74
- */
75
- Drive.prototype._parseFieldType = function (type) {
76
- var tp = type.left('(', true);
77
- var len = type.between('(', ')');
78
- var max = 0;
79
- var decimal = 0;
80
- if (len) {
81
- max = Number(len.left(',', true));
82
- var d = len.right(',');
83
- if (d) {
84
- decimal = Number(d);
85
- }
86
- }
87
- return { tp, max, decimal };
88
- };
89
-
90
- /**
91
- * 解析字段备注
92
- * @param {string} note 字段备注
93
- * @param {string} tp 字段类型
94
- * @param {number} max_length 最大长度
95
- * @returns {object} 解析结果
96
- */
97
- Drive.prototype._parseFieldNote = function (note, tp, max_length) {
98
- var proc_note = note.replace(':', ':').replace('(', '(').replace(')', ')');
99
- var title = proc_note.left(':', true).trim();
100
- var desc = proc_note.right(':');
101
- var description = '';
102
- var map = '';
103
- var min = 0;
104
- var max = 0;
105
- var local_max_length = max_length;
106
-
107
- if (desc) {
108
- var range = desc.between('[', ']');
109
- if (range) {
110
- var min_str = range.left(',', true);
111
- if (min_str) {
112
- min = Number(min_str);
113
- }
114
- var max_str = range.right(',');
115
- if (max_str) {
116
- var n = Number(max_str);
117
- if (tp === 'varchar') {
118
- if (n < local_max_length) {
119
- local_max_length = n;
120
- }
121
- } else {
122
- max = n;
123
- }
124
- }
125
- }
126
- map = desc.between('(', ')');
127
- description = desc.right(']', true).replace(`(${map})`, '').trim();
128
- }
129
- let min_length = 0;
130
- if (min > 0) {
131
- min_length = min;
132
- }
133
- return { title, description, map, min, max, min_length, max_length: local_max_length };
134
- };
135
-
136
- /**
137
- * 计算字段最大值
138
- * @param {string} tp 字段类型
139
- * @param {number} max 当前最大值
140
- * @param {number} max_length 最大长度
141
- * @returns {number} 计算后的最大值
142
- */
143
- Drive.prototype._calcMaxValue = function (tp, max, max_length) {
144
- var local_max = max;
145
- if (local_max === 0) {
146
- switch (tp) {
147
- case 'tinyint':
148
- local_max = 1;
149
- break;
150
- case 'smallint':
151
- local_max = 32767;
152
- break;
153
- case 'mediumint':
154
- local_max = 8388607;
155
- break;
156
- case 'int':
157
- local_max = 2147483647;
158
- break;
159
- case 'bigint':
160
- local_max = 0;
161
- break;
162
- default:
163
- break;
164
- }
165
- if (max_length) {
166
- var num = Math.pow(10, max_length) - 1;
167
- if (local_max > num) {
168
- local_max = num;
169
- }
170
- }
171
- }
172
- return local_max;
173
- };
174
-
175
- /**
176
- * 处理字段默认值
177
- * @param {string} tp 字段类型
178
- * @param {boolean} not_null 是否非空
179
- * @param {*} default_val 默认值
180
- * @returns {*} 处理后的默认值
181
- */
182
- Drive.prototype._handleDefaultValue = function (tp, not_null, default_val) {
183
- if (not_null && !default_val) {
184
- if (tp === 'varchar' || tp === 'text') {
185
- return '';
186
- } else if (tp === 'datetime' || tp === 'timestamp') {
187
- return 'CURRENT_TIMESTAMP';
188
- } else {
189
- return '0';
190
- }
191
- }
192
- return default_val;
193
- };
194
-
195
- /**
196
- * 创建字段模型对象
197
- * @param {string} name 字段名
198
- * @param {string} title 字段标题
199
- * @param {string} description 字段描述
200
- * @param {string} tp 字段类型
201
- * @param {boolean} pk 是否主键
202
- * @param {boolean} auto 是否自动
203
- * @param {boolean} not_null 是否非空
204
- * @param {number} min_length 最小长度
205
- * @param {number} max_length 最大长度
206
- * @param {number} min 最小值
207
- * @param {number} max 最大值
208
- * @param {number} decimal 小数位
209
- * @param {*} default_val 默认值
210
- * @param {object} map 转换编排
211
- * @returns {object} 字段模型对象
212
- * @private
213
- */
214
- Drive.prototype._createFieldModel = function (
215
- name, title, description, tp, pk, auto, not_null,
216
- min_length, max_length, min, max, decimal, default_val, map
217
- ) {
218
- return {
219
- // 字段名
220
- 'name': name,
221
- // 字段标题
222
- 'title': title,
223
- // 字段描述
224
- 'description': description,
225
- // 字段类型 smallint短整数、mediumint中长整数、int整数、float浮点数、double双精度、tinyint二进制(0和1的布尔)、text文本、varchar字符串、datetime日期时间、date日期、time时间、timestamp时间戳
226
- 'type': tp,
227
- // 是否主键
228
- 'key': pk,
229
- // 自动
230
- 'auto': auto,
231
- // 是否含符号
232
- 'symbol': tp === 'float' || tp === 'decimal',
233
- // 是否填充零,用于数字类型
234
- 'fill_zero': false,
235
- // 非空
236
- 'not_null': not_null,
237
- // 最小长度
238
- 'min_length': min_length,
239
- // 最大长度
240
- 'max_length': max_length,
241
- // 最小值
242
- 'min': min,
243
- // 最大值
244
- 'max': max,
245
- // 小数位
246
- 'decimal': decimal,
247
- // 默认值
248
- 'default': default_val,
249
- // 转换编排
250
- 'map': map
251
- };
252
- };
253
-
254
- /**
255
- * 创建字段模型
256
- * @param {object} fields 字段对象
257
- * @param {string} fields.name 字段名称
258
- * @param {object} fields.default 默认值
259
- * @param {object} fields.notnull 是否非空
260
- * @param {object} fields.type 字段类型
261
- * @param {object} fields.pk 是否主键
262
- * @param {object} fields.auto 是否自动增长
263
- * @param {object} fields.note 字段备注
264
- * @returns {object} 返回字段模型
265
- */
266
- Drive.prototype.model = function (fields) {
267
- var {
268
- name,
269
- notnull,
270
- type,
271
- pk,
272
- note,
273
- auto
274
- } = fields;
275
-
276
- var { tp, max: max_length, decimal } = this._parseFieldType(type);
277
- var {
278
- title,
279
- description,
280
- map,
281
- min,
282
- max: parsedMax,
283
- min_length,
284
- max_length: parsedMaxLength
285
- } = this._parseFieldNote(note, tp, max_length);
286
-
287
- var max = this._calcMaxValue(tp, parsedMax, parsedMaxLength);
288
-
289
- if (name.indexOf('_id') !== -1 || name == 'id') {
290
- min = 0;
291
- }
292
-
293
- var not_null = notnull | pk | (tp !== 'varchar' && tp !== 'text');
294
-
295
- var config = this.config;
296
- if (pk && !config.key) {
297
- config.key = name;
298
- }
299
-
300
- var default_value = this._handleDefaultValue(tp, not_null, fields.default);
301
-
302
- return this._createFieldModel(
303
- name, title, description, tp, pk, auto, not_null,
304
- min_length, parsedMaxLength, min, max, decimal,
305
- default_value, map
306
- );
307
- };
308
-
309
- /**
310
- * 创建索引字段模型
311
- * @param {object} o 索引对象
312
- * @property {string} o.Key_name 索引名称
313
- * @property {string} o.Non_unique 是否唯一索引
314
- * @property {string} o.Column_name 索引字段
315
- * @property {string} o.Manager_comment 索引备注
316
- * @returns {object} 返回索引模型
317
- */
318
- Drive.prototype.newIndexModel = function (o) {
319
- return {
320
- name: o.Key_name,
321
- type: o.Non_unique ? 'unique' : 'index',
322
- fields: o.Column_name.replace(/`/g, '').split(','),
323
- comment: o.Manager_comment
324
- };
325
- };
326
-
327
- /**
328
- * 从数据库更新索引配置
329
- * @param {object} db 数据库管理器
330
- * @returns {Array} 索引列表
331
- */
332
- Drive.prototype._updateFile = async function (db) {
333
- var sql = 'SHOW INDEX FROM `' + this.config.table + '`';
334
- var rows = await db.run(sql);
335
- var dict = {};
336
- for (var i = 0; i < rows.length; i++) {
337
- var o = rows[i];
338
- if (o.Key_name !== 'PRIMARY') {
339
- if (!dict[o.Key_name]) {
340
- dict[o.Key_name] = {
341
- Column_name: []
342
- };
343
- }
344
- dict[o.Key_name].Column_name.push(o.Column_name);
345
- }
346
- }
347
- var list = [];
348
- for (var k in dict) {
349
- var m = this.newIndexModel(dict[k]);
350
- list.push(m);
351
- }
352
-
353
- return list;
354
- };
355
-
356
- /**
357
- * 从数据库更新配置
358
- * @param {object} db 数据库管理器
359
- * @param {boolean} cover 是否覆盖文件
360
- */
361
- Drive.prototype.updateFile = async function (db, cover) {
362
- var cg = this.config;
363
- var list = [];
364
-
365
- // 查询表注释并修改
366
- var sql = "SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" + db
367
- .database() +
368
- "' && TABLE_NAME = '" + cg.table + "';";
369
-
370
- var lt = await db.run(sql);
371
- if (lt && lt.length > 0) {
372
- var commit = lt[0].TABLE_COMMENT;
373
- var arr = commit.replace(':', ':').split(':');
374
- cg.title = arr[0];
375
- if (arr.length > 1) {
376
- cg.description = arr[1];
377
- }
378
- }
379
-
380
- // 设置表名
381
- db.table = cg.table + '';
382
-
383
- // 获取所有字段
384
- var fields = await db.fields();
385
- for (var i = 0; i < fields.length; i++) {
386
- var field = this.model(fields[i]);
387
- list.push(field);
388
- }
389
- cg.fields = list;
390
- cg.index = await this._updateFile(db);
391
- if (!cg.name) {
392
- cg.name = cg.table;
393
- }
394
- await this.updateApp(cover);
395
- };
396
-
397
- /**
398
- * 更新索引
399
- * @param {object} db 数据库管理器
400
- */
401
- Drive.prototype._updateDb = async function (db) {
402
- var cg = this.config;
403
- let index = cg.index || [];
404
- // 更新索引
405
- if (index) {
406
- var len = index.length;
407
- for (var i = 0; i < len; i++) {
408
- var o = index[i];
409
- var sql_start = '';
410
- if (o.type === 'unique') {
411
- sql_start = 'CREATE UNIQUE INDEX `';
412
- } else {
413
- sql_start = 'CREATE INDEX `';
414
- }
415
- await db.exec(sql_start + o.name + '` ON `' + table + '` (' + o.fields + ');');
416
- }
417
- }
418
- };
419
-
420
- /**
421
- * 初始化数据表
422
- * @param {object} db 数据库管理器
423
- * @param {object} cg 配置对象
424
- * @param {Array} list 字段列表
425
- * @returns {Array} 字段列表
426
- */
427
- Drive.prototype._initTable = async function (db, cg, list) {
428
- var fields = await db.fields();
429
- if (fields.length === 0) {
430
- var k = cg.key;
431
- var len = list.length;
432
- for (var i = 0; i < len; i++) {
433
- var o = list[i];
434
- if (k === o.name) {
435
- await db.addTable(cg.table, o.name, o.type, o.auto, cg.title + ':' + cg.description);
436
- fields.push({
437
- name: o.name
438
- });
439
- break;
440
- }
441
- }
442
- } else {
443
- var commit = cg.title + ':' + cg.description;
444
- var sql = "alter table `{0}` comment '{1}';".replace('{0}', cg.table).replace('{1}', commit);
445
- await db.exec(sql);
446
- }
447
- return fields;
448
- };
449
-
450
- /**
451
- * 删除配置中没有的字段
452
- * @param {object} db 数据库管理器
453
- * @param {Array} fields 数据库字段列表
454
- * @param {Array} list 配置字段列表
455
- */
456
- Drive.prototype._deleteUnusedFields = async function (db, fields, list) {
457
- for (var i = 0; i < fields.length; i++) {
458
- var o = fields[i];
459
- var obj = list.getObj({
460
- name: o.name
461
- });
462
-
463
- if (!obj) {
464
- await db.fieldDel(o.name);
465
- }
466
- }
467
- };
468
-
469
- /**
470
- * 生成字段SQL
471
- * @param {object} o 字段对象
472
- * @returns {string} 字段SQL
473
- */
474
- Drive.prototype._generateFieldSql = function (o) {
475
- var type = this._getTypeSql(o);
476
- var notnull = this._getNotNullSql(o);
477
- var value = this._getValueSql(o);
478
- var note = this._getNoteSql(o);
479
- return this._buildFieldSql(o.name, type, notnull, value, note);
480
- };
481
-
482
- /**
483
- * 获取类型SQL
484
- * @param {object} o 字段对象
485
- * @returns {string} 类型SQL
486
- * @private
487
- */
488
- Drive.prototype._getTypeSql = function (o) {
489
- var type = o.type;
490
- var max_len = o.max_length || 0;
491
-
492
- if (max_len > 0) {
493
- // decimal仅对浮点类型有效(int/smallint/bigint等使用decimal=0时不追加)
494
- var is_float_type = o.type.indexOf('float') !== -1 ||
495
- o.type.indexOf('double') !== -1 ||
496
- o.type.indexOf('decimal') !== -1;
497
- if (is_float_type && o.decimal !== undefined && o.decimal !== null) {
498
- type += '(' + max_len + ',' + o.decimal + ')';
499
- } else {
500
- type += '(' + max_len + ')';
501
- }
502
- }
503
-
504
- // 类型未包含unsigned且为非字符/日期类型时追加UNSIGNED
505
- if (!o.symbol && type.indexOf('unsigned') === -1 &&
506
- (o.type !== 'varchar' && o.type !== 'longtext' && o.type !== 'text' &&
507
- o.type !== 'date' && o.type !== 'time' && o.type !== 'datetime' && o.type !== 'timestamp')) {
508
- type += ' UNSIGNED';
509
- }
510
-
511
- return type;
512
- };
513
-
514
- /**
515
- * 获取NOT NULL SQL
516
- * @param {object} o 字段对象
517
- * @returns {string} NOT NULL SQL
518
- * @private
519
- */
520
- Drive.prototype._getNotNullSql = function (o) {
521
- var notnull = '';
522
-
523
- if (this._isNotNullRequired(o)) {
524
- notnull = 'NOT NULL';
525
- }
526
-
527
- if (this._isAutoInc(o)) {
528
- notnull += ' AUTO_INCREMENT';
529
- }
530
-
531
- return notnull;
532
- };
533
-
534
- /**
535
- * 检查是否需要NOT NULL
536
- * @param {object} o 字段对象
537
- * @returns {boolean} 是否需要NOT NULL
538
- * @private
539
- */
540
- Drive.prototype._isNotNullRequired = function (o) {
541
- return o.not_null || this._isDateTimeType(o.type) || this._isNumericType(o.type);
542
- };
543
-
544
- /**
545
- * 检查是否为日期时间类型
546
- * @param {string} type 类型
547
- * @returns {boolean} 是否为日期时间类型
548
- * @private
549
- */
550
- Drive.prototype._isDateTimeType = function (type) {
551
- return type === 'date' || type === 'time' || type === 'datetime' || type === 'timestamp';
552
- };
553
-
554
- /**
555
- * 检查是否为数字类型
556
- * @param {string} type 类型
557
- * @returns {boolean} 是否为数字类型
558
- * @private
559
- */
560
- Drive.prototype._isNumericType = function (type) {
561
- return type !== 'varchar' && type !== 'longtext' && type !== 'text' && !this._isDateTimeType(type);
562
- };
563
-
564
- /**
565
- * 检查是否需要AUTO_INCREMENT
566
- * @param {object} o 字段对象
567
- * @returns {boolean} 是否需要AUTO_INCREMENT
568
- * @private
569
- */
570
- Drive.prototype._isAutoInc = function (o) {
571
- return !!o.auto && !this._isDateTimeType(o.type);
572
- };
573
-
574
- /**
575
- * 获取默认值SQL
576
- * @param {object} o 字段对象
577
- * @returns {string} 默认值SQL
578
- * @private
579
- */
580
- Drive.prototype._getValueSql = function (o) {
581
- if (this._isAutoTime(o)) {
582
- return 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
583
- }
584
-
585
- if (this._isAutoInc(o)) {
586
- return '';
587
- }
588
-
589
- if (o.default === null) {
590
- return 'DEFAULT NULL';
591
- }
592
-
593
- if (o.default !== undefined) {
594
- return this._getDefaultValueSql(o);
595
- }
596
-
597
- return this._getDefaultEmptySql(o);
598
- };
599
-
600
- /**
601
- * 检查是否为自动时间
602
- * @param {object} o 字段对象
603
- * @returns {boolean} 是否为自动时间
604
- * @private
605
- */
606
- Drive.prototype._isAutoTime = function (o) {
607
- return !!o.auto && this._isDateTimeType(o.type);
608
- };
609
-
610
- /**
611
- * 获取默认值SQL
612
- * @param {object} o 字段对象
613
- * @returns {string} 默认值SQL
614
- * @private
615
- */
616
- Drive.prototype._getDefaultValueSql = function (o) {
617
- if (this._isCurrentDefault(o)) {
618
- return 'DEFAULT ' + o.default;
619
- } else if (this._isStringType(o.type)) {
620
- return "DEFAULT '" + o.default + "'";
621
- } else {
622
- return 'DEFAULT ' + o.default;
623
- }
624
- };
625
-
626
- /**
627
- * 检查是否为当前时间默认值
628
- * @param {object} o 字段对象
629
- * @returns {boolean} 是否为当前时间默认值
630
- * @private
631
- */
632
- Drive.prototype._isCurrentDefault = function (o) {
633
- return this._isDateTimeType(o.type) && o.default && o.default.indexOf('CURR') !== -1;
634
- };
635
-
636
- /**
637
- * 检查是否为字符串类型
638
- * @param {string} type 类型
639
- * @returns {boolean} 是否为字符串类型
640
- * @private
641
- */
642
- Drive.prototype._isStringType = function (type) {
643
- return type === 'varchar' || type === 'longtext' || type === 'text' ||
644
- type === 'date' || type === 'time' || type === 'datetime' || type === 'timestamp';
645
- };
646
-
647
- /**
648
- * 获取空默认值SQL
649
- * @param {object} o 字段对象
650
- * @returns {string} 空默认值SQL
651
- * @private
652
- */
653
- Drive.prototype._getDefaultEmptySql = function (o) {
654
- if (this._isStringType(o.type)) {
655
- return "DEFAULT ''";
656
- } else {
657
- return 'DEFAULT 0';
658
- }
659
- };
660
-
661
- /**
662
- * 获取注释SQL
663
- * @param {object} o 字段对象
664
- * @returns {string} 注释SQL
665
- * @private
666
- */
667
- Drive.prototype._getNoteSql = function (o) {
668
- var note = (o.title || '') + ':';
669
-
670
- if (o.type === 'varchar' || o.type === 'text' || o.type === 'longtext') {
671
- if (o.max_length) {
672
- note += '[' + (o.min_length || '0') + ',' + o.max_length + ']';
673
- } else if (o.min_length) {
674
- note += '[' + o.min_length + ']';
675
- }
676
- } else {
677
- if (o.max) {
678
- note += '[' + (o.min || '0') + ',' + o.max + ']';
679
- } else if (o.min) {
680
- note += '[' + o.min + ']';
681
- }
682
- }
683
-
684
- note += (o.description || '');
685
- if (o.map) {
686
- note += '(' + o.map + ')';
687
- }
688
-
689
- return note;
690
- };
691
-
692
- /**
693
- * 构建字段SQL
694
- * @param {string} name 字段名
695
- * @param {string} type 类型
696
- * @param {string} notnull NOT NULL
697
- * @param {string} value 默认值
698
- * @param {string} note 注释
699
- * @returns {string} 字段SQL
700
- * @private
701
- */
702
- Drive.prototype._buildFieldSql = function (name, type, notnull, value, note) {
703
- var sql = "`{1}` {2} {3} {4} COMMENT '{5}'";
704
- return sql.replace('{1}', name).replace('{2}', type).replace('{3}', notnull).replace('{4}', value)
705
- .replace('{5}', note);
706
- };
707
-
708
- /**
709
- * 添加或修改字段
710
- * @param {object} db 数据库管理器
711
- * @param {string} table 表名
712
- * @param {object} o 字段对象
713
- * @param {Array} fields 数据库字段列表
714
- */
715
- Drive.prototype._addOrUpdateField = async function (db, table, o, fields) {
716
- var arr = fields.filter(function (f) {
717
- return f.name === o.name;
718
- });
719
- var sql = this._generateFieldSql(o);
720
-
721
- if (arr.length === 0) {
722
- // 如果没有则添加
723
- await db.exec('alter table `{0}` add '.replace('{0}', table) + sql);
724
- } else {
725
- // 如果有则修改
726
- await db.exec('alter table `{0}` change `{1}` '.replace('{0}', table).replace('{1}', o.name) + sql);
727
- }
728
- };
729
-
730
- /**
731
- * 通过配置更新数据库
732
- * @param {object} db 数据库管理器
733
- * @returns {string} 更新成功返回空,否则返回错误提示
734
- */
735
- Drive.prototype.updateDb = async function (db) {
736
- var cg = this.config;
737
- var table = cg.table + '';
738
- var list = cg.fields;
739
- db.table = table;
740
- var fields = await this._initTable(db, cg, list);
741
-
742
- if (fields.length > 0) {
743
- // 删除配置中没有的字段
744
- await this._deleteUnusedFields(db, fields, list);
745
-
746
- // 添加或修改配置
747
- var len = list.length;
748
- for (var i = 0; i < len; i++) {
749
- var o = list[i];
750
- await this._addOrUpdateField(db, table, o, fields);
751
- }
752
- } else {
753
- return '数据表更新失败';
754
- }
755
- };
756
-
757
- /**
758
- * 确定目录结构
759
- * @param {object} cg 配置对象
760
- * @returns {object} 目录信息
761
- * @private
762
- */
763
- Drive.prototype._deteDir = function (cg) {
764
- var arr = cg.table.split('_');
765
- var scope = arr[0];
766
- var p = './app/'.fullname();
767
- var dir = ('./' + scope).fullname(p);
768
- var dir_api = ('./plugin/server').fullname(dir);
769
- var db_dir = ('./db').fullname(dir_api);
770
- var config_file = ('./' + cg.table.replace(scope + '_', '') + '.db.json').fullname(db_dir);
771
-
772
- return {
773
- scope,
774
- dir,
775
- dir_api,
776
- db_dir,
777
- config_file
778
- };
779
- };
780
-
781
- /**
782
- * 确保目录存在
783
- * @param {string} dir 目录路径
784
- * @private
785
- */
786
- Drive.prototype._ensureDir = function (dir) {
787
- if (!fs.existsSync(dir)) {
788
- fs.mkdirSync(dir);
789
- }
790
- };
791
-
792
- /**
793
- * 处理配置文件
794
- * @param {string} f 文件路径
795
- * @param {boolean} cover 是否覆盖文件
796
- * @private
797
- */
798
- Drive.prototype._handleConfigFile = function (f, cover) {
799
- if (!f.hasFile()) {
800
- this.save();
801
- } else if (cover) {
802
- var jobj = f.loadJson();
803
- var o = { ...this.config };
804
- if (!o.title) {
805
- delete o.title;
806
- }
807
- if (!o.description) {
808
- delete o.description;
809
- }
810
- $.push(jobj, o, true);
811
- f.saveText(JSON.stringify(jobj, null, 2));
812
- }
813
- };
814
-
815
- /**
816
- * 更新应用,根据表生成目录结构和文件
817
- * @param {boolean} cover 是否覆盖文件
818
- */
819
- Drive.prototype.updateApp = async function (cover) {
820
- var cg = this.config;
821
- var f;
822
- var dir_api;
823
-
824
- if (!this.config_file) {
825
- var dirs = this._deteDir(cg);
826
- this._ensureDir(dirs.dir);
827
- this._ensureDir(dirs.dir + '/plugin');
828
- this._ensureDir(dirs.dir_api);
829
- this._ensureDir(dirs.db_dir);
830
- this._dir = dirs.db_dir;
831
- this.config_file = dirs.config_file;
832
- f = dirs.config_file;
833
- dir_api = dirs.dir_api;
834
- } else {
835
- if (this.config_file.endsWith('.db.json')) {
836
- f = this.config_file;
837
- } else {
838
- f = this.config_file + '.db.json';
839
- }
840
- }
841
-
842
- if (f) {
843
- f.addDir();
844
- if (!dir_api) {
845
- dir_api = f.dirname().dirname();
846
- }
847
- // 处理db配置文件,生成xxx.db.json文件
848
- this._handleConfigFile(f, cover);
849
- // 更新API及相关配置文件
850
- this.updateApi(dir_api, cover);
851
- }
852
- };
853
-
854
- /**
855
- * 确保API目录存在
856
- * @param {string} dir 目录路径
857
- * @private
858
- */
859
- Drive.prototype._ensureApiDir = function (dir) {
860
- if (!fs.existsSync(dir)) {
861
- fs.mkdirSync(dir);
862
- }
863
- };
864
-
865
- /**
866
- * 确定API目录路径
867
- * @param {string} dir 基础目录
868
- * @param {string} app 应用名称
869
- * @param {string} type 类型 (client 或 manage)
870
- * @param {string} name 目录名称
871
- * @returns {string} 完整目录路径
872
- * @private
873
- */
874
- Drive.prototype._getApiDir = function (dir, app, type, name) {
875
- var l = $.slash;
876
- let event_api = $.admin.event('api');
877
- var o = event_api.getMod(app + '_' + type);
878
- var api_dir;
879
-
880
- if (o) {
881
- api_dir = dir + l + 'api_' + app + '_' + type;
882
- } else {
883
- api_dir = dir + l + 'api_' + type;
884
- }
885
-
886
- this._ensureApiDir(api_dir);
887
- api_dir += '/' + name;
888
- this._ensureApiDir(api_dir);
889
-
890
- return api_dir;
891
- };
892
-
893
- /**
894
- * 更新API及相关配置文件
895
- * @param {string} dir API存放目录
896
- * @param {boolean} cover 是否覆盖文件
897
- */
898
- Drive.prototype.updateApi = async function (dir, cover) {
899
- var cg = this.config;
900
- var arr = cg.table.split('_');
901
-
902
- var name = 'root';
903
- if (arr.length > 1) {
904
- name = cg.table.replace(arr[0], '').trim('_');
905
- }
906
- var app = arr[0];
907
-
908
- var client = this._getApiDir(dir, app, 'client', name);
909
- var manage = this._getApiDir(dir, app, 'manage', name);
910
-
911
- await this.newSql(client, manage, cover);
912
- await this.newParam(client, manage, cover);
913
- this.newApi(client, manage, cover);
914
- };
915
-
916
- /**
917
- * 新建event配置文件和文件
918
- * @param {string} dir 保存的路径
919
- * @param {string} path 检索路径
920
- * @param {string} scope 接口域
921
- */
922
- Drive.prototype.newEvent = async function (dir, path, scope) {
923
- var f = dir + '/main.js';
924
- if (!f.hasFile()) {
925
- var code = (__dirname + '/event_script.js').loadText();
926
- code = code.replaceAll('{0}', path).replaceAll('{1}', scope);
927
- f.saveText(code);
928
- }
929
- };
930
-
931
- /**
932
- * 处理带逗号的列表映射
933
- * @param {Array} list 列表
934
- * @returns {Array} 处理后的列表
935
- * @private
936
- */
937
- Drive.prototype._handleCommaList = function (list) {
938
- return list.map((o) => {
939
- var arr = o.split(',');
940
- var value = arr[0];
941
- if (arr.length > 0) {
942
- return {
943
- name: arr[1],
944
- value: value
945
- };
946
- } else {
947
- return {
948
- name: value,
949
- value
950
- };
951
- }
952
- });
953
- };
954
-
955
- /**
956
- * 处理数字列表映射
957
- * @param {Array} list 列表
958
- * @param {string} map 映射字符串
959
- * @returns {Array} 处理后的列表
960
- * @private
961
- */
962
- Drive.prototype._handleNumberList = function (list, map) {
963
- if (map.indexOf('0') !== 0) {
964
- list.unshift('');
965
- }
966
- return list.map((value) => {
967
- return value.replace(/[0-9]+/, '');
968
- });
969
- };
970
-
971
- /**
972
- * 处理简单列表映射
973
- * @param {Array} list 列表
974
- * @returns {Array} 处理后的列表
975
- * @private
976
- */
977
- Drive.prototype._handleSimpleList = function (list) {
978
- return list.map((value) => {
979
- return {
980
- name: value,
981
- value
982
- };
983
- });
984
- };
985
-
986
- /**
987
- * 处理点分隔的映射
988
- * @param {object} obj 字段对象
989
- * @param {string} map 映射字符串
990
- * @returns {object} 处理后的格式对象
991
- * @private
992
- */
993
- Drive.prototype._handleDotMap = function (obj, map) {
994
- var arr = map.split('.');
995
- var format = {
996
- table: arr[0],
997
- name: arr[1]
998
- };
999
- if (arr.length > 2) {
1000
- format.id = arr[2];
1001
- } else {
1002
- format.id = obj.name;
1003
- }
1004
- return format;
1005
- };
1006
-
1007
- /**
1008
- * 处理简单映射
1009
- * @param {object} obj 字段对象
1010
- * @param {string} map 映射字符串
1011
- * @returns {object} 处理后的格式对象
1012
- * @private
1013
- */
1014
- Drive.prototype._handleSimpleMap = function (obj, map) {
1015
- return {
1016
- table: map,
1017
- id: obj.name,
1018
- name: 'name'
1019
- };
1020
- };
1021
-
1022
- /**
1023
- * 获取格式
1024
- * @param {object} obj 字段对象
1025
- * @returns {object} 返回格式
1026
- */
1027
- Drive.prototype.getFormat = async function (obj) {
1028
- var map = obj.map;
1029
- var format = {
1030
- key: obj.name,
1031
- title: obj.title.replace('ID', '').replace('id', '')
1032
- };
1033
-
1034
- if (map.indexOf('|') !== -1) {
1035
- var list = map.split('|');
1036
- if (map.indexOf(',') !== -1) {
1037
- format.list = this._handleCommaList(list);
1038
- } else if (/^[0-9]+/.test(map)) {
1039
- format.list = this._handleNumberList(list, map);
1040
- } else {
1041
- format.list = this._handleSimpleList(list);
1042
- }
1043
- } else {
1044
- if (map.indexOf('.') !== -1) {
1045
- Object.assign(format, this._handleDotMap(obj, map));
1046
- } else {
1047
- Object.assign(format, this._handleSimpleMap(obj, map));
1048
- }
1049
- }
1050
-
1051
- return format;
1052
- };
1053
-
1054
- /**
1055
- * 处理字段选择
1056
- * @param {string} field_name 字段名
1057
- * @param {string} field_type 字段类型
1058
- * @returns {object} 处理结果
1059
- * @private
1060
- */
1061
- Drive.prototype._procFieldSel = function (field_name, field_type) {
1062
- var res = {
1063
- field: '',
1064
- field_obj: ''
1065
- };
1066
-
1067
- if (this.isCan(field_name, this.get_not, field_type)) {
1068
- res.field += ',`' + field_name + '`';
1069
- }
1070
- if (this.isCan(field_name, this.get_obj_not)) {
1071
- res.field_obj += ',`' + field_name + '`';
1072
- }
1073
- return res;
1074
- };
1075
-
1076
- /**
1077
- * 处理字符串类型字段
1078
- * @param {object} query 查询对象
1079
- * @param {string} field_name 字段名
1080
- * @param {string} keyword 关键词
1081
- * @returns {string} 更新后的关键词
1082
- * @private
1083
- */
1084
- Drive.prototype._procStringQuery = function (query, field_name, keyword) {
1085
- var new_keyword = keyword;
1086
- query[field_name] = '`' + field_name + "` like '%{0}%'";
1087
- if (this.isSet(field_name, this.query_keyword)) {
1088
- new_keyword += ' || `' + field_name + "` like '%{0}%'";
1089
- }
1090
- return new_keyword;
1091
- };
1092
-
1093
- /**
1094
- * 处理日期时间类型字段
1095
- * @param {object} query 查询对象
1096
- * @param {string} field_name 字段名
1097
- * @private
1098
- */
1099
- Drive.prototype._procDateTimeQuery = function (query, field_name) {
1100
- query[field_name + '_min'] = '`' + field_name + "` >= '{0}'";
1101
- query[field_name + '_max'] = '`' + field_name + "` <= '{0}'";
1102
- };
1103
-
1104
- /**
1105
- * 处理数字类型字段
1106
- * @param {object} query 查询对象
1107
- * @param {object} update_obj 更新对象
1108
- * @param {string} field_name 字段名
1109
- * @param {string} uid 用户ID
1110
- * @param {boolean} query_default_user 是否默认用户查询
1111
- * @param {string} orderby 排序
1112
- * @returns {object} 处理结果
1113
- * @private
1114
- */
1115
- Drive.prototype._procNumberQuery = function (
1116
- query, update_obj, field_name, uid, query_default_user, orderby
1117
- ) {
1118
- var res = {
1119
- orderby: orderby,
1120
- update: { ...update_obj }
1121
- };
1122
-
1123
- if (!field_name.endsWith('id')) {
1124
- query[field_name + '_min'] = '`' + field_name + '` >= {0}';
1125
- query[field_name + '_max'] = '`' + field_name + '` <= {0}';
1126
- res.update[field_name + '_add'] = '`' + field_name + '` = `' + field_name + '` + {0}';
1127
-
1128
- if (field_name === 'sort' || field_name === 'display' || field_name === 'orderby') {
1129
- res.orderby = '`' + field_name + '` asc';
1130
- }
1131
- } else if (field_name === uid && query_default_user) {
1132
- res.query_default = '`' + field_name + '` = {' + uid + '}';
1133
- } else if (field_name === 'available' || field_name === 'show') {
1134
- res.query_default = '`' + field_name + '` = 1';
1135
- }
1136
-
1137
- return res;
1138
- };
1139
-
1140
- /**
1141
- * 处理字段类型
1142
- * @param {string} type 字段类型
1143
- * @param {string} field_name 字段名
1144
- * @param {object} query 查询对象
1145
- * @param {object} update_obj 更新对象
1146
- * @param {string} uid 用户ID
1147
- * @param {boolean} query_default_user 是否默认用户查询
1148
- * @param {string} orderby 排序
1149
- * @param {string} keyword 关键词
1150
- * @returns {object} 处理结果
1151
- * @private
1152
- */
1153
- Drive.prototype._procFieldType = function (
1154
- type, field_name, query, update_obj, uid, query_default_user, orderby, keyword
1155
- ) {
1156
- var result = {
1157
- keyword: keyword,
1158
- orderby: orderby,
1159
- update_obj: update_obj,
1160
- query_default: {}
1161
- };
1162
-
1163
- if (type === 'varchar' || type === 'text' || type === 'longtext') {
1164
- result.keyword = this._procStringQuery(query, field_name, keyword);
1165
- } else if (type === 'date' || type === 'time' || type === 'datetime' || type === 'timestamp') {
1166
- this._procDateTimeQuery(query, field_name);
1167
- } else if (type !== 'tinyint') {
1168
- var num_res = this._procNumberQuery(
1169
- query, update_obj, field_name, uid, query_default_user, orderby
1170
- );
1171
- result.orderby = num_res.orderby;
1172
- result.update_obj = num_res.update;
1173
- if (num_res.query_default) {
1174
- result.query_default[field_name] = num_res.query_default;
1175
- }
1176
- }
1177
-
1178
- return result;
1179
- };
1180
-
1181
- /**
1182
- * 处理单个字段
1183
- * @param {object} o 字段对象
1184
- * @param {object} query 查询对象
1185
- * @param {object} update_obj 更新对象
1186
- * @param {string} uid 用户ID
1187
- * @param {boolean} query_default_user 是否默认用户查询
1188
- * @param {string} orderby 排序
1189
- * @param {string} keyword 关键词
1190
- * @returns {object} 处理结果
1191
- * @private
1192
- */
1193
- Drive.prototype._procSingleField = async function (
1194
- o, query, update_obj, uid, query_default_user, orderby, keyword
1195
- ) {
1196
- var { type, name } = o;
1197
- var result = {
1198
- field: '*',
1199
- field_obj: '*',
1200
- keyword: keyword,
1201
- orderby: orderby,
1202
- update_obj: update_obj,
1203
- query_default: {},
1204
- format: []
1205
- };
1206
-
1207
- var field_sel = this._procFieldSel(name, type);
1208
- result.field = field_sel.field;
1209
- result.field_obj = field_sel.field_obj;
1210
-
1211
- var type_info = this._procFieldType(
1212
- type, name, query, update_obj, uid, query_default_user, orderby, keyword
1213
- );
1214
- result.keyword = type_info.keyword;
1215
- result.orderby = type_info.orderby;
1216
- result.update_obj = type_info.update_obj;
1217
- result.query_default = type_info.query_default;
1218
-
1219
- if (o.map) {
1220
- var fmt = await this.getFormat(o);
1221
- if (fmt) {
1222
- result.format.push(fmt);
1223
- }
1224
- }
1225
-
1226
- return result;
1227
- };
1228
-
1229
- /**
1230
- * 处理字段和查询条件
1231
- * @param {Array} fields 字段列表
1232
- * @param {string} table 表名
1233
- * @returns {object} 处理结果
1234
- * @private
1235
- */
1236
- Drive.prototype._procFields = async function (fields, table) {
1237
- var query = {};
1238
- var update_obj = {};
1239
- var field = '';
1240
- var field_obj = '';
1241
- var query_default = {};
1242
- var format = [];
1243
- var orderby = '';
1244
- var uid = $.dict.user_id;
1245
- var keyword = '';
1246
- var query_default_user = this.isSet(table, this.query_default_table);
1247
-
1248
- for (var i = 0; i < fields.length; i++) {
1249
- var field_info = await this._procSingleField(
1250
- fields[i], query, update_obj, uid, query_default_user, orderby, keyword
1251
- );
1252
-
1253
- field += field_info.field;
1254
- field_obj += field_info.field_obj;
1255
- keyword = field_info.keyword;
1256
- orderby = field_info.orderby;
1257
- update_obj = field_info.update_obj;
1258
-
1259
- // 合并查询默认值
1260
- for (var key in field_info.query_default) {
1261
- query_default[key] = field_info.query_default[key];
1262
- }
1263
-
1264
- // 合并格式
1265
- format = format.concat(field_info.format);
1266
- }
1267
-
1268
- return {
1269
- query,
1270
- update: update_obj,
1271
- field: field.trim(','),
1272
- field_obj: field_obj.trim(','),
1273
- query_default,
1274
- format,
1275
- orderby,
1276
- keyword
1277
- };
1278
- };
1279
-
1280
- /**
1281
- * 处理关键字搜索
1282
- * @param {string} keyword 关键字
1283
- * @param {string} table 表名
1284
- * @returns {object} 处理后的查询对象
1285
- * @private
1286
- */
1287
- Drive.prototype._procKeyword = function (keyword, table) {
1288
- var new_keyword = keyword;
1289
- if (table.indexOf('user_') !== -1) {
1290
- new_keyword += ' || ' + keyword_default;
1291
- }
1292
-
1293
- var query = {};
1294
- if (new_keyword) {
1295
- query['keyword'] = '(' + new_keyword.replace(' || ', '') + ')';
1296
- }
1297
-
1298
- return query;
1299
- };
1300
-
1301
- /**
1302
- * 创建基础模型
1303
- * @param {object} cg 配置对象
1304
- * @param {object} processed 处理结果
1305
- * @param {object} keyword_query 关键字查询
1306
- * @returns {object} 基础模型
1307
- * @private
1308
- */
1309
- Drive.prototype._createSqlConfig = function (cg, processed, keyword_query) {
1310
- return {
1311
- name: cg.table,
1312
- title: cg.title,
1313
- table: cg.table,
1314
- key: cg.key,
1315
- orderby_default: '`' + cg.key + '` desc',
1316
- field_obj: processed.field_obj,
1317
- field_default: processed.field,
1318
- method: 'get get_obj avg sum count',
1319
- query: { ...processed.query, ...keyword_query },
1320
- query_default: processed.query_default,
1321
- update: processed.update,
1322
- format: processed.format
1323
- };
1324
- };
1325
-
1326
- /**
1327
- * 保存客户端配置
1328
- * @param {string} client 客户端配置保存路径
1329
- * @param {object} base_model 基础模型
1330
- * @param {string} orderby 排序
1331
- * @param {string} uid 用户ID
1332
- * @param {string} table 表名
1333
- * @param {boolean} cover 是否覆盖文件
1334
- * @private
1335
- */
1336
- Drive.prototype._saveClientConfig = function (client, base_model, cover, orderby, uid, table) {
1337
- var oj = { ...base_model };
1338
- if (orderby) {
1339
- oj.orderby_default = orderby;
1340
- }
1341
- if (oj.orderby_default && oj.query_default[uid] && table.indexOf('user_') !== -1) {
1342
- oj.filter = {
1343
- 'table': 'table',
1344
- 'page': 'page',
1345
- 'size': 'size',
1346
- 'method': 'method',
1347
- 'orderby': 'orderby',
1348
- 'field': 'field',
1349
- 'count_ret': 'count_ret'
1350
- };
1351
- oj.filter[uid] = uid;
1352
- }
1353
- this.saveFile(client + '/sql.json', oj, cover);
1354
- };
1355
-
1356
- /**
1357
- * 保存管理端配置
1358
- * @param {string} manage 管理端配置保存路径
1359
- * @param {object} base_model 基础模型
1360
- * @param {boolean} cover 是否覆盖文件
1361
- * @private
1362
- */
1363
- Drive.prototype._saveManageConfig = function (manage, base_model, cover) {
1364
- var m = { ...base_model };
1365
- delete m.method;
1366
- m.field_hide = [];
1367
- m.name += 2;
1368
- m.field_obj = (m.field_obj || '*').replace(',`time_create`', '').replace(',`time_update`', '')
1369
- .replace(',`create_time`', '').replace(',`update_time`', '');
1370
- delete m.query_default;
1371
- this.saveFile(manage + '/sql.json', m, cover);
1372
- };
1373
-
1374
- /**
1375
- * 新建sql配置文件
1376
- * @param {string} client 客户端配置保存路径
1377
- * @param {string} manage 管理端配置保存路径
1378
- * @param {boolean} cover 是否覆盖文件
1379
- */
1380
- Drive.prototype.newSql = async function (client, manage, cover) {
1381
- var cg = this.config;
1382
- var uid = $.dict.user_id;
1383
-
1384
- var processed = await this._procFields(cg.fields, cg.table);
1385
- var keyword_query = this._procKeyword(processed.keyword, cg.table);
1386
- var base_model = this._createSqlConfig(cg, processed, keyword_query);
1387
-
1388
- if (client) {
1389
- this._saveClientConfig(client, base_model, cover, processed.orderby, uid, cg.table);
1390
- }
1391
-
1392
- if (manage) {
1393
- this._saveManageConfig(manage, base_model, cover);
1394
- }
1395
- };
1396
-
1397
- /**
1398
- * 保存sql配置
1399
- * @param {string} file 文件名
1400
- * @param {object} model 配置模型
1401
- * @param {boolean} cover 是否覆盖文件
1402
- */
1403
- Drive.prototype.saveFile = function (file, model, cover) {
1404
- if (!file.hasFile()) {
1405
- file.saveText(JSON.stringify(model, null, 2));
1406
- } else if (cover) {
1407
- var jobj = file.loadJson();
1408
- for (var k in model) {
1409
- var val = model[k];
1410
- if (!val) {
1411
- model[k] = jobj[k];
1412
- }
1413
- }
1414
- $.push(jobj, model, true);
1415
- file.saveText(JSON.stringify(jobj, null, 2));
1416
- }
1417
- };
1418
-
1419
- /**
1420
- * 新建param配置文件
1421
- * @param {string} client 客户端配置保存路径
1422
- * @param {string} manage 管理端配置保存路径
1423
- * @param {boolean} cover 是否覆盖文件
1424
- */
1425
- Drive.prototype.newParam = async function (client, manage, cover) {
1426
- var cg = this.config;
1427
- var lt = cg.fields;
1428
- var cm = this._initParamConfig(cg);
1429
- var keyword = '';
1430
-
1431
- for (var i = 0; i < lt.length; i++) {
1432
- var o = lt[i];
1433
- var p = o.type;
1434
- var n = o.name;
1435
- var m = this._createParamModel(o, p, n);
1436
-
1437
- if (this._isStringType(p)) {
1438
- keyword = this._processStringField(cm, m, o, n, keyword);
1439
- } else if (this._isDateTimeType(p)) {
1440
- this._processDateTimeField(cm, m, o, n);
1441
- } else {
1442
- this._processNumberField(cm, m, o, n, cg);
1443
- }
1444
- }
1445
-
1446
- this._addKeywordConfig(cm, keyword, cg);
1447
- this._saveParamFiles(cm, client, manage, cover);
1448
- };
1449
-
1450
- /**
1451
- * 初始化参数配置对象
1452
- * @param {object} cg 配置对象
1453
- * @returns {object} 参数配置对象
1454
- * @private
1455
- */
1456
- Drive.prototype._initParamConfig = function (cg) {
1457
- return {
1458
- name: cg.table,
1459
- title: cg.title,
1460
- add: {
1461
- body: [],
1462
- body_required: []
1463
- },
1464
- del: {
1465
- query: [],
1466
- query_required: []
1467
- },
1468
- set: {
1469
- query: [],
1470
- query_required: [],
1471
- body: [],
1472
- body_required: [],
1473
- body_not: []
1474
- },
1475
- get: {
1476
- query: [],
1477
- query_required: []
1478
- },
1479
- get_obj: {
1480
- query_required: []
1481
- },
1482
- list: []
1483
- };
1484
- };
1485
-
1486
- /**
1487
- * 创建参数模型
1488
- * @param {object} field 字段对象
1489
- * @param {string} type 字段类型
1490
- * @param {string} name 字段名
1491
- * @returns {object} 参数模型对象
1492
- * @private
1493
- */
1494
- Drive.prototype._createParamModel = function (field, type, name) {
1495
- return {
1496
- name: name,
1497
- title: field.title,
1498
- description: field.description + (field.map ? '(' + field.map + ')' : ''),
1499
- key: field.pk,
1500
- type: '',
1501
- dataType: type
1502
- };
1503
- };
1504
-
1505
- /**
1506
- * 检查是否为字符串类型
1507
- * @param {string} type 字段类型
1508
- * @returns {boolean} 是否为字符串类型
1509
- * @private
1510
- */
1511
- Drive.prototype._isStringType = function (type) {
1512
- return type === 'varchar' || type === 'text' || type === 'longtext';
1513
- };
1514
-
1515
- /**
1516
- * 检查是否为日期时间类型
1517
- * @param {string} type 字段类型
1518
- * @returns {boolean} 是否为日期时间类型
1519
- * @private
1520
- */
1521
- Drive.prototype._isDateTimeType = function (type) {
1522
- return type === 'date' || type === 'time' || type === 'datetime' || type === 'timestamp';
1523
- };
1524
-
1525
- /**
1526
- * 处理字符串类型字段
1527
- * @param {object} cm 参数配置对象
1528
- * @param {object} m 模型对象
1529
- * @param {object} o 字段对象
1530
- * @param {string} n 字段名
1531
- * @param {string} keyword 关键词
1532
- * @returns {string} 更新后的关键词
1533
- * @private
1534
- */
1535
- Drive.prototype._processStringField = function (cm, m, o, n, keyword) {
1536
- var new_keyword = keyword;
1537
- m.type = 'string';
1538
- m.default = o.default;
1539
- m.string = {};
1540
-
1541
- this._addStringRange(m, o);
1542
- this._addStringFormat(m, n);
1543
-
1544
- if (o.not_null) {
1545
- m.string.not_empty = !!o.not_null;
1546
- cm.add.body_required.push(n);
1547
- } else {
1548
- cm.add.body.push(n);
1549
- }
1550
-
1551
- if (this.isSet(n, this.query_string)) {
1552
- cm.get.query.push(n);
1553
- cm.set.query.push(n);
1554
- new_keyword += '、' + o.title + '(' + n + ')';
1555
- }
1556
-
1557
- cm.set.body.push(n);
1558
- cm.list.push(m);
1559
- return new_keyword;
1560
- };
1561
-
1562
- /**
1563
- * 添加字符串范围验证
1564
- * @param {object} m 模型对象
1565
- * @param {object} o 字段对象
1566
- * @private
1567
- */
1568
- Drive.prototype._addStringRange = function (m, o) {
1569
- var range = (o.min_length && o.max_length) ? [o.min_length, o.max_length] : [];
1570
- if (range.length > 0) {
1571
- m.string.range = range;
1572
- } else if (o.min_length) {
1573
- m.string.min = o.min_length;
1574
- } else if (o.max_length) {
1575
- m.string.max = o.max_length;
1576
- }
1577
- };
1578
-
1579
- /**
1580
- * 检查字段名是否包含指定关键词
1581
- * @param {string} field_name 字段名
1582
- * @param {string} keyword 关键词
1583
- * @returns {boolean} 是否包含
1584
- * @private
1585
- */
1586
- Drive.prototype._hasKeyword = function (field_name, keyword) {
1587
- return field_name.has(keyword);
1588
- };
1589
-
1590
- /**
1591
- * 检查字段名是否等于指定值
1592
- * @param {string} field_name 字段名
1593
- * @param {Array} values 值数组
1594
- * @returns {boolean} 是否等于
1595
- * @private
1596
- */
1597
- Drive.prototype._isEqual = function (field_name, values) {
1598
- return values.includes(field_name);
1599
- };
1600
-
1601
- /**
1602
- * 添加字符串格式验证
1603
- * @param {object} m 模型对象
1604
- * @param {string} n 字段名
1605
- * @private
1606
- */
1607
- Drive.prototype._addStringFormat = function (m, n) {
1608
- if (this._hasKeyword(n, 'phone') || this._isEqual(n, ['tel'])) {
1609
- m.string.format = 'phone';
1610
- } else if (this._hasKeyword(n, 'url') || this._isEqual(n, ['src', 'source'])) {
1611
- m.string.format = 'url';
1612
- } else if (this._hasKeyword(n, 'date')) {
1613
- m.string.format = 'date';
1614
- } else if (this._isEqual(n, ['num', 'number', 'count'])) {
1615
- m.string.format = 'digits';
1616
- } else if (this._isEqual(n, ['money', 'coin'])) {
1617
- m.string.format = 'number';
1618
- } else if (this._hasKeyword(n, 'email')) {
1619
- m.string.format = 'email';
1620
- }
1621
- };
1622
-
1623
- /**
1624
- * 处理日期时间类型字段
1625
- * @param {object} cm 参数配置对象
1626
- * @param {object} m 模型对象
1627
- * @param {object} o 字段对象
1628
- * @param {string} n 字段名
1629
- * @private
1630
- */
1631
- Drive.prototype._processDateTimeField = function (cm, m, o, n) {
1632
- var format = (o.type === 'timestamp') ? 'datetime' : o.type;
1633
- m.type = 'string';
1634
- m.default = '';
1635
- m.string = {
1636
- not_empty: true,
1637
- format: format
1638
- };
1639
-
1640
- cm.list.push(m);
1641
- this._addDateTimeRange(cm, m, n);
1642
-
1643
- if (n.indexOf('create') !== -1 && n.indexOf('update') !== -1) {
1644
- cm.add.body.push(n);
1645
- cm.set.body.push(n);
1646
- }
1647
-
1648
- cm.get.query.push(n + '_min');
1649
- cm.get.query.push(n + '_max');
1650
- };
1651
-
1652
- /**
1653
- * 添加日期时间范围模型
1654
- * @param {object} cm 参数配置对象
1655
- * @param {object} m 模型对象
1656
- * @param {string} n 字段名
1657
- * @private
1658
- */
1659
- Drive.prototype._addDateTimeRange = function (cm, m, n) {
1660
- var m_min = { ...m };
1661
- m_min.name = n + '_min';
1662
- m_min.title += '——开始时间';
1663
- cm.list.push(m_min);
1664
-
1665
- var m_max = { ...m };
1666
- m_max.name = n + '_max';
1667
- m_max.title += '——结束时间';
1668
- cm.list.push(m_max);
1669
- };
1670
-
1671
- /**
1672
- * 处理数字类型字段
1673
- * @param {object} cm 参数配置对象
1674
- * @param {object} m 模型对象
1675
- * @param {object} o 字段对象
1676
- * @param {string} n 字段名
1677
- * @param {object} cg 配置对象
1678
- * @private
1679
- */
1680
- Drive.prototype._processNumberField = function (cm, m, o, n, cg) {
1681
- m.type = 'number';
1682
- m.default = Number(o.default) + '';
1683
- m.number = {};
1684
-
1685
- this._addNumberRange(m, o);
1686
-
1687
- if (o.name === cg.key) {
1688
- this._processPrimaryKey(cm, m, n);
1689
- } else {
1690
- this._processNonPrimaryKey(cm, m, n);
1691
- }
1692
- };
1693
-
1694
- /**
1695
- * 添加数字范围验证
1696
- * @param {object} m 模型对象
1697
- * @param {object} o 字段对象
1698
- * @private
1699
- */
1700
- Drive.prototype._addNumberRange = function (m, o) {
1701
- var range = (o.min && o.max) ? [o.min, o.max] : [];
1702
- if (range.length > 0) {
1703
- m.number.range = range;
1704
- } else if (o.min) {
1705
- m.number.min = o.min;
1706
- } else if (o.max) {
1707
- m.number.max = o.max;
1708
- }
1709
- };
1710
-
1711
- /**
1712
- * 处理主键字段
1713
- * @param {object} cm 参数配置对象
1714
- * @param {object} m 模型对象
1715
- * @param {string} n 字段名
1716
- * @private
1717
- */
1718
- Drive.prototype._processPrimaryKey = function (cm, m, n) {
1719
- cm.del.query_required.push(n);
1720
- cm.set.query.push(n);
1721
- cm.get.query.push(n);
1722
- cm.get_obj.query_required.push(n);
1723
- cm.list.push(m);
1724
- };
1725
-
1726
- /**
1727
- * 处理非主键字段
1728
- * @param {object} cm 参数配置对象
1729
- * @param {object} m 模型对象
1730
- * @param {string} n 字段名
1731
- * @private
1732
- */
1733
- Drive.prototype._processNonPrimaryKey = function (cm, m, n) {
1734
- cm.add.body.push(n);
1735
- cm.set.body.push(n);
1736
- cm.list.push(m);
1737
-
1738
- if (m.dataType !== 'tinyint') {
1739
- this._processNonTinyInt(cm, m, n);
1740
- } else {
1741
- cm.set.query.push(n);
1742
- cm.get.query.push(n);
1743
- }
1744
- };
1745
-
1746
- /**
1747
- * 处理非tinyint字段
1748
- * @param {object} cm 参数配置对象
1749
- * @param {object} m 模型对象
1750
- * @param {string} n 字段名
1751
- * @private
1752
- */
1753
- Drive.prototype._processNonTinyInt = function (cm, m, n) {
1754
- var ne = n;
1755
- if (!ne.endsWith('id')) {
1756
- this._addNumberRangeFields(cm, m, n);
1757
- } else {
1758
- cm.get.query.push(n);
1759
- if (this.isSet(n, this.query_number)) {
1760
- cm.set.query.push(n);
1761
- }
1762
- }
1763
- };
1764
-
1765
- /**
1766
- * 添加数字范围字段
1767
- * @param {object} cm 参数配置对象
1768
- * @param {object} m 模型对象
1769
- * @param {string} n 字段名
1770
- * @private
1771
- */
1772
- Drive.prototype._addNumberRangeFields = function (cm, m, n) {
1773
- cm.get.query.push(n + '_min');
1774
- cm.get.query.push(n + '_max');
1775
- cm.set.query.push(n + '_min');
1776
- cm.set.query.push(n + '_max');
1777
- cm.set.body.push(n + '_add');
1778
-
1779
- var m_min = { ...m };
1780
- m_min.name = n + '_min';
1781
- m_min.title += '——最小值';
1782
- cm.list.push(m_min);
1783
-
1784
- var m_max = { ...m };
1785
- m_max.name = n + '_max';
1786
- m_max.title += '——最大值';
1787
- cm.list.push(m_max);
1788
- };
1789
-
1790
- /**
1791
- * 添加关键词配置
1792
- * @param {object} cm 参数配置对象
1793
- * @param {string} keyword 关键词
1794
- * @param {object} cg 配置对象
1795
- * @private
1796
- */
1797
- Drive.prototype._addKeywordConfig = function (cm, keyword, cg) {
1798
- if (keyword) {
1799
- cm.get.query.push('keyword');
1800
- cm.set.query.push('keyword');
1801
- var new_keyword = keyword;
1802
- if (cg.table.indexOf('user_') !== -1) {
1803
- new_keyword += '、' + keyword_default_tip;
1804
- }
1805
- var m_k = {
1806
- name: 'keyword',
1807
- title: '关键词',
1808
- description: '用于搜索' + new_keyword.replace('、', ''),
1809
- type: 'string',
1810
- string: {}
1811
- };
1812
- cm.list.push(m_k);
1813
- }
1814
- };
1815
-
1816
- /**
1817
- * 保存参数配置文件
1818
- * @param {object} cm 参数配置对象
1819
- * @param {string} client 客户端配置保存路径
1820
- * @param {string} manage 管理端配置保存路径
1821
- * @param {boolean} cover 是否覆盖文件
1822
- * @private
1823
- */
1824
- Drive.prototype._saveParamFiles = function (cm, client, manage, cover) {
1825
- if (client) {
1826
- this.saveFile(client + '/param.json', cm, cover);
1827
- }
1828
- if (manage) {
1829
- var manage_cm = { ...cm };
1830
- delete manage_cm.method;
1831
- manage_cm.name += 2;
1832
- this.saveFile(manage + '/param.json', manage_cm, cover);
1833
- }
1834
- };
1835
-
1836
- /**
1837
- * 是否设置
1838
- * @param {string} name 名称
1839
- * @param {Array} arr 匹配的对象
1840
- * @returns {boolean} 是否设置
1841
- */
1842
- Drive.prototype.isSet = function (name, arr) {
1843
- if (!arr) {
1844
- return false;
1845
- }
1846
- var bl = false;
1847
- for (var i = 0; i < arr.length; i++) {
1848
- if (name.indexOf(arr[i]) !== -1) {
1849
- bl = true;
1850
- break;
1851
- }
1852
- }
1853
- return bl;
1854
- };
1855
-
1856
- /**
1857
- * 是否排除
1858
- * @param {string} name 名称
1859
- * @param {Array} arr 匹配的对象
1860
- * @param {string} type 数据类型
1861
- * @returns {boolean} 是否可以
1862
- */
1863
- Drive.prototype.isCan = function (name, arr, type) {
1864
- if (type === 'longtext') {
1865
- return false;
1866
- }
1867
- if (!arr) {
1868
- return true;
1869
- }
1870
- var bl = true;
1871
- for (var i = 0; i < arr.length; i++) {
1872
- if (name.indexOf(arr[i]) !== -1) {
1873
- bl = false;
1874
- break;
1875
- }
1876
- }
1877
- return bl;
1878
- };
1879
-
1880
-
1881
- /**
1882
- * 确定API路径
1883
- * @param {object} cg 配置对象
1884
- * @returns {string} API路径
1885
- * @private
1886
- */
1887
- Drive.prototype._deteApiPath = function (cg) {
1888
- var arr = cg.table.split('_');
1889
- var p = '/api/';
1890
- if (arr.length > 1) {
1891
- p += cg.table.replace('_', '/');
1892
- } else {
1893
- p += arr[0];
1894
- }
1895
- return p;
1896
- };
1897
-
1898
- /**
1899
- * 创建基础API配置
1900
- * @param {object} cg 配置对象
1901
- * @param {string} path API路径
1902
- * @returns {object} API配置
1903
- * @private
1904
- */
1905
- Drive.prototype._createApiConfig = function (cg, path) {
1906
- return {
1907
- 'name': cg.table,
1908
- 'title': cg.title,
1909
- 'description': cg.description,
1910
- 'path': path,
1911
- 'method': 'ALL',
1912
- 'cache': 0,
1913
- 'client_cache': false,
1914
- 'param_path': './param.json',
1915
- 'sql_path': './sql.json',
1916
- 'check_param': true
1917
- };
1918
- };
1919
-
1920
- /**
1921
- * 检查是否包含用户ID字段
1922
- * @param {Array} fields 字段列表
1923
- * @returns {boolean} 是否包含用户ID字段
1924
- * @private
1925
- */
1926
- Drive.prototype._hasUserIdField = function (fields) {
1927
- for (var i = 0, item; item = fields[i++];) {
1928
- var name = item.name;
1929
- if (name == $.dict.user_id || name === 'uid' || name === 'user_id' || name === 'userid') {
1930
- return true;
1931
- }
1932
- }
1933
- return false;
1934
- };
1935
-
1936
- /**
1937
- * 保存客户端API配置
1938
- * @param {string} client 客户端配置保存路径
1939
- * @param {object} base_config 基础配置
1940
- * @param {object} cg 配置对象
1941
- * @param {boolean} cover 是否覆盖文件
1942
- * @private
1943
- */
1944
- Drive.prototype._saveClientApiConfig = function (client, base_config, cg, cover) {
1945
- var o = { ...base_config };
1946
-
1947
- if (cg.title.indexOf('user') !== -1) {
1948
- if (this._hasUserIdField(cg.fields)) {
1949
- o.oauth = {
1950
- 'scope': true,
1951
- 'sign_in': true,
1952
- 'vip': 0,
1953
- 'user_group': []
1954
- };
1955
- }
1956
- }
1957
- o.method = 'GET';
1958
- this.saveFile(client + '/api.json', o, cover);
1959
- };
1960
-
1961
- /**
1962
- * 保存管理端API配置
1963
- * @param {string} manage 管理端配置保存路径
1964
- * @param {object} base_config 基础配置
1965
- * @param {boolean} cover 是否覆盖文件
1966
- * @private
1967
- */
1968
- Drive.prototype._saveManageApiConfig = function (manage, base_config, cover) {
1969
- var o = { ...base_config };
1970
- o.oauth = {
1971
- 'scope': true,
1972
- 'sign_in': true,
1973
- 'gm': 2,
1974
- 'user_admin': []
1975
- };
1976
- o.path = o.path.replace('/api/', '/apis/');
1977
- o.name += '_manage';
1978
- this.saveFile(manage + '/api.json', o, cover);
1979
- };
1980
-
1981
- /**
1982
- * 新建api配置文件
1983
- * @param {string} client 客户端配置保存路径
1984
- * @param {string} manage 管理端配置保存路径
1985
- * @param {boolean} cover 是否覆盖文件
1986
- */
1987
- Drive.prototype.newApi = async function (client, manage, cover) {
1988
- var cg = this.config;
1989
- var path = this._deteApiPath(cg);
1990
- var base_config = this._createApiConfig(cg, path);
1991
-
1992
- if (client) {
1993
- this._saveClientApiConfig(client, base_config, cg, cover);
1994
- }
1995
-
1996
- if (manage) {
1997
- this._saveManageApiConfig(manage, base_config, cover);
1998
- }
1999
- };
2000
-
2001
- /**
2002
- * 获取模型
2003
- * @param {string} type 模型类型
2004
- * @returns {object} 返回获取到的模型
2005
- */
2006
- Drive.prototype.getModel = function (type) {
2007
- let model = { ...this.config };
2008
- let dir = this.getDir();
2009
- let l = $.slash;
2010
- let app_name = dir.between('app' + l, l);
2011
- let plugin_name = dir.between('plugin' + l, l);
2012
- let name = dir.basename();
2013
- model.app = app_name;
2014
- model.plugin = plugin_name;
2015
- model.name = model.name || name;
2016
- return model;
2017
- };
2018
-
2019
- module.exports = Drive;
1
+ const Item = require('mm_machine').Drive;
2
+ const fs = require('fs');
3
+
4
+ if (!$.dict.user_id) {
5
+ $.dict.user_id = 'user_id';
6
+ }
7
+
8
+ var keyword_default =
9
+ "`user_id` in (SELECT `user_id` FROM `user_account` WHERE `nickname` LIKE '%{0}%' OR `phone` LIKE '%{0}%' OR `wallet_address` LIKE '%{0}%')";
10
+
11
+ var keyword_default_tip = '昵称、手机号、钱包地址';
12
+
13
+ /**
14
+ * 对LIKE查询中的关键词进行转义, 防止通配符注入和性能攻击
15
+ * @param {string} keyword 原始关键词
16
+ * @returns {string} 转义后的安全关键词
17
+ */
18
+ var escapeLikeKeyword = function (keyword) {
19
+ if (typeof keyword !== 'string') {
20
+ return '';
21
+ }
22
+ return keyword
23
+ .replace(/\\/g, '\\\\')
24
+ .replace(/%/g, '\\%')
25
+ .replace(/_/g, '\\_');
26
+ };
27
+
28
+ /**
29
+ * db数据库开发驱动类
30
+ * @augments {Item}
31
+ * @class
32
+ */
33
+ class Drive extends Item {
34
+ static config = {
35
+ // 库表名称(唯一标识)
36
+ name: '',
37
+ // 标题(中文名)
38
+ title: '',
39
+ // 表名
40
+ table: '',
41
+ // 主键,用于实体模型
42
+ key: '',
43
+ // 字段
44
+ fields: [/* */],
45
+ index: [],
46
+ // 主程序文件 - 默认为空
47
+ main: '',
48
+ };
49
+
50
+ /**
51
+ * 构造函数
52
+ * @param {object} config 配置参数
53
+ * @param {object} parent 父对象
54
+ * @class
55
+ */
56
+ constructor(config, parent) {
57
+ super({ ...Drive.config, ...config }, parent);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * 重置配置参数
63
+ */
64
+ Drive.prototype._preset = function () {
65
+ this.query_string = [
66
+ 'name',
67
+ 'title',
68
+ 'keywords',
69
+ 'tag',
70
+ 'description',
71
+ 'content',
72
+ ];
73
+
74
+ // 是否设置数值类型为可查询
75
+ this.query_number = ['state', 'uin'];
76
+
77
+ // 是否设置数值类型为关键词可查
78
+ this.query_keyword = ['name', 'title', 'keywords', 'tag', 'description'];
79
+
80
+ // 是否设置以下字段为get查列表SQL时不可见
81
+ this.get_not = ['password', 'salt', 'content'];
82
+
83
+ // 是否设置以下字段为getObj查对象SQL时不可见
84
+ this.get_obj_not = ['password', 'salt', 'display'];
85
+
86
+ // 是否设置默认该表仅用户可访问
87
+ this.query_default_table = ['user'];
88
+ };
89
+
90
+ /**
91
+ * 解析字段类型
92
+ * @param {string} type 字段类型
93
+ * @returns {object} 解析结果
94
+ */
95
+ Drive.prototype._parseFieldType = function (type) {
96
+ var tp = type.left('(', true);
97
+ var len = type.between('(', ')');
98
+ var max = 0;
99
+ var decimal = 0;
100
+ if (len) {
101
+ max = Number(len.left(',', true));
102
+ var d = len.right(',');
103
+ if (d) {
104
+ decimal = Number(d);
105
+ }
106
+ }
107
+ return { tp, max, decimal };
108
+ };
109
+
110
+ /**
111
+ * 解析字段备注
112
+ * @param {string} note 字段备注
113
+ * @param {string} tp 字段类型
114
+ * @param {number} max_length 最大长度
115
+ * @returns {object} 解析结果
116
+ */
117
+ Drive.prototype._parseFieldNote = function (note, tp, max_length) {
118
+ var proc_note = note.replace(':', ':').replace('(', '(').replace(')', ')');
119
+ var title = proc_note.left(':', true).trim();
120
+ var desc = proc_note.right(':');
121
+ var description = '';
122
+ var map = '';
123
+ var min = 0;
124
+ var max = 0;
125
+ var local_max_length = max_length;
126
+
127
+ if (desc) {
128
+ var range = desc.between('[', ']');
129
+ if (range) {
130
+ var min_str = range.left(',', true);
131
+ if (min_str) {
132
+ min = Number(min_str);
133
+ }
134
+ var max_str = range.right(',');
135
+ if (max_str) {
136
+ var n = Number(max_str);
137
+ if (tp === 'varchar') {
138
+ if (n < local_max_length) {
139
+ local_max_length = n;
140
+ }
141
+ } else {
142
+ max = n;
143
+ }
144
+ }
145
+ }
146
+ map = desc.between('(', ')');
147
+ description = desc.right(']', true).replace(`(${map})`, '').trim();
148
+ }
149
+ let min_length = 0;
150
+ if (min > 0) {
151
+ min_length = min;
152
+ }
153
+ return {
154
+ title,
155
+ description,
156
+ map,
157
+ min,
158
+ max,
159
+ min_length,
160
+ max_length: local_max_length,
161
+ };
162
+ };
163
+
164
+ /**
165
+ * 计算字段最大值
166
+ * @param {string} tp 字段类型
167
+ * @param {number} max 当前最大值
168
+ * @param {number} max_length 最大长度
169
+ * @returns {number} 计算后的最大值
170
+ */
171
+ Drive.prototype._calcMaxValue = function (tp, max, max_length) {
172
+ var local_max = max;
173
+ if (local_max === 0) {
174
+ switch (tp) {
175
+ case 'tinyint':
176
+ local_max = 1;
177
+ break;
178
+ case 'smallint':
179
+ local_max = 32767;
180
+ break;
181
+ case 'mediumint':
182
+ local_max = 8388607;
183
+ break;
184
+ case 'int':
185
+ local_max = 2147483647;
186
+ break;
187
+ case 'bigint':
188
+ local_max = 0;
189
+ break;
190
+ default:
191
+ break;
192
+ }
193
+ if (max_length) {
194
+ var num = Math.pow(10, max_length) - 1;
195
+ if (local_max > num) {
196
+ local_max = num;
197
+ }
198
+ }
199
+ }
200
+ return local_max;
201
+ };
202
+
203
+ /**
204
+ * 处理字段默认值
205
+ * @param {string} tp 字段类型
206
+ * @param {boolean} not_null 是否非空
207
+ * @param {*} default_val 默认值
208
+ * @returns {*} 处理后的默认值
209
+ */
210
+ Drive.prototype._handleDefaultValue = function (tp, not_null, default_val) {
211
+ if (not_null && !default_val) {
212
+ if (tp === 'varchar' || tp === 'text') {
213
+ return '';
214
+ } else if (tp === 'datetime' || tp === 'timestamp') {
215
+ return 'CURRENT_TIMESTAMP';
216
+ } else {
217
+ return '0';
218
+ }
219
+ }
220
+ return default_val;
221
+ };
222
+
223
+ /**
224
+ * 创建字段模型对象
225
+ * @param {string} name 字段名
226
+ * @param {string} title 字段标题
227
+ * @param {string} description 字段描述
228
+ * @param {string} tp 字段类型
229
+ * @param {boolean} pk 是否主键
230
+ * @param {boolean} auto 是否自动
231
+ * @param {boolean} not_null 是否非空
232
+ * @param {number} min_length 最小长度
233
+ * @param {number} max_length 最大长度
234
+ * @param {number} min 最小值
235
+ * @param {number} max 最大值
236
+ * @param {number} decimal 小数位
237
+ * @param {*} default_val 默认值
238
+ * @param {object} map 转换编排
239
+ * @returns {object} 字段模型对象
240
+ * @private
241
+ */
242
+ Drive.prototype._createFieldModel = function (
243
+ name,
244
+ title,
245
+ description,
246
+ tp,
247
+ pk,
248
+ auto,
249
+ not_null,
250
+ min_length,
251
+ max_length,
252
+ min,
253
+ max,
254
+ decimal,
255
+ default_val,
256
+ map,
257
+ ) {
258
+ return {
259
+ // 字段名
260
+ name: name,
261
+ // 字段标题
262
+ title: title,
263
+ // 字段描述
264
+ description: description,
265
+ // 字段类型 smallint短整数、mediumint中长整数、int整数、float浮点数、double双精度、tinyint二进制(0和1的布尔)、text文本、varchar字符串、datetime日期时间、date日期、time时间、timestamp时间戳
266
+ type: tp,
267
+ // 是否主键
268
+ key: pk,
269
+ // 自动
270
+ auto: auto,
271
+ // 是否含符号
272
+ symbol: tp === 'float' || tp === 'decimal',
273
+ // 是否填充零,用于数字类型
274
+ fill_zero: false,
275
+ // 非空
276
+ not_null: not_null,
277
+ // 最小长度
278
+ min_length: min_length,
279
+ // 最大长度
280
+ max_length: max_length,
281
+ // 最小值
282
+ min: min,
283
+ // 最大值
284
+ max: max,
285
+ // 小数位
286
+ decimal: decimal,
287
+ // 默认值
288
+ default: default_val,
289
+ // 转换编排
290
+ map: map,
291
+ };
292
+ };
293
+
294
+ /**
295
+ * 创建字段模型
296
+ * @param {object} fields 字段对象
297
+ * @param {string} fields.name 字段名称
298
+ * @param {object} fields.default 默认值
299
+ * @param {object} fields.notnull 是否非空
300
+ * @param {object} fields.type 字段类型
301
+ * @param {object} fields.pk 是否主键
302
+ * @param {object} fields.auto 是否自动增长
303
+ * @param {object} fields.note 字段备注
304
+ * @returns {object} 返回字段模型
305
+ */
306
+ Drive.prototype.model = function (fields) {
307
+ var { name, notnull, type, pk, note, auto } = fields;
308
+
309
+ var { tp, max: max_length, decimal } = this._parseFieldType(type);
310
+ var {
311
+ title,
312
+ description,
313
+ map,
314
+ min,
315
+ max: parsedMax,
316
+ min_length,
317
+ max_length: parsedMaxLength,
318
+ } = this._parseFieldNote(note, tp, max_length);
319
+
320
+ var max = this._calcMaxValue(tp, parsedMax, parsedMaxLength);
321
+
322
+ if (name.indexOf('_id') !== -1 || name == 'id') {
323
+ min = 0;
324
+ }
325
+
326
+ var not_null = notnull | pk | (tp !== 'varchar' && tp !== 'text');
327
+
328
+ var config = this.config;
329
+ if (pk && !config.key) {
330
+ config.key = name;
331
+ }
332
+
333
+ var default_value = this._handleDefaultValue(tp, not_null, fields.default);
334
+
335
+ return this._createFieldModel(
336
+ name,
337
+ title,
338
+ description,
339
+ tp,
340
+ pk,
341
+ auto,
342
+ not_null,
343
+ min_length,
344
+ parsedMaxLength,
345
+ min,
346
+ max,
347
+ decimal,
348
+ default_value,
349
+ map,
350
+ );
351
+ };
352
+
353
+ /**
354
+ * 创建索引字段模型
355
+ * @param {object} o 索引对象
356
+ * @property {string} o.Key_name 索引名称
357
+ * @property {string} o.Non_unique 是否唯一索引
358
+ * @property {string} o.Column_name 索引字段
359
+ * @property {string} o.Manager_comment 索引备注
360
+ * @returns {object} 返回索引模型
361
+ */
362
+ Drive.prototype.newIndexModel = function (o) {
363
+ return {
364
+ name: o.Key_name,
365
+ type: o.Non_unique ? 'unique' : 'index',
366
+ fields: o.Column_name.replace(/`/g, '').split(','),
367
+ comment: o.Manager_comment,
368
+ };
369
+ };
370
+
371
+ /**
372
+ * 从数据库更新索引配置
373
+ * @param {object} db 数据库管理器
374
+ * @returns {Array} 索引列表
375
+ */
376
+ Drive.prototype._updateFile = async function (db) {
377
+ var sql = 'SHOW INDEX FROM `' + this.config.table + '`';
378
+ var rows = await db.run(sql);
379
+ var dict = {};
380
+ for (var i = 0; i < rows.length; i++) {
381
+ var o = rows[i];
382
+ if (o.Key_name !== 'PRIMARY') {
383
+ if (!dict[o.Key_name]) {
384
+ dict[o.Key_name] = {
385
+ columnName: [],
386
+ };
387
+ }
388
+ dict[o.Key_name].Column_name.push(o.Column_name);
389
+ }
390
+ }
391
+ var list = [];
392
+ for (var k in dict) {
393
+ var m = this.newIndexModel(dict[k]);
394
+ list.push(m);
395
+ }
396
+
397
+ return list;
398
+ };
399
+
400
+ /**
401
+ * 从数据库更新配置
402
+ * @param {object} db 数据库管理器
403
+ * @param {boolean} cover 是否覆盖文件
404
+ */
405
+ Drive.prototype.updateFile = async function (db, cover) {
406
+ var cg = this.config;
407
+ var list = [];
408
+
409
+ // 查询表注释并修改
410
+ var db_name = db.database().replace(/[^a-zA-Z0-9_]/g, '');
411
+ var sql =
412
+ "SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" +
413
+ db_name +
414
+ "' && TABLE_NAME = '" +
415
+ cg.table +
416
+ "';";
417
+
418
+ var lt = await db.run(sql);
419
+ if (lt && lt.length > 0) {
420
+ var commit = lt[0].TABLE_COMMENT;
421
+ var arr = commit.replace(':', ':').split(':');
422
+ cg.title = arr[0];
423
+ if (arr.length > 1) {
424
+ cg.description = arr[1];
425
+ }
426
+ }
427
+
428
+ // 设置表名
429
+ db.table = cg.table + '';
430
+
431
+ // 获取所有字段
432
+ var fields = await db.fields();
433
+ for (var i = 0; i < fields.length; i++) {
434
+ var field = this.model(fields[i]);
435
+ list.push(field);
436
+ }
437
+ cg.fields = list;
438
+ cg.index = await this._updateFile(db);
439
+ if (!cg.name) {
440
+ cg.name = cg.table;
441
+ }
442
+ await this.updateApp(cover);
443
+ };
444
+
445
+ /**
446
+ * 更新索引
447
+ * @param {object} db 数据库管理器
448
+ */
449
+ Drive.prototype._updateDb = async function (db) {
450
+ var cg = this.config;
451
+ let index = cg.index || [];
452
+ // 更新索引
453
+ if (index) {
454
+ var len = index.length;
455
+ for (var i = 0; i < len; i++) {
456
+ var o = index[i];
457
+ var sql_start = '';
458
+ if (o.type === 'unique') {
459
+ sql_start = 'CREATE UNIQUE INDEX `';
460
+ } else {
461
+ sql_start = 'CREATE INDEX `';
462
+ }
463
+ await db.exec(
464
+ sql_start + o.name + '` ON `' + cg.table + '` (' + o.fields + ');',
465
+ );
466
+ }
467
+ }
468
+ };
469
+
470
+ /**
471
+ * 初始化数据表
472
+ * @param {object} db 数据库管理器
473
+ * @param {object} cg 配置对象
474
+ * @param {Array} list 字段列表
475
+ * @returns {Array} 字段列表
476
+ */
477
+ Drive.prototype._initTable = async function (db, cg, list) {
478
+ var fields = await db.fields();
479
+ if (fields.length === 0) {
480
+ var k = cg.key;
481
+ var len = list.length;
482
+ for (var i = 0; i < len; i++) {
483
+ var o = list[i];
484
+ if (k === o.name) {
485
+ await db.addTable(
486
+ cg.table,
487
+ o.name,
488
+ o.type,
489
+ o.auto,
490
+ cg.title + ':' + cg.description,
491
+ );
492
+ fields.push({
493
+ name: o.name,
494
+ });
495
+ break;
496
+ }
497
+ }
498
+ } else {
499
+ var commit = cg.title + ':' + cg.description;
500
+ var sql = "alter table `{0}` comment '{1}';"
501
+ .replace('{0}', cg.table)
502
+ .replace('{1}', commit);
503
+ await db.exec(sql);
504
+ }
505
+ return fields;
506
+ };
507
+
508
+ /**
509
+ * 删除配置中没有的字段
510
+ * @param {object} db 数据库管理器
511
+ * @param {Array} fields 数据库字段列表
512
+ * @param {Array} list 配置字段列表
513
+ */
514
+ Drive.prototype._deleteUnusedFields = async function (db, fields, list) {
515
+ for (var i = 0; i < fields.length; i++) {
516
+ var o = fields[i];
517
+ var obj = list.getObj({
518
+ name: o.name,
519
+ });
520
+
521
+ if (!obj) {
522
+ await db.fieldDel(o.name);
523
+ }
524
+ }
525
+ };
526
+
527
+ /**
528
+ * 生成字段SQL
529
+ * @param {object} o 字段对象
530
+ * @returns {string} 字段SQL
531
+ */
532
+ Drive.prototype._generateFieldSql = function (o) {
533
+ var type = this._getTypeSql(o);
534
+ var notnull = this._getNotNullSql(o);
535
+ var value = this._getValueSql(o);
536
+ var note = this._getNoteSql(o);
537
+ return this._buildFieldSql(o.name, type, notnull, value, note);
538
+ };
539
+
540
+ /**
541
+ * 获取类型SQL
542
+ * @param {object} o 字段对象
543
+ * @returns {string} 类型SQL
544
+ * @private
545
+ */
546
+ Drive.prototype._getTypeSql = function (o) {
547
+ var type = o.type;
548
+ var max_len = o.max_length || 0;
549
+
550
+ if (max_len > 0) {
551
+ // decimal仅对浮点类型有效(int/smallint/bigint等使用decimal=0时不追加)
552
+ var is_float_type =
553
+ o.type.indexOf('float') !== -1 ||
554
+ o.type.indexOf('double') !== -1 ||
555
+ o.type.indexOf('decimal') !== -1;
556
+ if (is_float_type && o.decimal !== undefined && o.decimal !== null) {
557
+ type += '(' + max_len + ',' + o.decimal + ')';
558
+ } else {
559
+ type += '(' + max_len + ')';
560
+ }
561
+ }
562
+
563
+ // 类型未包含unsigned且为非字符/日期类型时追加UNSIGNED
564
+ if (
565
+ !o.symbol &&
566
+ type.indexOf('unsigned') === -1 &&
567
+ o.type !== 'varchar' &&
568
+ o.type !== 'longtext' &&
569
+ o.type !== 'text' &&
570
+ o.type !== 'date' &&
571
+ o.type !== 'time' &&
572
+ o.type !== 'datetime' &&
573
+ o.type !== 'timestamp'
574
+ ) {
575
+ type += ' UNSIGNED';
576
+ }
577
+
578
+ return type;
579
+ };
580
+
581
+ /**
582
+ * 获取NOT NULL SQL
583
+ * @param {object} o 字段对象
584
+ * @returns {string} NOT NULL SQL
585
+ * @private
586
+ */
587
+ Drive.prototype._getNotNullSql = function (o) {
588
+ var notnull = '';
589
+
590
+ if (this._isNotNullRequired(o)) {
591
+ notnull = 'NOT NULL';
592
+ }
593
+
594
+ if (this._isAutoInc(o)) {
595
+ notnull += ' AUTO_INCREMENT';
596
+ }
597
+
598
+ return notnull;
599
+ };
600
+
601
+ /**
602
+ * 检查是否需要NOT NULL
603
+ * @param {object} o 字段对象
604
+ * @returns {boolean} 是否需要NOT NULL
605
+ * @private
606
+ */
607
+ Drive.prototype._isNotNullRequired = function (o) {
608
+ return (
609
+ o.not_null || this._isDateTimeType(o.type) || this._isNumericType(o.type)
610
+ );
611
+ };
612
+
613
+ /**
614
+ * 检查是否为日期时间类型
615
+ * @param {string} type 类型
616
+ * @returns {boolean} 是否为日期时间类型
617
+ * @private
618
+ */
619
+ Drive.prototype._isDateTimeType = function (type) {
620
+ return (
621
+ type === 'date' ||
622
+ type === 'time' ||
623
+ type === 'datetime' ||
624
+ type === 'timestamp'
625
+ );
626
+ };
627
+
628
+ /**
629
+ * 检查是否为数字类型
630
+ * @param {string} type 类型
631
+ * @returns {boolean} 是否为数字类型
632
+ * @private
633
+ */
634
+ Drive.prototype._isNumericType = function (type) {
635
+ return (
636
+ type !== 'varchar' &&
637
+ type !== 'longtext' &&
638
+ type !== 'text' &&
639
+ !this._isDateTimeType(type)
640
+ );
641
+ };
642
+
643
+ /**
644
+ * 检查是否需要AUTO_INCREMENT
645
+ * @param {object} o 字段对象
646
+ * @returns {boolean} 是否需要AUTO_INCREMENT
647
+ * @private
648
+ */
649
+ Drive.prototype._isAutoInc = function (o) {
650
+ return !!o.auto && !this._isDateTimeType(o.type);
651
+ };
652
+
653
+ /**
654
+ * 获取默认值SQL
655
+ * @param {object} o 字段对象
656
+ * @returns {string} 默认值SQL
657
+ * @private
658
+ */
659
+ Drive.prototype._getValueSql = function (o) {
660
+ if (this._isAutoTime(o)) {
661
+ return 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
662
+ }
663
+
664
+ if (this._isAutoInc(o)) {
665
+ return '';
666
+ }
667
+
668
+ if (o.default === null) {
669
+ return 'DEFAULT NULL';
670
+ }
671
+
672
+ if (o.default !== undefined) {
673
+ return this._getDefaultValueSql(o);
674
+ }
675
+
676
+ return this._getDefaultEmptySql(o);
677
+ };
678
+
679
+ /**
680
+ * 检查是否为自动时间
681
+ * @param {object} o 字段对象
682
+ * @returns {boolean} 是否为自动时间
683
+ * @private
684
+ */
685
+ Drive.prototype._isAutoTime = function (o) {
686
+ return !!o.auto && this._isDateTimeType(o.type);
687
+ };
688
+
689
+ /**
690
+ * 获取默认值SQL
691
+ * @param {object} o 字段对象
692
+ * @returns {string} 默认值SQL
693
+ * @private
694
+ */
695
+ Drive.prototype._getDefaultValueSql = function (o) {
696
+ if (this._isCurrentDefault(o)) {
697
+ return 'DEFAULT ' + o.default;
698
+ } else if (this._isStringType(o.type)) {
699
+ return "DEFAULT '" + o.default + "'";
700
+ } else {
701
+ return 'DEFAULT ' + o.default;
702
+ }
703
+ };
704
+
705
+ /**
706
+ * 检查是否为当前时间默认值
707
+ * @param {object} o 字段对象
708
+ * @returns {boolean} 是否为当前时间默认值
709
+ * @private
710
+ */
711
+ Drive.prototype._isCurrentDefault = function (o) {
712
+ return (
713
+ this._isDateTimeType(o.type) &&
714
+ o.default &&
715
+ o.default.indexOf('CURR') !== -1
716
+ );
717
+ };
718
+
719
+ /**
720
+ * 检查是否为字符串类型
721
+ * @param {string} type 类型
722
+ * @returns {boolean} 是否为字符串类型
723
+ * @private
724
+ */
725
+ Drive.prototype._isStringType = function (type) {
726
+ return (
727
+ type === 'varchar' ||
728
+ type === 'longtext' ||
729
+ type === 'text' ||
730
+ type === 'date' ||
731
+ type === 'time' ||
732
+ type === 'datetime' ||
733
+ type === 'timestamp'
734
+ );
735
+ };
736
+
737
+ /**
738
+ * 获取空默认值SQL
739
+ * @param {object} o 字段对象
740
+ * @returns {string} 空默认值SQL
741
+ * @private
742
+ */
743
+ Drive.prototype._getDefaultEmptySql = function (o) {
744
+ if (this._isStringType(o.type)) {
745
+ return "DEFAULT ''";
746
+ } else {
747
+ return 'DEFAULT 0';
748
+ }
749
+ };
750
+
751
+ /**
752
+ * 获取注释SQL
753
+ * @param {object} o 字段对象
754
+ * @returns {string} 注释SQL
755
+ * @private
756
+ */
757
+ Drive.prototype._getNoteSql = function (o) {
758
+ var note = (o.title || '') + ':';
759
+
760
+ if (o.type === 'varchar' || o.type === 'text' || o.type === 'longtext') {
761
+ if (o.max_length) {
762
+ note += '[' + (o.min_length || '0') + ',' + o.max_length + ']';
763
+ } else if (o.min_length) {
764
+ note += '[' + o.min_length + ']';
765
+ }
766
+ } else {
767
+ if (o.max) {
768
+ note += '[' + (o.min || '0') + ',' + o.max + ']';
769
+ } else if (o.min) {
770
+ note += '[' + o.min + ']';
771
+ }
772
+ }
773
+
774
+ note += o.description || '';
775
+ if (o.map) {
776
+ note += '(' + o.map + ')';
777
+ }
778
+
779
+ return note;
780
+ };
781
+
782
+ /**
783
+ * 构建字段SQL
784
+ * @param {string} name 字段名
785
+ * @param {string} type 类型
786
+ * @param {string} notnull NOT NULL
787
+ * @param {string} value 默认值
788
+ * @param {string} note 注释
789
+ * @returns {string} 字段SQL
790
+ * @private
791
+ */
792
+ Drive.prototype._buildFieldSql = function (name, type, notnull, value, note) {
793
+ var sql = "`{1}` {2} {3} {4} COMMENT '{5}'";
794
+ return sql
795
+ .replace('{1}', name)
796
+ .replace('{2}', type)
797
+ .replace('{3}', notnull)
798
+ .replace('{4}', value)
799
+ .replace('{5}', note);
800
+ };
801
+
802
+ /**
803
+ * 添加或修改字段
804
+ * @param {object} db 数据库管理器
805
+ * @param {string} table 表名
806
+ * @param {object} o 字段对象
807
+ * @param {Array} fields 数据库字段列表
808
+ */
809
+ Drive.prototype._addOrUpdateField = async function (db, table, o, fields) {
810
+ var arr = fields.filter((f) => {
811
+ return f.name === o.name;
812
+ });
813
+ var sql = this._generateFieldSql(o);
814
+
815
+ if (arr.length === 0) {
816
+ // 如果没有则添加
817
+ await db.exec('alter table `{0}` add '.replace('{0}', table) + sql);
818
+ } else {
819
+ // 如果有则修改
820
+ await db.exec(
821
+ 'alter table `{0}` change `{1}` '
822
+ .replace('{0}', table)
823
+ .replace('{1}', o.name) + sql,
824
+ );
825
+ }
826
+ };
827
+
828
+ /**
829
+ * 通过配置更新数据库
830
+ * @param {object} db 数据库管理器
831
+ * @returns {string} 更新成功返回空,否则返回错误提示
832
+ */
833
+ Drive.prototype.updateDb = async function (db) {
834
+ var cg = this.config;
835
+ var table = cg.table + '';
836
+ var list = cg.fields;
837
+ db.table = table;
838
+ var fields = await this._initTable(db, cg, list);
839
+
840
+ if (fields.length > 0) {
841
+ // 删除配置中没有的字段
842
+ await this._deleteUnusedFields(db, fields, list);
843
+
844
+ // 添加或修改配置
845
+ var len = list.length;
846
+ for (var i = 0; i < len; i++) {
847
+ var o = list[i];
848
+ await this._addOrUpdateField(db, table, o, fields);
849
+ }
850
+ } else {
851
+ return '数据表更新失败';
852
+ }
853
+ };
854
+
855
+ /**
856
+ * 确定目录结构
857
+ * @param {object} cg 配置对象
858
+ * @returns {object} 目录信息
859
+ * @private
860
+ */
861
+ Drive.prototype._deteDir = function (cg) {
862
+ var arr = cg.table.split('_');
863
+ var scope = arr[0];
864
+ var p = './app/'.fullname();
865
+ var dir = ('./' + scope).fullname(p);
866
+ var dir_api = './plugin/server'.fullname(dir);
867
+ var db_dir = './db'.fullname(dir_api);
868
+ var config_file = (
869
+ './' +
870
+ cg.table.replace(scope + '_', '') +
871
+ '.db.json'
872
+ ).fullname(db_dir);
873
+
874
+ return {
875
+ scope,
876
+ dir,
877
+ dir_api,
878
+ db_dir,
879
+ config_file,
880
+ };
881
+ };
882
+
883
+ /**
884
+ * 确保目录存在
885
+ * @param {string} dir 目录路径
886
+ * @private
887
+ */
888
+ Drive.prototype._ensureDir = function (dir) {
889
+ if (!fs.existsSync(dir)) {
890
+ fs.mkdirSync(dir);
891
+ }
892
+ };
893
+
894
+ /**
895
+ * 处理配置文件
896
+ * @param {string} f 文件路径
897
+ * @param {boolean} cover 是否覆盖文件
898
+ * @private
899
+ */
900
+ Drive.prototype._handleConfigFile = function (f, cover) {
901
+ if (!f.hasFile()) {
902
+ this.save();
903
+ } else if (cover) {
904
+ var jobj = f.loadJson();
905
+ var o = { ...this.config };
906
+ if (!o.title) {
907
+ delete o.title;
908
+ }
909
+ if (!o.description) {
910
+ delete o.description;
911
+ }
912
+ $.push(jobj, o, true);
913
+ f.saveText(JSON.stringify(jobj, null, 2));
914
+ }
915
+ };
916
+
917
+ /**
918
+ * 更新应用,根据表生成目录结构和文件
919
+ * @param {boolean} cover 是否覆盖文件
920
+ */
921
+ Drive.prototype.updateApp = async function (cover) {
922
+ var cg = this.config;
923
+ var f;
924
+ var dir_api;
925
+
926
+ if (!this.config_file) {
927
+ var dirs = this._deteDir(cg);
928
+ this._ensureDir(dirs.dir);
929
+ this._ensureDir(dirs.dir + '/plugin');
930
+ this._ensureDir(dirs.dir_api);
931
+ this._ensureDir(dirs.db_dir);
932
+ this._dir = dirs.db_dir;
933
+ this.config_file = dirs.config_file;
934
+ f = dirs.config_file;
935
+ dir_api = dirs.dir_api;
936
+ } else {
937
+ if (this.config_file.endsWith('.db.json')) {
938
+ f = this.config_file;
939
+ } else {
940
+ f = this.config_file + '.db.json';
941
+ }
942
+ }
943
+
944
+ if (f) {
945
+ f.addDir();
946
+ if (!dir_api) {
947
+ dir_api = f.dirname().dirname();
948
+ }
949
+ // 处理db配置文件,生成xxx.db.json文件
950
+ this._handleConfigFile(f, cover);
951
+ // 更新API及相关配置文件
952
+ this.updateApi(dir_api, cover);
953
+ }
954
+ };
955
+
956
+ /**
957
+ * 确保API目录存在
958
+ * @param {string} dir 目录路径
959
+ * @private
960
+ */
961
+ Drive.prototype._ensureApiDir = function (dir) {
962
+ if (!fs.existsSync(dir)) {
963
+ fs.mkdirSync(dir);
964
+ }
965
+ };
966
+
967
+ /**
968
+ * 确定API目录路径
969
+ * @param {string} dir 基础目录
970
+ * @param {string} app 应用名称
971
+ * @param {string} type 类型 (client 或 manage)
972
+ * @param {string} name 目录名称
973
+ * @returns {string} 完整目录路径
974
+ * @private
975
+ */
976
+ Drive.prototype._getApiDir = function (dir, app, type, name) {
977
+ var l = $.slash;
978
+ let event_api = $.admin.event('api');
979
+ var o = event_api.getMod(app + '_' + type);
980
+ var api_dir;
981
+
982
+ if (o) {
983
+ api_dir = dir + l + 'api_' + app + '_' + type;
984
+ } else {
985
+ api_dir = dir + l + 'api_' + type;
986
+ }
987
+
988
+ this._ensureApiDir(api_dir);
989
+ api_dir += '/' + name;
990
+ this._ensureApiDir(api_dir);
991
+
992
+ return api_dir;
993
+ };
994
+
995
+ /**
996
+ * 更新API及相关配置文件
997
+ * @param {string} dir API存放目录
998
+ * @param {boolean} cover 是否覆盖文件
999
+ */
1000
+ Drive.prototype.updateApi = async function (dir, cover) {
1001
+ var cg = this.config;
1002
+ var arr = cg.table.split('_');
1003
+
1004
+ var name = 'root';
1005
+ if (arr.length > 1) {
1006
+ name = cg.table.replace(arr[0], '').trim('_');
1007
+ }
1008
+ var app = arr[0];
1009
+
1010
+ var client = this._getApiDir(dir, app, 'client', name);
1011
+ var manage = this._getApiDir(dir, app, 'manage', name);
1012
+
1013
+ await this.newSql(client, manage, cover);
1014
+ await this.newParam(client, manage, cover);
1015
+ this.newApi(client, manage, cover);
1016
+ };
1017
+
1018
+ /**
1019
+ * 新建event配置文件和文件
1020
+ * @param {string} dir 保存的路径
1021
+ * @param {string} path 检索路径
1022
+ * @param {string} scope 接口域
1023
+ */
1024
+ Drive.prototype.newEvent = async function (dir, path, scope) {
1025
+ var f = dir + '/main.js';
1026
+ if (!f.hasFile()) {
1027
+ var code = (__dirname + '/event_script.js').loadText();
1028
+ code = code.replaceAll('{0}', path).replaceAll('{1}', scope);
1029
+ f.saveText(code);
1030
+ }
1031
+ };
1032
+
1033
+ /**
1034
+ * 处理带逗号的列表映射
1035
+ * @param {Array} list 列表
1036
+ * @returns {Array} 处理后的列表
1037
+ * @private
1038
+ */
1039
+ Drive.prototype._handleCommaList = function (list) {
1040
+ return list.map((o) => {
1041
+ var arr = o.split(',');
1042
+ var value = arr[0];
1043
+ if (arr.length > 0) {
1044
+ return {
1045
+ name: arr[1],
1046
+ value: value,
1047
+ };
1048
+ } else {
1049
+ return {
1050
+ name: value,
1051
+ value,
1052
+ };
1053
+ }
1054
+ });
1055
+ };
1056
+
1057
+ /**
1058
+ * 处理数字列表映射
1059
+ * @param {Array} list 列表
1060
+ * @param {string} map 映射字符串
1061
+ * @returns {Array} 处理后的列表
1062
+ * @private
1063
+ */
1064
+ Drive.prototype._handleNumberList = function (list, map) {
1065
+ if (map.indexOf('0') !== 0) {
1066
+ list.unshift('');
1067
+ }
1068
+ return list.map((value) => {
1069
+ return value.replace(/[0-9]+/, '');
1070
+ });
1071
+ };
1072
+
1073
+ /**
1074
+ * 处理简单列表映射
1075
+ * @param {Array} list 列表
1076
+ * @returns {Array} 处理后的列表
1077
+ * @private
1078
+ */
1079
+ Drive.prototype._handleSimpleList = function (list) {
1080
+ return list.map((value) => {
1081
+ return {
1082
+ name: value,
1083
+ value,
1084
+ };
1085
+ });
1086
+ };
1087
+
1088
+ /**
1089
+ * 处理点分隔的映射
1090
+ * @param {object} obj 字段对象
1091
+ * @param {string} map 映射字符串
1092
+ * @returns {object} 处理后的格式对象
1093
+ * @private
1094
+ */
1095
+ Drive.prototype._handleDotMap = function (obj, map) {
1096
+ var arr = map.split('.');
1097
+ var format = {
1098
+ table: arr[0],
1099
+ name: arr[1],
1100
+ };
1101
+ if (arr.length > 2) {
1102
+ format.id = arr[2];
1103
+ } else {
1104
+ format.id = obj.name;
1105
+ }
1106
+ return format;
1107
+ };
1108
+
1109
+ /**
1110
+ * 处理简单映射
1111
+ * @param {object} obj 字段对象
1112
+ * @param {string} map 映射字符串
1113
+ * @returns {object} 处理后的格式对象
1114
+ * @private
1115
+ */
1116
+ Drive.prototype._handleSimpleMap = function (obj, map) {
1117
+ return {
1118
+ table: map,
1119
+ id: obj.name,
1120
+ name: 'name',
1121
+ };
1122
+ };
1123
+
1124
+ /**
1125
+ * 获取格式
1126
+ * @param {object} obj 字段对象
1127
+ * @returns {object} 返回格式
1128
+ */
1129
+ Drive.prototype.getFormat = async function (obj) {
1130
+ var map = obj.map;
1131
+ var format = {
1132
+ key: obj.name,
1133
+ title: obj.title.replace('ID', '').replace('id', ''),
1134
+ };
1135
+
1136
+ if (map.indexOf('|') !== -1) {
1137
+ var list = map.split('|');
1138
+ if (map.indexOf(',') !== -1) {
1139
+ format.list = this._handleCommaList(list);
1140
+ } else if (/^[0-9]+/.test(map)) {
1141
+ format.list = this._handleNumberList(list, map);
1142
+ } else {
1143
+ format.list = this._handleSimpleList(list);
1144
+ }
1145
+ } else {
1146
+ if (map.indexOf('.') !== -1) {
1147
+ Object.assign(format, this._handleDotMap(obj, map));
1148
+ } else {
1149
+ Object.assign(format, this._handleSimpleMap(obj, map));
1150
+ }
1151
+ }
1152
+
1153
+ return format;
1154
+ };
1155
+
1156
+ /**
1157
+ * 处理字段选择
1158
+ * @param {string} field_name 字段名
1159
+ * @param {string} field_type 字段类型
1160
+ * @returns {object} 处理结果
1161
+ * @private
1162
+ */
1163
+ Drive.prototype._procFieldSel = function (field_name, field_type) {
1164
+ var res = {
1165
+ field: '',
1166
+ field_obj: '',
1167
+ };
1168
+
1169
+ if (this.isCan(field_name, this.get_not, field_type)) {
1170
+ res.field += ',`' + field_name + '`';
1171
+ }
1172
+ if (this.isCan(field_name, this.get_obj_not)) {
1173
+ res.field_obj += ',`' + field_name + '`';
1174
+ }
1175
+ return res;
1176
+ };
1177
+
1178
+ /**
1179
+ * 处理字符串类型字段
1180
+ * @param {object} query 查询对象
1181
+ * @param {string} field_name 字段名
1182
+ * @param {string} keyword 关键词
1183
+ * @returns {string} 更新后的关键词
1184
+ * @private
1185
+ */
1186
+ Drive.prototype._procStringQuery = function (query, field_name, keyword) {
1187
+ var new_keyword = keyword;
1188
+ query[field_name] = '`' + field_name + "` like '%{0}%'";
1189
+ if (this.isSet(field_name, this.query_keyword)) {
1190
+ new_keyword += ' || `' + field_name + "` like '%{0}%'";
1191
+ }
1192
+ return new_keyword;
1193
+ };
1194
+
1195
+ /**
1196
+ * 处理日期时间类型字段
1197
+ * @param {object} query 查询对象
1198
+ * @param {string} field_name 字段名
1199
+ * @private
1200
+ */
1201
+ Drive.prototype._procDateTimeQuery = function (query, field_name) {
1202
+ query[field_name + '_min'] = '`' + field_name + "` >= '{0}'";
1203
+ query[field_name + '_max'] = '`' + field_name + "` <= '{0}'";
1204
+ };
1205
+
1206
+ /**
1207
+ * 处理数字类型字段
1208
+ * @param {object} query 查询对象
1209
+ * @param {object} update_obj 更新对象
1210
+ * @param {string} field_name 字段名
1211
+ * @param {string} uid 用户ID
1212
+ * @param {boolean} query_default_user 是否默认用户查询
1213
+ * @param {string} orderby 排序
1214
+ * @returns {object} 处理结果
1215
+ * @private
1216
+ */
1217
+ Drive.prototype._procNumberQuery = function (
1218
+ query,
1219
+ update_obj,
1220
+ field_name,
1221
+ uid,
1222
+ query_default_user,
1223
+ orderby,
1224
+ ) {
1225
+ var res = {
1226
+ orderby: orderby,
1227
+ update: { ...update_obj },
1228
+ };
1229
+
1230
+ if (!field_name.endsWith('id')) {
1231
+ query[field_name + '_min'] = '`' + field_name + '` >= {0}';
1232
+ query[field_name + '_max'] = '`' + field_name + '` <= {0}';
1233
+ res.update[field_name + '_add'] =
1234
+ '`' + field_name + '` = `' + field_name + '` + {0}';
1235
+
1236
+ if (
1237
+ field_name === 'sort' ||
1238
+ field_name === 'display' ||
1239
+ field_name === 'orderby'
1240
+ ) {
1241
+ res.orderby = '`' + field_name + '` asc';
1242
+ }
1243
+ } else if (field_name === uid && query_default_user) {
1244
+ res.query_default = '`' + field_name + '` = {' + uid + '}';
1245
+ } else if (field_name === 'available' || field_name === 'show') {
1246
+ res.query_default = '`' + field_name + '` = 1';
1247
+ }
1248
+
1249
+ return res;
1250
+ };
1251
+
1252
+ /**
1253
+ * 处理字段类型
1254
+ * @param {string} type 字段类型
1255
+ * @param {string} field_name 字段名
1256
+ * @param {object} query 查询对象
1257
+ * @param {object} update_obj 更新对象
1258
+ * @param {string} uid 用户ID
1259
+ * @param {boolean} query_default_user 是否默认用户查询
1260
+ * @param {string} orderby 排序
1261
+ * @param {string} keyword 关键词
1262
+ * @returns {object} 处理结果
1263
+ * @private
1264
+ */
1265
+ Drive.prototype._procFieldType = function (
1266
+ type,
1267
+ field_name,
1268
+ query,
1269
+ update_obj,
1270
+ uid,
1271
+ query_default_user,
1272
+ orderby,
1273
+ keyword,
1274
+ ) {
1275
+ var result = {
1276
+ keyword: keyword,
1277
+ orderby: orderby,
1278
+ update_obj: update_obj,
1279
+ query_default: {},
1280
+ };
1281
+
1282
+ if (type === 'varchar' || type === 'text' || type === 'longtext') {
1283
+ result.keyword = this._procStringQuery(query, field_name, keyword);
1284
+ } else if (
1285
+ type === 'date' ||
1286
+ type === 'time' ||
1287
+ type === 'datetime' ||
1288
+ type === 'timestamp'
1289
+ ) {
1290
+ this._procDateTimeQuery(query, field_name);
1291
+ } else if (type !== 'tinyint') {
1292
+ var num_res = this._procNumberQuery(
1293
+ query,
1294
+ update_obj,
1295
+ field_name,
1296
+ uid,
1297
+ query_default_user,
1298
+ orderby,
1299
+ );
1300
+ result.orderby = num_res.orderby;
1301
+ result.update_obj = num_res.update;
1302
+ if (num_res.query_default) {
1303
+ result.query_default[field_name] = num_res.query_default;
1304
+ }
1305
+ }
1306
+
1307
+ return result;
1308
+ };
1309
+
1310
+ /**
1311
+ * 处理单个字段
1312
+ * @param {object} o 字段对象
1313
+ * @param {object} query 查询对象
1314
+ * @param {object} update_obj 更新对象
1315
+ * @param {string} uid 用户ID
1316
+ * @param {boolean} query_default_user 是否默认用户查询
1317
+ * @param {string} orderby 排序
1318
+ * @param {string} keyword 关键词
1319
+ * @returns {object} 处理结果
1320
+ * @private
1321
+ */
1322
+ Drive.prototype._procSingleField = async function (
1323
+ o,
1324
+ query,
1325
+ update_obj,
1326
+ uid,
1327
+ query_default_user,
1328
+ orderby,
1329
+ keyword,
1330
+ ) {
1331
+ var { type, name } = o;
1332
+ var result = {
1333
+ field: '*',
1334
+ field_obj: '*',
1335
+ keyword: keyword,
1336
+ orderby: orderby,
1337
+ update_obj: update_obj,
1338
+ query_default: {},
1339
+ format: [],
1340
+ };
1341
+
1342
+ var field_sel = this._procFieldSel(name, type);
1343
+ result.field = field_sel.field;
1344
+ result.field_obj = field_sel.field_obj;
1345
+
1346
+ var type_info = this._procFieldType(
1347
+ type,
1348
+ name,
1349
+ query,
1350
+ update_obj,
1351
+ uid,
1352
+ query_default_user,
1353
+ orderby,
1354
+ keyword,
1355
+ );
1356
+ result.keyword = type_info.keyword;
1357
+ result.orderby = type_info.orderby;
1358
+ result.update_obj = type_info.update_obj;
1359
+ result.query_default = type_info.query_default;
1360
+
1361
+ if (o.map) {
1362
+ var fmt = await this.getFormat(o);
1363
+ if (fmt) {
1364
+ result.format.push(fmt);
1365
+ }
1366
+ }
1367
+
1368
+ return result;
1369
+ };
1370
+
1371
+ /**
1372
+ * 处理字段和查询条件
1373
+ * @param {Array} fields 字段列表
1374
+ * @param {string} table 表名
1375
+ * @returns {object} 处理结果
1376
+ * @private
1377
+ */
1378
+ Drive.prototype._procFields = async function (fields, table) {
1379
+ var query = {};
1380
+ var update_obj = {};
1381
+ var field = '';
1382
+ var field_obj = '';
1383
+ var query_default = {};
1384
+ var format = [];
1385
+ var orderby = '';
1386
+ var uid = $.dict.user_id;
1387
+ var keyword = '';
1388
+ var query_default_user = this.isSet(table, this.query_default_table);
1389
+
1390
+ for (var i = 0; i < fields.length; i++) {
1391
+ var field_info = await this._procSingleField(
1392
+ fields[i],
1393
+ query,
1394
+ update_obj,
1395
+ uid,
1396
+ query_default_user,
1397
+ orderby,
1398
+ keyword,
1399
+ );
1400
+
1401
+ field += field_info.field;
1402
+ field_obj += field_info.field_obj;
1403
+ keyword = field_info.keyword;
1404
+ orderby = field_info.orderby;
1405
+ update_obj = field_info.update_obj;
1406
+
1407
+ // 合并查询默认值
1408
+ for (var key in field_info.query_default) {
1409
+ query_default[key] = field_info.query_default[key];
1410
+ }
1411
+
1412
+ // 合并格式
1413
+ format = format.concat(field_info.format);
1414
+ }
1415
+
1416
+ return {
1417
+ query,
1418
+ update: update_obj,
1419
+ field: field.trim(','),
1420
+ field_obj: field_obj.trim(','),
1421
+ query_default,
1422
+ format,
1423
+ orderby,
1424
+ keyword,
1425
+ };
1426
+ };
1427
+
1428
+ /**
1429
+ * 处理关键字搜索
1430
+ * @param {string} keyword 关键字
1431
+ * @param {string} table 表名
1432
+ * @returns {object} 处理后的查询对象
1433
+ * @private
1434
+ */
1435
+ Drive.prototype._procKeyword = function (keyword, table) {
1436
+ var safe_keyword = escapeLikeKeyword(keyword);
1437
+ var new_keyword = safe_keyword;
1438
+ if (table.indexOf('user_') !== -1) {
1439
+ new_keyword += ' || ' + keyword_default;
1440
+ }
1441
+
1442
+ var query = {};
1443
+ if (new_keyword) {
1444
+ query['keyword'] = '(' + new_keyword.replace(' || ', '') + ')';
1445
+ }
1446
+
1447
+ return query;
1448
+ };
1449
+
1450
+ /**
1451
+ * 创建基础模型
1452
+ * @param {object} cg 配置对象
1453
+ * @param {object} processed 处理结果
1454
+ * @param {object} keyword_query 关键字查询
1455
+ * @returns {object} 基础模型
1456
+ * @private
1457
+ */
1458
+ Drive.prototype._createSqlConfig = function (cg, processed, keyword_query) {
1459
+ return {
1460
+ name: cg.table,
1461
+ title: cg.title,
1462
+ table: cg.table,
1463
+ key: cg.key,
1464
+ orderby_default: '`' + cg.key + '` desc',
1465
+ field_obj: processed.field_obj,
1466
+ field_default: processed.field,
1467
+ method: 'get get_obj avg sum count',
1468
+ query: { ...processed.query, ...keyword_query },
1469
+ query_default: processed.query_default,
1470
+ update: processed.update,
1471
+ format: processed.format,
1472
+ };
1473
+ };
1474
+
1475
+ /**
1476
+ * 保存客户端配置
1477
+ * @param {string} client 客户端配置保存路径
1478
+ * @param {object} base_model 基础模型
1479
+ * @param {boolean} cover 是否覆盖文件
1480
+ * @param {string} orderby 排序
1481
+ * @param {string} uid 用户ID
1482
+ * @param {string} table 表名
1483
+ * @private
1484
+ */
1485
+ Drive.prototype._saveClientConfig = function (
1486
+ client,
1487
+ base_model,
1488
+ cover,
1489
+ orderby,
1490
+ uid,
1491
+ table,
1492
+ ) {
1493
+ var oj = { ...base_model };
1494
+ if (orderby) {
1495
+ oj.orderby_default = orderby;
1496
+ }
1497
+ if (
1498
+ oj.orderby_default &&
1499
+ oj.query_default[uid] &&
1500
+ table.indexOf('user_') !== -1
1501
+ ) {
1502
+ oj.filter = {
1503
+ table: 'table',
1504
+ page: 'page',
1505
+ size: 'size',
1506
+ method: 'method',
1507
+ orderby: 'orderby',
1508
+ field: 'field',
1509
+ count_ret: 'count_ret',
1510
+ };
1511
+ oj.filter[uid] = uid;
1512
+ }
1513
+ this.saveFile(client + '/sql.json', oj, cover);
1514
+ };
1515
+
1516
+ /**
1517
+ * 保存管理端配置
1518
+ * @param {string} manage 管理端配置保存路径
1519
+ * @param {object} base_model 基础模型
1520
+ * @param {boolean} cover 是否覆盖文件
1521
+ * @private
1522
+ */
1523
+ Drive.prototype._saveManageConfig = function (manage, base_model, cover) {
1524
+ var m = { ...base_model };
1525
+ delete m.method;
1526
+ m.field_hide = [];
1527
+ m.name += 2;
1528
+ m.field_obj = (m.field_obj || '*')
1529
+ .replace(',`time_create`', '')
1530
+ .replace(',`time_update`', '')
1531
+ .replace(',`create_time`', '')
1532
+ .replace(',`update_time`', '');
1533
+ delete m.query_default;
1534
+ this.saveFile(manage + '/sql.json', m, cover);
1535
+ };
1536
+
1537
+ /**
1538
+ * 新建sql配置文件
1539
+ * @param {string} client 客户端配置保存路径
1540
+ * @param {string} manage 管理端配置保存路径
1541
+ * @param {boolean} cover 是否覆盖文件
1542
+ */
1543
+ Drive.prototype.newSql = async function (client, manage, cover) {
1544
+ var cg = this.config;
1545
+ var uid = $.dict.user_id;
1546
+
1547
+ var processed = await this._procFields(cg.fields, cg.table);
1548
+ var keyword_query = this._procKeyword(processed.keyword, cg.table);
1549
+ var base_model = this._createSqlConfig(cg, processed, keyword_query);
1550
+
1551
+ if (client) {
1552
+ this._saveClientConfig(
1553
+ client,
1554
+ base_model,
1555
+ cover,
1556
+ processed.orderby,
1557
+ uid,
1558
+ cg.table,
1559
+ );
1560
+ }
1561
+
1562
+ if (manage) {
1563
+ this._saveManageConfig(manage, base_model, cover);
1564
+ }
1565
+ };
1566
+
1567
+ /**
1568
+ * 保存sql配置
1569
+ * @param {string} file 文件名
1570
+ * @param {object} model 配置模型
1571
+ * @param {boolean} cover 是否覆盖文件
1572
+ */
1573
+ Drive.prototype.saveFile = function (file, model, cover) {
1574
+ if (!file.hasFile()) {
1575
+ file.saveText(JSON.stringify(model, null, 2));
1576
+ } else if (cover) {
1577
+ var jobj = file.loadJson();
1578
+ for (var k in model) {
1579
+ var val = model[k];
1580
+ if (!val) {
1581
+ model[k] = jobj[k];
1582
+ }
1583
+ }
1584
+ $.push(jobj, model, true);
1585
+ file.saveText(JSON.stringify(jobj, null, 2));
1586
+ }
1587
+ };
1588
+
1589
+ /**
1590
+ * 新建param配置文件
1591
+ * @param {string} client 客户端配置保存路径
1592
+ * @param {string} manage 管理端配置保存路径
1593
+ * @param {boolean} cover 是否覆盖文件
1594
+ */
1595
+ Drive.prototype.newParam = async function (client, manage, cover) {
1596
+ var cg = this.config;
1597
+ var lt = cg.fields;
1598
+ var cm = this._initParamConfig(cg);
1599
+ var keyword = '';
1600
+
1601
+ for (var i = 0; i < lt.length; i++) {
1602
+ var o = lt[i];
1603
+ var p = o.type;
1604
+ var n = o.name;
1605
+ var m = this._createParamModel(o, p, n);
1606
+
1607
+ if (this._isStringType(p)) {
1608
+ keyword = this._processStringField(cm, m, o, n, keyword);
1609
+ } else if (this._isDateTimeType(p)) {
1610
+ this._processDateTimeField(cm, m, o, n);
1611
+ } else {
1612
+ this._processNumberField(cm, m, o, n, cg);
1613
+ }
1614
+ }
1615
+
1616
+ this._addKeywordConfig(cm, keyword, cg);
1617
+ this._saveParamFiles(cm, client, manage, cover);
1618
+ };
1619
+
1620
+ /**
1621
+ * 初始化参数配置对象
1622
+ * @param {object} cg 配置对象
1623
+ * @returns {object} 参数配置对象
1624
+ * @private
1625
+ */
1626
+ Drive.prototype._initParamConfig = function (cg) {
1627
+ return {
1628
+ name: cg.table,
1629
+ title: cg.title,
1630
+ add: {
1631
+ body: [],
1632
+ body_required: [],
1633
+ },
1634
+ del: {
1635
+ query: [],
1636
+ query_required: [],
1637
+ },
1638
+ set: {
1639
+ query: [],
1640
+ query_required: [],
1641
+ body: [],
1642
+ body_required: [],
1643
+ body_not: [],
1644
+ },
1645
+ get: {
1646
+ query: [],
1647
+ query_required: [],
1648
+ },
1649
+ get_obj: {
1650
+ query_required: [],
1651
+ },
1652
+ list: [],
1653
+ };
1654
+ };
1655
+
1656
+ /**
1657
+ * 创建参数模型
1658
+ * @param {object} field 字段对象
1659
+ * @param {string} type 字段类型
1660
+ * @param {string} name 字段名
1661
+ * @returns {object} 参数模型对象
1662
+ * @private
1663
+ */
1664
+ Drive.prototype._createParamModel = function (field, type, name) {
1665
+ return {
1666
+ name: name,
1667
+ title: field.title,
1668
+ description: field.description + (field.map ? '(' + field.map + ')' : ''),
1669
+ key: field.pk,
1670
+ type: '',
1671
+ dataType: type,
1672
+ };
1673
+ };
1674
+
1675
+ /**
1676
+ * 检查是否为字符串类型
1677
+ * @param {string} type 字段类型
1678
+ * @returns {boolean} 是否为字符串类型
1679
+ * @private
1680
+ */
1681
+ Drive.prototype._isStringType = function (type) {
1682
+ return type === 'varchar' || type === 'text' || type === 'longtext';
1683
+ };
1684
+
1685
+ /**
1686
+ * 检查是否为日期时间类型
1687
+ * @param {string} type 字段类型
1688
+ * @returns {boolean} 是否为日期时间类型
1689
+ * @private
1690
+ */
1691
+ Drive.prototype._isDateTimeType = function (type) {
1692
+ return (
1693
+ type === 'date' ||
1694
+ type === 'time' ||
1695
+ type === 'datetime' ||
1696
+ type === 'timestamp'
1697
+ );
1698
+ };
1699
+
1700
+ /**
1701
+ * 处理字符串类型字段
1702
+ * @param {object} cm 参数配置对象
1703
+ * @param {object} m 模型对象
1704
+ * @param {object} o 字段对象
1705
+ * @param {string} n 字段名
1706
+ * @param {string} keyword 关键词
1707
+ * @returns {string} 更新后的关键词
1708
+ * @private
1709
+ */
1710
+ Drive.prototype._processStringField = function (cm, m, o, n, keyword) {
1711
+ var new_keyword = keyword;
1712
+ m.type = 'string';
1713
+ m.default = o.default;
1714
+ m.string = {};
1715
+
1716
+ this._addStringRange(m, o);
1717
+ this._addStringFormat(m, n);
1718
+
1719
+ if (o.not_null) {
1720
+ m.string.not_empty = !!o.not_null;
1721
+ cm.add.body_required.push(n);
1722
+ } else {
1723
+ cm.add.body.push(n);
1724
+ }
1725
+
1726
+ if (this.isSet(n, this.query_string)) {
1727
+ cm.get.query.push(n);
1728
+ cm.set.query.push(n);
1729
+ new_keyword += '、' + o.title + '(' + n + ')';
1730
+ }
1731
+
1732
+ cm.set.body.push(n);
1733
+ cm.list.push(m);
1734
+ return new_keyword;
1735
+ };
1736
+
1737
+ /**
1738
+ * 添加字符串范围验证
1739
+ * @param {object} m 模型对象
1740
+ * @param {object} o 字段对象
1741
+ * @private
1742
+ */
1743
+ Drive.prototype._addStringRange = function (m, o) {
1744
+ var range = o.min_length && o.max_length ? [o.min_length, o.max_length] : [];
1745
+ if (range.length > 0) {
1746
+ m.string.range = range;
1747
+ } else if (o.min_length) {
1748
+ m.string.min = o.min_length;
1749
+ } else if (o.max_length) {
1750
+ m.string.max = o.max_length;
1751
+ }
1752
+ };
1753
+
1754
+ /**
1755
+ * 检查字段名是否包含指定关键词
1756
+ * @param {string} field_name 字段名
1757
+ * @param {string} keyword 关键词
1758
+ * @returns {boolean} 是否包含
1759
+ * @private
1760
+ */
1761
+ Drive.prototype._hasKeyword = function (field_name, keyword) {
1762
+ return field_name.has(keyword);
1763
+ };
1764
+
1765
+ /**
1766
+ * 检查字段名是否等于指定值
1767
+ * @param {string} field_name 字段名
1768
+ * @param {Array} values 值数组
1769
+ * @returns {boolean} 是否等于
1770
+ * @private
1771
+ */
1772
+ Drive.prototype._isEqual = function (field_name, values) {
1773
+ return values.includes(field_name);
1774
+ };
1775
+
1776
+ /**
1777
+ * 添加字符串格式验证
1778
+ * @param {object} m 模型对象
1779
+ * @param {string} n 字段名
1780
+ * @private
1781
+ */
1782
+ Drive.prototype._addStringFormat = function (m, n) {
1783
+ if (this._hasKeyword(n, 'phone') || this._isEqual(n, ['tel'])) {
1784
+ m.string.format = 'phone';
1785
+ } else if (
1786
+ this._hasKeyword(n, 'url') ||
1787
+ this._isEqual(n, ['src', 'source'])
1788
+ ) {
1789
+ m.string.format = 'url';
1790
+ } else if (this._hasKeyword(n, 'date')) {
1791
+ m.string.format = 'date';
1792
+ } else if (this._isEqual(n, ['num', 'number', 'count'])) {
1793
+ m.string.format = 'digits';
1794
+ } else if (this._isEqual(n, ['money', 'coin'])) {
1795
+ m.string.format = 'number';
1796
+ } else if (this._hasKeyword(n, 'email')) {
1797
+ m.string.format = 'email';
1798
+ }
1799
+ };
1800
+
1801
+ /**
1802
+ * 处理日期时间类型字段
1803
+ * @param {object} cm 参数配置对象
1804
+ * @param {object} m 模型对象
1805
+ * @param {object} o 字段对象
1806
+ * @param {string} n 字段名
1807
+ * @private
1808
+ */
1809
+ Drive.prototype._processDateTimeField = function (cm, m, o, n) {
1810
+ var format = o.type === 'timestamp' ? 'datetime' : o.type;
1811
+ m.type = 'string';
1812
+ m.default = '';
1813
+ m.string = {
1814
+ not_empty: true,
1815
+ format: format,
1816
+ };
1817
+
1818
+ cm.list.push(m);
1819
+ this._addDateTimeRange(cm, m, n);
1820
+
1821
+ if (n.indexOf('create') !== -1 && n.indexOf('update') !== -1) {
1822
+ cm.add.body.push(n);
1823
+ cm.set.body.push(n);
1824
+ }
1825
+
1826
+ cm.get.query.push(n + '_min');
1827
+ cm.get.query.push(n + '_max');
1828
+ };
1829
+
1830
+ /**
1831
+ * 添加日期时间范围模型
1832
+ * @param {object} cm 参数配置对象
1833
+ * @param {object} m 模型对象
1834
+ * @param {string} n 字段名
1835
+ * @private
1836
+ */
1837
+ Drive.prototype._addDateTimeRange = function (cm, m, n) {
1838
+ var m_min = { ...m };
1839
+ m_min.name = n + '_min';
1840
+ m_min.title += '——开始时间';
1841
+ cm.list.push(m_min);
1842
+
1843
+ var m_max = { ...m };
1844
+ m_max.name = n + '_max';
1845
+ m_max.title += '——结束时间';
1846
+ cm.list.push(m_max);
1847
+ };
1848
+
1849
+ /**
1850
+ * 处理数字类型字段
1851
+ * @param {object} cm 参数配置对象
1852
+ * @param {object} m 模型对象
1853
+ * @param {object} o 字段对象
1854
+ * @param {string} n 字段名
1855
+ * @param {object} cg 配置对象
1856
+ * @private
1857
+ */
1858
+ Drive.prototype._processNumberField = function (cm, m, o, n, cg) {
1859
+ m.type = 'number';
1860
+ m.default = Number(o.default) + '';
1861
+ m.number = {};
1862
+
1863
+ this._addNumberRange(m, o);
1864
+
1865
+ if (o.name === cg.key) {
1866
+ this._processPrimaryKey(cm, m, n);
1867
+ } else {
1868
+ this._processNonPrimaryKey(cm, m, n);
1869
+ }
1870
+ };
1871
+
1872
+ /**
1873
+ * 添加数字范围验证
1874
+ * @param {object} m 模型对象
1875
+ * @param {object} o 字段对象
1876
+ * @private
1877
+ */
1878
+ Drive.prototype._addNumberRange = function (m, o) {
1879
+ var range = o.min && o.max ? [o.min, o.max] : [];
1880
+ if (range.length > 0) {
1881
+ m.number.range = range;
1882
+ } else if (o.min) {
1883
+ m.number.min = o.min;
1884
+ } else if (o.max) {
1885
+ m.number.max = o.max;
1886
+ }
1887
+ };
1888
+
1889
+ /**
1890
+ * 处理主键字段
1891
+ * @param {object} cm 参数配置对象
1892
+ * @param {object} m 模型对象
1893
+ * @param {string} n 字段名
1894
+ * @private
1895
+ */
1896
+ Drive.prototype._processPrimaryKey = function (cm, m, n) {
1897
+ cm.del.query_required.push(n);
1898
+ cm.set.query.push(n);
1899
+ cm.get.query.push(n);
1900
+ cm.get_obj.query_required.push(n);
1901
+ cm.list.push(m);
1902
+ };
1903
+
1904
+ /**
1905
+ * 处理非主键字段
1906
+ * @param {object} cm 参数配置对象
1907
+ * @param {object} m 模型对象
1908
+ * @param {string} n 字段名
1909
+ * @private
1910
+ */
1911
+ Drive.prototype._processNonPrimaryKey = function (cm, m, n) {
1912
+ cm.add.body.push(n);
1913
+ cm.set.body.push(n);
1914
+ cm.list.push(m);
1915
+
1916
+ if (m.dataType !== 'tinyint') {
1917
+ this._processNonTinyInt(cm, m, n);
1918
+ } else {
1919
+ cm.set.query.push(n);
1920
+ cm.get.query.push(n);
1921
+ }
1922
+ };
1923
+
1924
+ /**
1925
+ * 处理非tinyint字段
1926
+ * @param {object} cm 参数配置对象
1927
+ * @param {object} m 模型对象
1928
+ * @param {string} n 字段名
1929
+ * @private
1930
+ */
1931
+ Drive.prototype._processNonTinyInt = function (cm, m, n) {
1932
+ var ne = n;
1933
+ if (!ne.endsWith('id')) {
1934
+ this._addNumberRangeFields(cm, m, n);
1935
+ } else {
1936
+ cm.get.query.push(n);
1937
+ if (this.isSet(n, this.query_number)) {
1938
+ cm.set.query.push(n);
1939
+ }
1940
+ }
1941
+ };
1942
+
1943
+ /**
1944
+ * 添加数字范围字段
1945
+ * @param {object} cm 参数配置对象
1946
+ * @param {object} m 模型对象
1947
+ * @param {string} n 字段名
1948
+ * @private
1949
+ */
1950
+ Drive.prototype._addNumberRangeFields = function (cm, m, n) {
1951
+ cm.get.query.push(n + '_min');
1952
+ cm.get.query.push(n + '_max');
1953
+ cm.set.query.push(n + '_min');
1954
+ cm.set.query.push(n + '_max');
1955
+ cm.set.body.push(n + '_add');
1956
+
1957
+ var m_min = { ...m };
1958
+ m_min.name = n + '_min';
1959
+ m_min.title += '——最小值';
1960
+ cm.list.push(m_min);
1961
+
1962
+ var m_max = { ...m };
1963
+ m_max.name = n + '_max';
1964
+ m_max.title += '——最大值';
1965
+ cm.list.push(m_max);
1966
+ };
1967
+
1968
+ /**
1969
+ * 添加关键词配置
1970
+ * @param {object} cm 参数配置对象
1971
+ * @param {string} keyword 关键词
1972
+ * @param {object} cg 配置对象
1973
+ * @private
1974
+ */
1975
+ Drive.prototype._addKeywordConfig = function (cm, keyword, cg) {
1976
+ if (keyword) {
1977
+ cm.get.query.push('keyword');
1978
+ cm.set.query.push('keyword');
1979
+ var new_keyword = keyword;
1980
+ if (cg.table.indexOf('user_') !== -1) {
1981
+ new_keyword += '、' + keyword_default_tip;
1982
+ }
1983
+ var m_k = {
1984
+ name: 'keyword',
1985
+ title: '关键词',
1986
+ description: '用于搜索' + new_keyword.replace('、', ''),
1987
+ type: 'string',
1988
+ string: {},
1989
+ };
1990
+ cm.list.push(m_k);
1991
+ }
1992
+ };
1993
+
1994
+ /**
1995
+ * 保存参数配置文件
1996
+ * @param {object} cm 参数配置对象
1997
+ * @param {string} client 客户端配置保存路径
1998
+ * @param {string} manage 管理端配置保存路径
1999
+ * @param {boolean} cover 是否覆盖文件
2000
+ * @private
2001
+ */
2002
+ Drive.prototype._saveParamFiles = function (cm, client, manage, cover) {
2003
+ if (client) {
2004
+ this.saveFile(client + '/param.json', cm, cover);
2005
+ }
2006
+ if (manage) {
2007
+ var manage_cm = { ...cm };
2008
+ delete manage_cm.method;
2009
+ manage_cm.name += 2;
2010
+ this.saveFile(manage + '/param.json', manage_cm, cover);
2011
+ }
2012
+ };
2013
+
2014
+ /**
2015
+ * 是否设置
2016
+ * @param {string} name 名称
2017
+ * @param {Array} arr 匹配的对象
2018
+ * @returns {boolean} 是否设置
2019
+ */
2020
+ Drive.prototype.isSet = function (name, arr) {
2021
+ if (!arr) {
2022
+ return false;
2023
+ }
2024
+ var bl = false;
2025
+ for (var i = 0; i < arr.length; i++) {
2026
+ if (name.indexOf(arr[i]) !== -1) {
2027
+ bl = true;
2028
+ break;
2029
+ }
2030
+ }
2031
+ return bl;
2032
+ };
2033
+
2034
+ /**
2035
+ * 是否排除
2036
+ * @param {string} name 名称
2037
+ * @param {Array} arr 匹配的对象
2038
+ * @param {string} type 数据类型
2039
+ * @returns {boolean} 是否可以
2040
+ */
2041
+ Drive.prototype.isCan = function (name, arr, type) {
2042
+ if (type === 'longtext') {
2043
+ return false;
2044
+ }
2045
+ if (!arr) {
2046
+ return true;
2047
+ }
2048
+ var bl = true;
2049
+ for (var i = 0; i < arr.length; i++) {
2050
+ if (name.indexOf(arr[i]) !== -1) {
2051
+ bl = false;
2052
+ break;
2053
+ }
2054
+ }
2055
+ return bl;
2056
+ };
2057
+
2058
+ /**
2059
+ * 确定API路径
2060
+ * @param {object} cg 配置对象
2061
+ * @returns {string} API路径
2062
+ * @private
2063
+ */
2064
+ Drive.prototype._deteApiPath = function (cg) {
2065
+ var arr = cg.table.split('_');
2066
+ var p = '/api/';
2067
+ if (arr.length > 1) {
2068
+ p += cg.table.replace('_', '/');
2069
+ } else {
2070
+ p += arr[0];
2071
+ }
2072
+ return p;
2073
+ };
2074
+
2075
+ /**
2076
+ * 创建基础API配置
2077
+ * @param {object} cg 配置对象
2078
+ * @param {string} path API路径
2079
+ * @returns {object} API配置
2080
+ * @private
2081
+ */
2082
+ Drive.prototype._createApiConfig = function (cg, path) {
2083
+ return {
2084
+ name: cg.table,
2085
+ title: cg.title,
2086
+ description: cg.description,
2087
+ path: path,
2088
+ method: 'ALL',
2089
+ cache: 0,
2090
+ client_cache: false,
2091
+ param_path: './param.json',
2092
+ sql_path: './sql.json',
2093
+ check_param: true,
2094
+ };
2095
+ };
2096
+
2097
+ /**
2098
+ * 检查是否包含用户ID字段
2099
+ * @param {Array} fields 字段列表
2100
+ * @returns {boolean} 是否包含用户ID字段
2101
+ * @private
2102
+ */
2103
+ Drive.prototype._hasUserIdField = function (fields) {
2104
+ for (var i = 0, item; (item = fields[i++]);) {
2105
+ var name = item.name;
2106
+ if (
2107
+ name == $.dict.user_id ||
2108
+ name === 'uid' ||
2109
+ name === 'user_id' ||
2110
+ name === 'userid'
2111
+ ) {
2112
+ return true;
2113
+ }
2114
+ }
2115
+ return false;
2116
+ };
2117
+
2118
+ /**
2119
+ * 保存客户端API配置
2120
+ * @param {string} client 客户端配置保存路径
2121
+ * @param {object} base_config 基础配置
2122
+ * @param {object} cg 配置对象
2123
+ * @param {boolean} cover 是否覆盖文件
2124
+ * @private
2125
+ */
2126
+ Drive.prototype._saveClientApiConfig = function (
2127
+ client,
2128
+ base_config,
2129
+ cg,
2130
+ cover,
2131
+ ) {
2132
+ var o = { ...base_config };
2133
+
2134
+ if (cg.title.indexOf('user') !== -1) {
2135
+ if (this._hasUserIdField(cg.fields)) {
2136
+ o.oauth = {
2137
+ scope: true,
2138
+ sign_in: true,
2139
+ vip: 0,
2140
+ user_group: [],
2141
+ };
2142
+ }
2143
+ }
2144
+ o.method = 'GET';
2145
+ this.saveFile(client + '/api.json', o, cover);
2146
+ };
2147
+
2148
+ /**
2149
+ * 保存管理端API配置
2150
+ * @param {string} manage 管理端配置保存路径
2151
+ * @param {object} base_config 基础配置
2152
+ * @param {boolean} cover 是否覆盖文件
2153
+ * @private
2154
+ */
2155
+ Drive.prototype._saveManageApiConfig = function (manage, base_config, cover) {
2156
+ var o = { ...base_config };
2157
+ o.oauth = {
2158
+ scope: true,
2159
+ sign_in: true,
2160
+ gm: 2,
2161
+ user_admin: [],
2162
+ };
2163
+ o.path = o.path.replace('/api/', '/apis/');
2164
+ o.name += '_manage';
2165
+ this.saveFile(manage + '/api.json', o, cover);
2166
+ };
2167
+
2168
+ /**
2169
+ * 新建api配置文件
2170
+ * @param {string} client 客户端配置保存路径
2171
+ * @param {string} manage 管理端配置保存路径
2172
+ * @param {boolean} cover 是否覆盖文件
2173
+ */
2174
+ Drive.prototype.newApi = async function (client, manage, cover) {
2175
+ var cg = this.config;
2176
+ var path = this._deteApiPath(cg);
2177
+ var base_config = this._createApiConfig(cg, path);
2178
+
2179
+ if (client) {
2180
+ this._saveClientApiConfig(client, base_config, cg, cover);
2181
+ }
2182
+
2183
+ if (manage) {
2184
+ this._saveManageApiConfig(manage, base_config, cover);
2185
+ }
2186
+ };
2187
+
2188
+ /**
2189
+ * 获取模型
2190
+ * @param {string} type 模型类型
2191
+ * @returns {object} 返回获取到的模型
2192
+ */
2193
+ Drive.prototype.getModel = function (type) {
2194
+ let model = { ...this.config };
2195
+ let dir = this.getDir();
2196
+ let l = $.slash;
2197
+ let app_name = dir.between('app' + l, l);
2198
+ let plugin_name = dir.between('plugin' + l, l);
2199
+ let name = dir.basename();
2200
+ model.app = app_name;
2201
+ model.plugin = plugin_name;
2202
+ model.name = model.name || name;
2203
+ return model;
2204
+ };
2205
+
2206
+ module.exports = Drive;