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