mm_machine 2.1.1 → 2.1.3

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/index.js CHANGED
@@ -1,571 +1,640 @@
1
- /**
2
- * @fileOverview 机制构建帮助类函数
3
- * @author <a href="http://qww.elins.cn">邱文武</a>
4
- * @version 1.6
5
- */
6
- const util = require('util');
7
- const Item = require('./item.js');
8
-
9
- /**
10
- * @class Index索引类
11
- */
12
- class Index {
13
- /**
14
- * 构造函数
15
- * @param {Object} scope 作用域
16
- * @param {String} dir_base 模块目录
17
- * @constructor
18
- */
19
- constructor(scope, dir_base) {
20
- // 作用域(同时作为检索的后缀名)
21
- this.scope = scope || ($.val && $.val.scope) ? $.val.scope + '' : '';
22
- // Index接口列表
23
- this.list = [];
24
-
25
- /**
26
- * 机制类型
27
- */
28
- this.type = "";
29
-
30
- /**
31
- * 排序项
32
- */
33
- this.sort_key = "sort";
34
-
35
- /**
36
- * 模块目录
37
- */
38
- this.dir_base = dir_base;
39
-
40
- /**
41
- * 模式
42
- * 1.生产模式,改变文件不会重新加载
43
- * 2.热更新模式,改变配置文件会重新加载配置,不重新加载脚本
44
- * 3.热重载模式,改变配置文件都会加载配置和脚本
45
- * 4.重载模式,执行完后重新加载脚本,避免变量污染
46
- * 5.热更新+重载模式,改变配置文件重新加载配置和脚本,执行完后重新加载脚本
47
- */
48
- this.mode = 1;
49
- }
50
-
51
- /**
52
- * 私有方法:执行模块方法
53
- * @param {Object} module 模块对象
54
- * @param {String} method 方法名称
55
- * @param {Array} params 参数数组
56
- * @returns {Promise<any>} 执行结果
57
- * @private
58
- */
59
- async _execute(module, method, params = []) {
60
- if (!module || !method) return null;
61
-
62
- try {
63
- // 确保模块已加载
64
- if (!module.complete) {
65
- await module.exec('load');
66
- }
67
-
68
- // 执行方法
69
- const ret = await module.exec(method, ...params);
70
-
71
- // 根据模式决定是否重载
72
- if (this.mode >= 4) {
73
- module.exec('reload');
74
- }
75
-
76
- return ret;
77
- } catch (err) {
78
- $.log.error(`[${this.type}] 执行模块方法失败: `, err);
79
- return null;
80
- }
81
- }
82
- }
83
-
84
- /**
85
- * 清除接口缓存
86
- */
87
- Index.prototype.clear = function() {
88
- this.list = [];
89
- };
90
-
91
- /**
92
- * 默认驱动
93
- */
94
- Index.prototype.Drive = Item;
95
-
96
- /**
97
- * 加载项
98
- * @param {String} dir 文件路径
99
- * @param {Object} cg 配置参数
100
- * @param {String} file 配置文件
101
- * @returns {Promise<Object|null>} 加载的驱动或配置对象
102
- */
103
- Index.prototype.loadItem = async function(dir, cg, file) {
104
- if (!dir || !file) {
105
- $.log.error(`[${this.type}] load: 缺少必要参数`);
106
- return null;
107
- }
108
-
109
- try {
110
- if (this.Drive) {
111
- const drive = new this.Drive(dir, this.dir_base.fullname());
112
- drive.mode = this.mode;
113
- if (cg) {
114
- await drive.exec('loadConfig', file, cg.name);
115
- await drive.exec('setConfig', cg);
116
- } else {
117
- await drive.exec('loadConfig', file);
118
- }
119
- this.list.push(drive);
120
- return drive;
121
- } else {
122
- let json = file.loadJson();
123
- if (!json) {
124
- const fl = "./config.tpl.json".fullname(this.dir_base);
125
- if (fl.hasFile()) {
126
- fl.copyFile(file);
127
- json = file.loadJson();
128
- }
129
- }
130
- if (json) {
131
- this.list.push(json);
132
- }
133
- return json;
134
- }
135
- } catch (err) {
136
- $.log.error(`[${this.type}] 加载项失败: `, err);
137
- return null;
138
- }
139
- };
140
-
141
- /**
142
- * 加载列表
143
- * @param {Array} list 文件列表
144
- * @returns {Promise<void>}
145
- */
146
- Index.prototype.loads = async function(list) {
147
- // 遍历文件路径
148
- if (!Array.isArray(list)) {
149
- $.log.error(`[${this.type}] loads: 列表参数必须是数组`);
150
- return;
151
- }
152
-
153
- // 使用for...of和async/await以保证正确的执行顺序
154
- for (const file of list) {
155
- await this.loadFile(file, true);
156
- }
157
- };
158
-
159
- /**
160
- * 排序
161
- */
162
- Index.prototype.sort = function() {
163
- this.list.sort((o1, o2) => {
164
- const p1 = o1.config?.[this.sort_key] || 0;
165
- const p2 = o2.config?.[this.sort_key] || 0;
166
- return p2 - p1;
167
- });
168
- };
169
-
170
- /**
171
- * 更新前
172
- */
173
- Index.prototype.update_before = async function(dir) {
174
- // $.log.debug("更新前")
175
- }
176
-
177
- /**
178
- * 更新后
179
- */
180
- Index.prototype.update_after = async function(dir) {
181
- // $.log.debug("更新后")
182
- }
183
-
184
- /**
185
- * 更新所有配置
186
- * @param {String} searchPath 检索路径
187
- * @param {Boolean} accurate 精准路径,默认为false
188
- * @returns {Promise<void>}
189
- */
190
- Index.prototype.updateConfigAll = async function(searchPath, accurate) {
191
- try {
192
- // 规范化路径
193
- let normalizedPath = searchPath;
194
- if (!normalizedPath) {
195
- normalizedPath = './app/';
196
- } else {
197
- // 直接使用传入的路径,不强制添加app/
198
- normalizedPath = normalizedPath.fullname();
199
- }
200
-
201
- let list_scope = [];
202
- try {
203
- // 使用精准模式总是获取所有目录
204
- // 这样可以确保找到test1和test2目录
205
- list_scope = $.dir.getAll(normalizedPath);
206
- } catch (err) {
207
- $.log.error(`[${this.type}] 检索目录失败!`, err);
208
- }
209
-
210
- // 处理找到的目录
211
- for (const f of list_scope) {
212
- // 直接检查并加载demo.json文件
213
- const config_file = `./${this.type}.json`.fullname(f);
214
- if (config_file && config_file.hasFile && config_file.hasFile()) {
215
- // 直接加载这个文件
216
- await this.loadFile(config_file, true);
217
- }
218
-
219
- // 同时也检查config_demo.json文件
220
- const config_file2 = `./config_${this.type}.json`.fullname(f);
221
- if (config_file2 && config_file2.hasFile && config_file2.hasFile()) {
222
- // 直接加载这个文件
223
- await this.loadFile(config_file2, true);
224
- }
225
- }
226
- } catch (err) {
227
- $.log.error(`[${this.type}] 更新所有配置失败: `, err);
228
- }
229
- };
230
-
231
- /**
232
- * 更新已有配置文件
233
- * @param {string} dir 目录路径
234
- * @returns {Promise<void>}
235
- */
236
- Index.prototype.updateConfigHave = async function(dir) {
237
- const list = this.list;
238
- for (const o of list) {
239
- const file = o.filename;
240
- if (file) {
241
- try {
242
- const config = file.loadJson();
243
- if (config) {
244
- if (Array.isArray(config)) {
245
- // 使用find方法查找匹配项
246
- const targetConfig = config.find(cg => cg.name === o.config.name);
247
- if (targetConfig) {
248
- await o.exec('setConfig', targetConfig);
249
- }
250
- } else {
251
- await o.exec('setConfig', config);
252
- }
253
- }
254
- } catch (err) {
255
- $.log.error(`[${this.type}] 更新配置失败: `, err);
256
- }
257
- }
258
- }
259
- };
260
-
261
- /**
262
- * 更新配置
263
- * @param {Object} dir - 检索目录
264
- * @param {Boolean} accurate - 精准路径,默认为false
265
- * @param {Boolean} clear - 是否清除现有配置,默认为false
266
- * @returns {Promise<void>}
267
- */
268
- Index.prototype.updateConfig = async function(dir, accurate = false, clear = false) {
269
- try {
270
- if (clear) {
271
- this.clear();
272
- await this.updateConfigAll(dir, accurate);
273
- } else {
274
- // 如果没有指定目录,则更新已有的配置文件
275
- await this.updateConfigHave();
276
- }
277
- this.sort();
278
- } catch (err) {
279
- $.log.error(`[${this.type}] 配置更新失败: `, err);
280
- }
281
- };
282
-
283
- /**
284
- * 更新JS
285
- * @returns {Promise<void>}
286
- */
287
- Index.prototype.updateScript = async function() {
288
- const list = this.list;
289
- for (const o of list) {
290
- if (o.config?.state === 1) {
291
- await o.exec('load');
292
- }
293
- }
294
- };
295
-
296
- /**
297
- * 更新
298
- * @param {String} dir 检索的路径
299
- * @param {Boolean} accurate 是否精准路径,默认为false
300
- * @param {Boolean} loadJS 是否加载JS,默认为true
301
- * @param {Boolean} clear 是否清除现有配置,默认为true
302
- * @returns {Promise<void>}
303
- */
304
- Index.prototype.update_main = async function(dir, accurate = false, loadJS = true, clear = true) {
305
- await this.updateConfig(dir, accurate, clear);
306
- if (loadJS) {
307
- await this.updateScript();
308
- }
309
- };
310
-
311
- /**
312
- * 更新配置
313
- * @param {String} dir 检索的路径
314
- * @param {Boolean} loadJS 是否加载JS
315
- */
316
- Index.prototype.update = async function(dir, accurate = false, loadJS = true, clear = true) {
317
- await this.update_before(dir);
318
- await this.update_main(dir, accurate, loadJS, clear);
319
- await this.update_after(dir);
320
- };
321
-
322
- /**
323
- * 查询配置项
324
- * @param {String} name 名称
325
- * @return {Object|null} 返回单项配置
326
- */
327
- Index.prototype.get = function(name) {
328
- if (!name) return null;
329
- return this.list.find(item => item.config?.name === name) || null;
330
- };
331
-
332
- /**
333
- * 设置配置项
334
- * @param {String} name 配置项名称
335
- * @param {Object} cg 配置对象
336
- * @return {Boolean} 是否设置成功
337
- */
338
- Index.prototype.set = function(name, cg) {
339
- if (!name || !cg) return false;
340
-
341
- const item = this.get(name);
342
- if (item) {
343
- // 使用Object.assign合并属性
344
- Object.assign(item.config, cg);
345
- return true;
346
- }
347
- return false;
348
- };
349
-
350
- /**
351
- * 保存
352
- * @returns {Boolean} 是否保存成功
353
- */
354
- Index.prototype.save = async function() {
355
- const list = this.list;
356
- for (const o of list) {
357
- try {
358
- await o.exec('save');
359
- } catch (err) {
360
- $.log.error(`[${this.type}] 保存失败: `, err);
361
- }
362
- }
363
- return true;
364
- };
365
-
366
- /**
367
- * 添加插件
368
- * @param {Object} config 配置对象
369
- * @returns {Promise<Object|null>} 新添加的插件对象
370
- */
371
- Index.prototype.add = async function(config) {
372
- if (!config || !config.name) {
373
- $.log.error(`[${this.type}] 添加插件失败: 缺少必要的配置信息`);
374
- return null;
375
- }
376
-
377
- try {
378
- // 检查是否已存在
379
- const existing = this.get(config.name);
380
- if (existing) {
381
- $.log.warn(`插件 ${config.name} 已存在`);
382
- return existing;
383
- }
384
-
385
- const item = new Item(this, config);
386
- this.list.push(item);
387
- this.sort();
388
- return item;
389
- } catch (err) {
390
- $.log.error(`[${this.type}] 添加插件失败: `, err);
391
- return null;
392
- }
393
- };
394
-
395
- /**
396
- * 删除插件
397
- * @param {String} name 插件名称
398
- * @returns {Promise<Boolean>} 是否删除成功
399
- */
400
- Index.prototype.del = async function(name) {
401
- if (!name) return false;
402
-
403
- const index = this.list.findIndex(item => item.config?.name === name);
404
- if (index === -1) return false;
405
-
406
- try {
407
- const item = this.list[index];
408
- await item.exec('unload');
409
- this.list.splice(index, 1);
410
- return true;
411
- } catch (err) {
412
- $.log.error(`[${this.type}] 删除插件失败: `, err);
413
- return false;
414
- }
415
- };
416
-
417
- /**
418
- * 加载插件
419
- * @param {String} name 插件名称
420
- * @returns {Promise<Object|null>} 加载的插件对象
421
- */
422
- Index.prototype.load = async function(name) {
423
- if (!name) return null;
424
-
425
- const item = this.get(name);
426
- if (!item) return null;
427
-
428
- try {
429
- if (item.config?.state === 1) {
430
- await item.exec('load');
431
- }
432
- return item;
433
- } catch (err) {
434
- $.log.error(`[${this.type}] 加载插件 ${name} 失败: `, err);
435
- return null;
436
- }
437
- };
438
-
439
- /**
440
- * 卸载插件
441
- * @param {String} name 插件名称
442
- * @returns {Promise<Boolean>} 是否卸载成功
443
- */
444
- Index.prototype.unload = async function(name) {
445
- if (!name) return false;
446
-
447
- const item = this.get(name);
448
- if (!item) return false;
449
-
450
- try {
451
- await item.exec('unload');
452
- return true;
453
- } catch (err) {
454
- $.log.error(`[${this.type}] 卸载插件 ${name} 失败: `, err);
455
- return false;
456
- }
457
- };
458
-
459
- /**
460
- * 重新加载插件
461
- * @param {String} name 插件名称
462
- * @returns {Promise<Object|null>} 重新加载的插件对象
463
- */
464
- Index.prototype.reload = async function(name) {
465
- if (!name) return null;
466
-
467
- const item = this.get(name);
468
- if (!item) return null;
469
-
470
- try {
471
- await item.exec('reload');
472
- return item;
473
- } catch (err) {
474
- $.log.error(`[${this.type}] 重新加载插件 ${name} 失败: `, err);
475
- return null;
476
- }
477
- };
478
-
479
- /**
480
- * 通过文件加载配置
481
- * @param {String} file 文件名
482
- * @param {Boolean} create 不存在则进行创建
483
- * @returns {Promise<Object|null|String>} 加载的驱动对象、null或错误信息
484
- */
485
- Index.prototype.loadFile = async function(file, create = false) {
486
- try {
487
- const dir = file.dirname();
488
- // 载入文件
489
- const obj = file.loadJson();
490
- if (obj) {
491
- if (Array.isArray(obj)) {
492
- for (const o of obj) {
493
- // 实例化一个驱动
494
- await this.loadItem(dir, o, file);
495
- }
496
- } else {
497
- return await this.loadItem(dir, null, file);
498
- }
499
- } else if (create) {
500
- return await this.loadItem(dir, null, file);
501
- } else {
502
- return `${file}文件不存在`;
503
- }
504
- return null;
505
- } catch (err) {
506
- $.log.error(`[${this.type}] 加载文件失败: `, err);
507
- return `加载文件失败: ${err.message}`;
508
- }
509
- };
510
-
511
- /**
512
- * 调用函数
513
- * @param {String} name 模块名
514
- * @param {String} method 函数名
515
- * @param {Object} params 参数集合
516
- * @returns {Promise<any>} 执行结果
517
- */
518
- Index.prototype.run = async function(name, method, ...params) {
519
- if (name) {
520
- const module = await this.get(name);
521
- if (module && module.config?.state === 1) {
522
- return await this._execute(module, method, params);
523
- }
524
- return null;
525
- } else if (name === null) {
526
- let result = null;
527
- for (const module of this.list) {
528
- if (module.config?.state === 1) {
529
- result = await this._execute(module, method, params);
530
- if (result && module.config?.end) {
531
- break;
532
- }
533
- }
534
- }
535
- return result;
536
- }
537
- return null;
538
- };
539
-
540
- /**
541
- * 执行方法
542
- * @param {String} name 插件名称
543
- * @param {String} method 方法名称
544
- * @param {Object} option 配置参数
545
- * @returns {Promise<any>} 执行结果
546
- */
547
- Index.prototype.exec = async function(name, method, ...params) {
548
- if (name) {
549
- const module = await this.get(name);
550
- if (module) {
551
- return await this._execute(module, method, params);
552
- }
553
- return null;
554
- } else if (name === null) {
555
- let result = null;
556
- for (const module of this.list) {
557
- result = await this._execute(module, method, params);
558
- if (result && module.config?.end) {
559
- break;
560
- }
561
- }
562
- return result;
563
- }
564
- return null;
565
- };
566
-
567
- /**
568
- * @module 导出Index类
569
- */
570
- exports.Index = Index;
571
- exports.Item = Item;
1
+ /**
2
+ * @file 机制构建帮助类函数
3
+ * @author <a href="http://qww.elins.cn">邱文武</a>
4
+ * @version 1.6
5
+ */
6
+ const { Item } = require('./item.js');
7
+
8
+ /**
9
+ * @class Index索引类
10
+ */
11
+ class Index {
12
+ /**
13
+ * 构造函数
14
+ * @param {object} scope 作用域
15
+ * @param {string} dir_base 模块目录
16
+ * @class
17
+ */
18
+ constructor(scope, dir_base) {
19
+ // 作用域(同时作为检索的后缀名)
20
+ this.scope = scope || ($.val && $.val.scope) ? $.val.scope + '' : '';
21
+ // Index接口列表
22
+ this.list = [];
23
+
24
+ /**
25
+ * 机制类型
26
+ */
27
+ this.type = '';
28
+
29
+ /**
30
+ * 排序项
31
+ */
32
+ this.sort_key = 'sort';
33
+
34
+ /**
35
+ * 模块目录
36
+ */
37
+ this.dir_base = dir_base;
38
+
39
+ /**
40
+ * 模式
41
+ * 1.生产模式,改变文件不会重新加载
42
+ * 2.热更新模式,改变配置文件会重新加载配置,不重新加载脚本
43
+ * 3.热重载模式,改变配置文件都会加载配置和脚本
44
+ * 4.重载模式,执行完后重新加载脚本,避免变量污染
45
+ * 5.热更新+重载模式,改变配置文件重新加载配置和脚本,执行完后重新加载脚本
46
+ */
47
+ this.mode = 1;
48
+
49
+ /**
50
+ * 日志输出
51
+ */
52
+ this._logger = $.log || console;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 日志输出
58
+ * @param {string} level - 日志级别(debug/info/warn/error)
59
+ * @param {string} message - 日志消息
60
+ * @param {...*} args - 可选参数
61
+ */
62
+ Index.prototype.log = function (level, message, ...args) {
63
+ this._logger[level](`[${this.constructor.name}.${this.type}] ${message}`, ...args);
64
+ };
65
+
66
+ /**
67
+ * 清除接口缓存
68
+ */
69
+ Index.prototype.clear = function () {
70
+ this.list = [];
71
+ };
72
+
73
+ /**
74
+ * 默认驱动
75
+ */
76
+ Index.prototype.Drive = Item;
77
+
78
+ /**
79
+ * 私有方法:执行模块方法
80
+ * @param {object} module 模块对象
81
+ * @param {string} method 方法名称
82
+ * @param {Array} params 参数数组
83
+ * @returns {Promise<any>} 执行结果
84
+ * @private
85
+ */
86
+ Index.prototype._exec = async function (module, method, params = []) {
87
+ if (!module || !method) return null;
88
+
89
+ try {
90
+ // 确保模块已加载
91
+ if (!module.complete) {
92
+ await module.exec('load');
93
+ }
94
+
95
+ // 执行方法
96
+ const ret = await module.exec(method, ...params);
97
+
98
+ // 根据模式决定是否重载
99
+ if (this.mode >= 4) {
100
+ module.exec('reload');
101
+ }
102
+
103
+ return ret;
104
+ } catch (err) {
105
+ $.log.error(`[${this.type}] 执行模块方法失败: `, err);
106
+ return null;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * 加载项
112
+ * @param {string} dir 文件路径
113
+ * @param {object} cg 配置参数
114
+ * @param {string} file 配置文件
115
+ * @returns {Promise<object | null>} 加载的驱动或配置对象
116
+ */
117
+ Index.prototype.loadItem = async function (dir, cg, file) {
118
+ if (!dir || !file) {
119
+ $.log.error(`[${this.type}] load: 缺少必要参数`);
120
+ return null;
121
+ }
122
+
123
+ try {
124
+ if (this.Drive) {
125
+ const drive = new this.Drive(dir, this.dir_base.fullname());
126
+ drive.mode = this.mode;
127
+ if (cg) {
128
+ await drive.exec('loadConfig', file, cg.name);
129
+ await drive.exec('setConfig', cg);
130
+ } else {
131
+ await drive.exec('loadConfig', file);
132
+ }
133
+ this.list.push(drive);
134
+ return drive;
135
+ } else {
136
+ let json = file.loadJson();
137
+ if (!json) {
138
+ const fl = './config.tpl.json'.fullname(this.dir_base);
139
+ if (fl.hasFile()) {
140
+ fl.copyFile(file);
141
+ json = file.loadJson();
142
+ }
143
+ }
144
+ if (json) {
145
+ this.list.push(json);
146
+ }
147
+ return json;
148
+ }
149
+ } catch (err) {
150
+ $.log.error(`[${this.type}] 加载项失败: `, err);
151
+ return null;
152
+ }
153
+ };
154
+
155
+ /**
156
+ * 加载列表
157
+ * @param {Array} list 文件列表
158
+ * @returns {Promise<void>}
159
+ */
160
+ Index.prototype.loads = async function (list) {
161
+ // 遍历文件路径
162
+ if (!Array.isArray(list)) {
163
+ $.log.error(`[${this.type}] loads: 列表参数必须是数组`);
164
+ return;
165
+ }
166
+
167
+ // 使用函数式编程,按顺序加载文件
168
+ await list.reduce(async (prev_promise, file_item) => {
169
+ await prev_promise;
170
+ return this.loadFile(file_item, true);
171
+ }, Promise.resolve());
172
+ };
173
+
174
+ /**
175
+ * 排序
176
+ */
177
+ Index.prototype.sort = function () {
178
+ this.list.sort((o1, o2) => {
179
+ const p1 = o1.config?.[this.sort_key] || 0;
180
+ const p2 = o2.config?.[this.sort_key] || 0;
181
+ return p2 - p1;
182
+ });
183
+ };
184
+
185
+ /**
186
+ * 更新前
187
+ * @param {string} dir_path 目录路径
188
+ */
189
+ Index.prototype.updateBefore = async function (dir_path) {
190
+ // $.log.debug("更新前")
191
+ };
192
+
193
+ /**
194
+ * 更新后
195
+ * @param {string} dir_path 目录路径
196
+ */
197
+ Index.prototype.updateAfter = async function (dir_path) {
198
+ // $.log.debug("更新后")
199
+ };
200
+
201
+ /**
202
+ * 规范化检索路径
203
+ * @param {string} search_path 检索路径
204
+ * @returns {string} 规范化后的路径
205
+ */
206
+ Index.prototype._normalizeSearchPath = function (search_path) {
207
+ if (!search_path) {
208
+ return './app/';
209
+ }
210
+ // 直接使用传入的路径,不强制添加app/
211
+ return search_path.fullname();
212
+ };
213
+
214
+ /**
215
+ * 获取目录列表
216
+ * @param {string} norm_path 规范化路径
217
+ * @returns {Array} 目录列表
218
+ */
219
+ Index.prototype._getDirectoryList = function (norm_path) {
220
+ try {
221
+ // 使用精准模式总是获取所有目录
222
+ // 这样可以确保找到test1和test2目录
223
+ return $.dir.getAll(norm_path);
224
+ } catch (err) {
225
+ $.log.error(`[${this.type}] 检索目录失败!`, err);
226
+ return [];
227
+ }
228
+ };
229
+
230
+ /**
231
+ * 处理单个目录的配置文件
232
+ * @param {string} dir 目录路径
233
+ * @returns {Promise<void>}
234
+ */
235
+ Index.prototype._processDirectoryConfig = async function (dir) {
236
+ // 直接检查并加载demo.json文件
237
+ const config_file = `./${this.type}.json`.fullname(dir);
238
+ if (config_file && config_file.hasFile && config_file.hasFile()) {
239
+ await this.loadFile(config_file, true);
240
+ }
241
+
242
+ // 同时也检查config_demo.json文件
243
+ const config_file2 = `./config_${this.type}.json`.fullname(dir);
244
+ if (config_file2 && config_file2.hasFile && config_file2.hasFile()) {
245
+ await this.loadFile(config_file2, true);
246
+ }
247
+ };
248
+
249
+ /**
250
+ * 更新所有配置
251
+ * @param {string} search_path 检索路径
252
+ * @param {boolean} accurate 精准路径,默认为false
253
+ * @returns {Promise<void>}
254
+ */
255
+ Index.prototype.updateConfigAll = async function (search_path, accurate) {
256
+ try {
257
+ // 规范化路径
258
+ const norm_path = this._normalizeSearchPath(search_path);
259
+
260
+ // 获取目录列表
261
+ const list_scope = this._getDirectoryList(norm_path);
262
+
263
+ // 使用函数式编程处理目录
264
+ await list_scope.reduce(async (prev_promise, dir) => {
265
+ await prev_promise;
266
+ return this._processDirectoryConfig(dir);
267
+ }, Promise.resolve());
268
+ } catch (err) {
269
+ $.log.error(`[${this.type}] 更新所有配置失败: `, err);
270
+ }
271
+ };
272
+
273
+ /**
274
+ * 更新已有配置文件
275
+ * @param {string} dir_path 目录路径
276
+ * @returns {Promise<void>}
277
+ */
278
+ Index.prototype.updateConfigHave = async function (dir_path) {
279
+ const list = this.list;
280
+ await list.reduce(async (prev_promise, o) => {
281
+ await prev_promise;
282
+ return this._updateSingleConfig(o);
283
+ }, Promise.resolve());
284
+ };
285
+
286
+ /**
287
+ * 更新单个配置项
288
+ * @param {object} o 配置对象
289
+ */
290
+ Index.prototype._updateSingleConfig = async function (o) {
291
+ const file = o.filename;
292
+ if (!file) return;
293
+
294
+ try {
295
+ const config = file.loadJson();
296
+ if (!config) return;
297
+
298
+ const target_config = this._findConfig(config, o.config.name);
299
+ if (target_config) {
300
+ await o.exec('setConfig', target_config);
301
+ }
302
+ } catch (err) {
303
+ $.log.error(`[${this.type}] 更新配置失败: `, err);
304
+ }
305
+ };
306
+
307
+ /**
308
+ * 查找配置项
309
+ * @param {object | Array} config 配置对象
310
+ * @param {string} name 配置名称
311
+ * @returns {object | null} 目标配置
312
+ */
313
+ Index.prototype._findConfig = function (config, name) {
314
+ if (Array.isArray(config)) {
315
+ return config.find((cg) => cg.name === name) || null;
316
+ }
317
+ return config;
318
+ };
319
+
320
+ /**
321
+ * 更新配置
322
+ * @param {object} dir - 检索目录
323
+ * @param {boolean} accurate - 精准路径,默认为false
324
+ * @param {boolean} clear - 是否清除现有配置,默认为false
325
+ * @returns {Promise<void>}
326
+ */
327
+ Index.prototype.updateConfig = async function (dir, accurate = false, clear = false) {
328
+ try {
329
+ if (clear) {
330
+ this.clear();
331
+ await this.updateConfigAll(dir, accurate);
332
+ } else {
333
+ // 如果没有指定目录,则更新已有的配置文件
334
+ await this.updateConfigHave();
335
+ }
336
+ this.sort();
337
+ } catch (err) {
338
+ $.log.error(`[${this.type}] 配置更新失败: `, err);
339
+ }
340
+ };
341
+
342
+ /**
343
+ * 更新JS
344
+ * @returns {Promise<void>}
345
+ */
346
+ Index.prototype.updateScript = async function () {
347
+ const list = this.list;
348
+ await list.reduce(async (prev_promise, o) => {
349
+ await prev_promise;
350
+ if (o.config?.state === 1) {
351
+ return o.exec('loadScript');
352
+ }
353
+ }, Promise.resolve());
354
+ };
355
+
356
+ /**
357
+ * 更新
358
+ * @param {string} dir 检索的路径
359
+ * @param {boolean} accurate 是否精准路径,默认为false
360
+ * @param {boolean} loadJS 是否加载JS,默认为true
361
+ * @param {boolean} clear 是否清除现有配置,默认为true
362
+ * @returns {Promise<void>}
363
+ */
364
+ Index.prototype.updateMain = async function (dir, accurate = false, loadJS = true, clear = true) {
365
+ await this.updateConfig(dir, accurate, clear);
366
+ if (loadJS) {
367
+ await this.updateScript();
368
+ }
369
+ };
370
+
371
+ /**
372
+ * 更新配置
373
+ * @param {string} dir 检索的路径
374
+ * @param {boolean} accurate 精准路径
375
+ * @param {boolean} loadJS 是否加载JS
376
+ * @param {boolean} clear 是否清除缓存
377
+ */
378
+ Index.prototype.update = async function (dir, accurate = false, loadJS = true, clear = true) {
379
+ await this.updateBefore(dir);
380
+ await this.updateMain(dir, accurate, loadJS, clear);
381
+ await this.updateAfter(dir);
382
+ };
383
+
384
+ /**
385
+ * 查询配置项
386
+ * @param {string} name 名称
387
+ * @returns {object | null} 返回单项配置
388
+ */
389
+ Index.prototype.get = function (name) {
390
+ if (!name) return null;
391
+ return this.list.find((item) => item.config?.name === name) || null;
392
+ };
393
+
394
+ /**
395
+ * 设置配置项
396
+ * @param {string} name 配置项名称
397
+ * @param {object} cg 配置对象
398
+ * @returns {boolean} 是否设置成功
399
+ */
400
+ Index.prototype.set = function (name, cg) {
401
+ if (!name || !cg) return false;
402
+
403
+ const item = this.get(name);
404
+ if (item) {
405
+ // 使用Object.assign合并属性
406
+ Object.assign(item.config, cg);
407
+ return true;
408
+ }
409
+ return false;
410
+ };
411
+
412
+ /**
413
+ * 保存
414
+ * @returns {boolean} 是否保存成功
415
+ */
416
+ Index.prototype.save = async function () {
417
+ const list = this.list;
418
+ await list.reduce(async (prev_promise, o) => {
419
+ await prev_promise;
420
+ if (o.config?.state === 1) {
421
+ return o.exec('save');
422
+ }
423
+ }, Promise.resolve());
424
+ };
425
+
426
+ /**
427
+ * 添加插件
428
+ * @param {object} config 配置对象
429
+ * @returns {Promise<object | null>} 新添加的插件对象
430
+ */
431
+ Index.prototype.add = async function (config) {
432
+ if (!config || !config.name) {
433
+ $.log.error(`[${this.type}] 添加插件失败: 缺少必要的配置信息`);
434
+ return null;
435
+ }
436
+
437
+ try {
438
+ // 检查是否已存在
439
+ const existing = this.get(config.name);
440
+ if (existing) {
441
+ $.log.warn(`插件 ${config.name} 已存在`);
442
+ return existing;
443
+ }
444
+
445
+ const item = new Item(this, config);
446
+ this.list.push(item);
447
+ this.sort();
448
+ return item;
449
+ } catch (err) {
450
+ $.log.error(`[${this.type}] 添加插件失败: `, err);
451
+ return null;
452
+ }
453
+ };
454
+
455
+ /**
456
+ * 删除插件
457
+ * @param {string} name 插件名称
458
+ * @returns {Promise<boolean>} 是否删除成功
459
+ */
460
+ Index.prototype.del = async function (name) {
461
+ if (!name) return false;
462
+
463
+ const index = this.list.findIndex((item) => item.config?.name === name);
464
+ if (index === -1) return false;
465
+
466
+ try {
467
+ const item = this.list[index];
468
+ await item.exec('unload');
469
+ this.list.splice(index, 1);
470
+ return true;
471
+ } catch (err) {
472
+ $.log.error(`[${this.type}] 删除插件失败: `, err);
473
+ return false;
474
+ }
475
+ };
476
+
477
+ /**
478
+ * 加载插件
479
+ * @param {string} name 插件名称
480
+ * @returns {Promise<object | null>} 加载的插件对象
481
+ */
482
+ Index.prototype.load = async function (name) {
483
+ if (!name) return null;
484
+
485
+ const item = this.get(name);
486
+ if (!item) return null;
487
+
488
+ try {
489
+ if (item.config?.state === 1) {
490
+ await item.exec('load');
491
+ }
492
+ return item;
493
+ } catch (err) {
494
+ $.log.error(`[${this.type}] 加载插件 ${name} 失败: `, err);
495
+ return null;
496
+ }
497
+ };
498
+
499
+ /**
500
+ * 卸载插件
501
+ * @param {string} name 插件名称
502
+ * @returns {Promise<boolean>} 是否卸载成功
503
+ */
504
+ Index.prototype.unload = async function (name) {
505
+ if (!name) return false;
506
+
507
+ const item = this.get(name);
508
+ if (!item) return false;
509
+
510
+ try {
511
+ await item.exec('unload');
512
+ return true;
513
+ } catch (err) {
514
+ $.log.error(`[${this.type}] 卸载插件 ${name} 失败: `, err);
515
+ return false;
516
+ }
517
+ };
518
+
519
+ /**
520
+ * 重新加载插件
521
+ * @param {string} name 插件名称
522
+ * @returns {Promise<object | null>} 重新加载的插件对象
523
+ */
524
+ Index.prototype.reload = async function (name) {
525
+ if (!name) return null;
526
+
527
+ const item = this.get(name);
528
+ if (!item) return null;
529
+
530
+ try {
531
+ await item.exec('reload');
532
+ return item;
533
+ } catch (err) {
534
+ $.log.error(`[${this.type}] 重新加载插件 ${name} 失败: `, err);
535
+ return null;
536
+ }
537
+ };
538
+
539
+ /**
540
+ * 通过文件加载配置
541
+ * @param {string} file 文件名
542
+ * @param {boolean} create 不存在则进行创建
543
+ * @returns {Promise<object | null | string>} 加载的驱动对象、null或错误信息
544
+ */
545
+ Index.prototype.loadFile = async function (file, create = false) {
546
+ try {
547
+ const dir = file.dirname();
548
+ // 载入文件
549
+ const obj = file.loadJson();
550
+ if (obj) {
551
+ if (Array.isArray(obj)) {
552
+ // 使用函数式编程处理数组
553
+ await obj.reduce(async (prev_promise, o) => {
554
+ await prev_promise;
555
+ // 实例化一个驱动
556
+ return this.loadItem(dir, o, file);
557
+ }, Promise.resolve());
558
+ } else {
559
+ return await this.loadItem(dir, null, file);
560
+ }
561
+ } else if (create) {
562
+ return await this.loadItem(dir, null, file);
563
+ } else {
564
+ return `${file}文件不存在`;
565
+ }
566
+ return null;
567
+ } catch (err) {
568
+ $.log.error(`[${this.type}] 加载文件失败: `, err);
569
+ return `加载文件失败: ${err.message}`;
570
+ }
571
+ };
572
+
573
+ /**
574
+ * 调用函数
575
+ * @param {string} name 模块名
576
+ * @param {string} method 函数名
577
+ * @param {object} params 参数集合
578
+ * @returns {Promise<any>} 执行结果
579
+ */
580
+ Index.prototype.run = async function (name, method, ...params) {
581
+ if (name) {
582
+ const module = await this.get(name);
583
+ if (module && module.config?.state === 1) {
584
+ return await this._exec(module, method, params);
585
+ }
586
+ return null;
587
+ } else if (name === null) {
588
+ let result = null;
589
+ // 使用函数式编程处理列表,使用some模拟break逻辑
590
+ await this.list.some(async (module) => {
591
+ if (module.config?.state === 1) {
592
+ result = await this._exec(module, method, params);
593
+ // 如果结果不为空且有结束标志,则停止迭代
594
+ return result && module.config?.end;
595
+ }
596
+ return false;
597
+ });
598
+ return result;
599
+ }
600
+ return null;
601
+ };
602
+
603
+ /**
604
+ * 执行方法
605
+ * @param {string} name 插件名称
606
+ * @param {string} method 方法名称
607
+ * @param {...any} params 方法参数
608
+ * @returns {Promise<any>} 执行结果
609
+ */
610
+ Index.prototype.exec = async function (name, method, ...params) {
611
+ if (name) {
612
+ const module = await this.get(name);
613
+ if (module) {
614
+ return await this._exec(module, method, params);
615
+ }
616
+ return null;
617
+ } else if (name === null) {
618
+ let result = null;
619
+ // 使用函数式编程处理列表,使用reduce模拟break逻辑
620
+ await this.list.reduce(async (prev_promise, module) => {
621
+ await prev_promise;
622
+ // 如果已经得到结果且有结束标志,则跳过后续处理
623
+ if (result && module.config?.end) {
624
+ return;
625
+ }
626
+ const current_result = await this._exec(module, method, params);
627
+ if (current_result) {
628
+ result = current_result;
629
+ }
630
+ }, Promise.resolve());
631
+ return result;
632
+ }
633
+ return null;
634
+ };
635
+
636
+ /**
637
+ * @module 导出Index类
638
+ */
639
+ exports.Index = Index;
640
+ exports.Item = Item;