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/item.js CHANGED
@@ -1,589 +1,684 @@
1
- require('mm_logs');
2
- const util = require('util');
3
- const conf = require('mm_config');
4
- const Mod = require('mm_hot_reload');
5
-
6
-
7
- // 初始化模块管理器
8
- $.mod = $.mod || new Mod();
9
-
10
- /**
11
- * 增强require函数,支持热更新
12
- * @param {String} file 文件路径
13
- * @param {Function} func 回调函数
14
- * @returns {Object} 加载的模块
15
- */
16
- $.require = function (file, func) {
17
- return $.mod.load(file, func);
18
- };
19
-
20
- /**
21
- * 增强JSON加载函数,支持热更新
22
- * @param {String} file 文件路径
23
- * @param {Function} func 回调函数
24
- * @returns {Object} 解析的JSON对象
25
- */
26
- $.loadJson = function (file, func) {
27
- return $.mod.load(file, func);
28
- };
29
-
30
- /**
31
- * 驱动基础类
32
- * @class
33
- */
34
- class Item {
35
- /**
36
- * 构造函数
37
- * @param {String} dir 当前目录
38
- * @param {String} dir_base 模块目录
39
- * @constructor
40
- */
41
- constructor(dir, dir_base) {
42
- /**
43
- * 当前路径
44
- */
45
- this.dir = dir;
46
-
47
- /**
48
- * 默认文件
49
- */
50
- this.default_file = './sys.json';
51
-
52
- // 当前文件
53
- this.filename = null;
54
-
55
- /* 通用项 */
56
- /**
57
- * 配置参数
58
- */
59
- this.config = {
60
- /**
61
- * 名称, 由中英文和下"_"组成, 用于卸载接口 例如: demo
62
- */
63
- name: '',
64
- /**
65
- * 标题, 介绍作用
66
- */
67
- title: '',
68
- /**
69
- * 描述, 用于描述该有什么用的
70
- */
71
- description: '',
72
- /**
73
- * 文件路径, 当调用函数不存在时,会先从文件中加载
74
- */
75
- func_file: './index.js',
76
- /**
77
- * 回调函数名 用于决定调用脚本的哪个函数
78
- */
79
- func_name: '',
80
- /**
81
- * 排序
82
- */
83
- sort: 10,
84
- /**
85
- * 状态, 0表示未启用, 1表示启用
86
- */
87
- state: 1,
88
- /**
89
- * 显示, 0表示不显示, 1表示显示
90
- */
91
- show: 0
92
- };
93
-
94
- /**
95
- * 模块目录
96
- */
97
- this.dir_base = dir_base;
98
-
99
- /**
100
- * 模式
101
- * 1.生产模式,改变文件不会重新加载
102
- * 2.热更新模式,改变配置文件会重新加载配置,不重新加载脚本
103
- * 3.热重载模式,改变配置文件都会加载配置和脚本
104
- * 4.热更新+重载模式,改变配置文件重新加载配置和脚本,执行完后重新加载脚本
105
- * 5.重载模式,执行完后重新加载脚本,避免变量污染
106
- */
107
- this.mode = 1;
108
-
109
- // 加载完成
110
- this.complete = false;
111
- }
112
- }
113
-
114
- /**
115
- * 设置配置
116
- * @param {Object} config 配置对象
117
- */
118
- Item.prototype.setConfig = function (config) {
119
- if (!config) return;
120
-
121
- // 如果func_file改变,重置加载状态
122
- if (config.func_file !== this.config.func_file) {
123
- this.complete = false;
124
- }
125
-
126
- // 合并配置并应用配置处理
127
- this.config = conf(Object.assign({}, this.config, config), this.filename);
128
- };
129
-
130
- /**
131
- * 设置配置后钩子方法
132
- */
133
- Item.prototype.setConfig_after = function () {
134
- // 空实现,供子类重写
135
- };
136
-
137
- /**
138
- * 新建脚本文件
139
- * @param {String} file 目标文件路径
140
- */
141
- Item.prototype.newScript = function (file) {
142
- const templateFile = './script.js'.fullname(this.dir_base);
143
- if (templateFile.hasFile()) {
144
- templateFile.copyFile(file);
145
- }
146
- };
147
-
148
- /**
149
- * 移除模块
150
- * @param {Object|String} module 模块对象或路径
151
- */
152
- Item.prototype._remove = function (module) {
153
- if (!module) return;
154
-
155
- try {
156
- if (this.mode === 3 || this.mode === 4) {
157
- // 移除模块和监听
158
- $.mod.unload(module);
159
- } else {
160
- // 移除模块缓存
161
- const filename = require.resolve(module);
162
- if (require.cache[filename]) {
163
- require.cache[filename] = null;
164
- delete require.cache[filename];
165
- }
166
- }
167
- } catch (err) {
168
- $.log.error(`[${this.config.name}] 移除模块失败: `, err);
169
- }
170
- };
171
-
172
- /**
173
- * 卸载脚本
174
- * @param {string} file 脚本文件路径
175
- */
176
- Item.prototype.unloadScript = function (file) {
177
- if (!file) {
178
- const funcFile = this.config.func_file;
179
- if (funcFile) {
180
- file = funcFile.fullname(this.dir);
181
- }
182
- }
183
-
184
- if (file) {
185
- this._remove(file);
186
- }
187
-
188
- this.complete = false;
189
- };
190
-
191
- /**
192
- * 重新加载脚本
193
- */
194
- Item.prototype.reloadScript = function () {
195
- this.unloadScript();
196
- this.loadScript();
197
- };
198
-
199
- /**
200
- * 卸载钩子方法
201
- */
202
- Item.prototype.unload = async function () {
203
- // 空实现,供子类重写
204
- };
205
-
206
- /**
207
- * 卸载之后处理
208
- * @param {Boolean} remove 是否删除文件
209
- */
210
- Item.prototype.unload_after = async function (remove) {
211
- // 删除脚本
212
- this.unloadScript();
213
- if (remove) {
214
- this.removeFile();
215
- }
216
- };
217
-
218
- /**
219
- * 加载脚本
220
- * @param {String} file 文件路径
221
- * @param {String} name 函数名
222
- * @returns {Object|null} 返回加载的模块对象
223
- */
224
- Item.prototype.loadScript = function (file, name = '') {
225
- if (!file) {
226
- const funcFile = this.config.func_file;
227
- if (funcFile) {
228
- file = funcFile.fullname(this.dir);
229
- if (!file.hasFile()) {
230
- this.newScript(file);
231
- }
232
- } else {
233
- return null;
234
- }
235
- }
236
-
237
- if (!name) {
238
- name = this.config.func_name;
239
- }
240
-
241
- let cs;
242
- try {
243
- if (this.mode === 3 || this.mode === 4) {
244
- cs = $.require(file, (loadedModule, changeType) => {
245
- if (changeType === 'change' && loadedModule) {
246
- if (name) {
247
- if (loadedModule[name]) {
248
- this.main = loadedModule[name];
249
- }
250
- } else if ($.push) {
251
- $.push(this, loadedModule, true);
252
- }
253
- }
254
- });
255
- } else {
256
- cs = require(file);
257
- }
258
-
259
- if (cs) {
260
- if (name) {
261
- if (cs[name]) {
262
- this.main = cs[name];
263
- }
264
- } else if ($.push) {
265
- $.push(this, cs, true);
266
- }
267
- }
268
-
269
- return cs;
270
- } catch (err) {
271
- $.log.error(`[${this.config.name}] 加载脚本失败: `, err);
272
- return null;
273
- }
274
- };
275
-
276
- /**
277
- * 新建配置文件
278
- * @param {String} file 目标文件路径
279
- */
280
- Item.prototype.newConfig = function (file) {
281
- const templateFile = './config.tpl.json'.fullname(this.dir_base);
282
- templateFile.copyFile(file);
283
- };
284
-
285
- /**
286
- * 加载配置文件
287
- * @param {String} file 文件路径
288
- * @param {String} name 配置项名称
289
- * @return {Object|null} 配置对象
290
- */
291
- Item.prototype.loadFile = function (file, name) {
292
- let config = null;
293
- try {
294
- const fullPath = file.fullname(this.dir);
295
- let text = fullPath.loadText();
296
-
297
- // 如果文件不存在,创建新的配置文件
298
- if (!text) {
299
- this.newConfig(fullPath);
300
- text = fullPath.loadText();
301
- }
302
-
303
- if (text) {
304
- // 根据模式决定是否使用热更新加载
305
- if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
306
- config = $.loadJson(fullPath, function (loadedConfig, changeType) {
307
- if (changeType === 'change' && loadedConfig) {
308
- try {
309
- if (Array.isArray(loadedConfig)) {
310
- // 在数组中查找匹配的配置项
311
- const targetConfig = loadedConfig.find(item => item.name === this.config.name);
312
- if (targetConfig) {
313
- this.setConfig(targetConfig);
314
- this.setConfig_after();
315
- // 根据模式决定是否重新加载脚本
316
- if (this.mode === 3 || this.mode === 4) {
317
- this.reloadScript();
318
- }
319
- }
320
- } else {
321
- this.setConfig(loadedConfig);
322
- this.setConfig_after();
323
- if (this.mode === 3 || this.mode === 4) {
324
- this.reloadScript();
325
- }
326
- }
327
- } catch (err) {
328
- $.log.error(`[${this.config.name}] 配置热更新失败: `, err);
329
- }
330
- }
331
- }.bind(this));
332
- } else {
333
- config = fullPath.loadJson();
334
- }
335
-
336
- // 如果指定了名称且配置是数组,查找匹配项
337
- if (name && Array.isArray(config)) {
338
- config = config.find(item => item.name === name) || null;
339
- }
340
- }
341
-
342
- // 更新目录和文件名引用
343
- this.dir = fullPath.dirname();
344
- this.filename = fullPath;
345
- return config;
346
- } catch (err) {
347
- $.log.error(`[${this.config.name}] 加载配置文件失败: `, err);
348
- return null;
349
- }
350
- };
351
-
352
- /**
353
- * 删除目录
354
- */
355
- Item.prototype.delDir = function () {
356
- const funcFile = this.config.func_file;
357
- if (funcFile && $.dir && $.dir.del) {
358
- $.dir.del(this.dir);
359
- }
360
- };
361
-
362
- /**
363
- * 删除配置和脚本文件
364
- * @returns {String|null} 错误消息,如果没有错误则返回null
365
- */
366
- Item.prototype.removeFile = function () {
367
- const name = this.config.name;
368
- const file = this.filename;
369
-
370
- let errorMessage = null;
371
- try {
372
- if (file && file.hasFile) {
373
- if (file.hasFile()) {
374
- const text = file.loadText();
375
- if (text) {
376
- const config = text.toJson();
377
- if (Array.isArray(config)) {
378
- // 在数组配置中查找并删除指定项
379
- const index = config.findIndex(item => item.name === name);
380
- if (index !== -1) {
381
- this.delDir();
382
- config.splice(index, 1);
383
- }
384
- // 根据剩余配置决定保存或删除文件
385
- if (config.length > 0) {
386
- file.saveText(JSON.stringify(config, null, 4));
387
- } else if (file.delFile) {
388
- file.delFile();
389
- }
390
- } else {
391
- // 单个配置直接删除目录
392
- this.delDir();
393
- }
394
- } else {
395
- this.delDir();
396
- }
397
- } else {
398
- errorMessage = '配置文件不存在';
399
- }
400
- }
401
- } catch (err) {
402
- $.log.error(`[${this.config.name}] 删除文件失败: `, err);
403
- errorMessage = `删除失败: ${err.message}`;
404
- }
405
- return errorMessage;
406
- };
407
-
408
- /**
409
- * 载入配置
410
- * @param {Object|String} configData 配置对象或配置路径
411
- * @param {String} name 配置名称
412
- */
413
- Item.prototype.loadConfig = function (configData, name) {
414
- let config;
415
- try {
416
- if (configData) {
417
- if (typeof configData === 'string') {
418
- config = this.loadFile(configData, name);
419
- } else {
420
- config = configData;
421
- }
422
- } else {
423
- config = this.loadFile(this.filename, name);
424
- }
425
-
426
- this.setConfig(config);
427
- this.setConfig_after();
428
- } catch (err) {
429
- $.log.error(`[${this.config.name}] 载入配置失败: `, err);
430
- }
431
- };
432
-
433
- /**
434
- * 加载前处理
435
- */
436
- Item.prototype.load_before = async function () {
437
- try {
438
- const module = this.loadScript();
439
- if (module) {
440
- this.complete = true;
441
- }
442
- } catch (err) {
443
- $.log.error(`[${this.config.name}] 加载前处理失败: `, err);
444
- this.complete = false;
445
- }
446
- };
447
-
448
- /**
449
- * 加载处理
450
- */
451
- Item.prototype.load = async function () {
452
- // 空实现,供子类重写
453
- };
454
-
455
- /**
456
- * 重载配置和脚本
457
- */
458
- Item.prototype.reload = async function () {
459
- await this.run('unload');
460
- await this.run('load');
461
- };
462
-
463
- /**
464
- * 保存配置
465
- */
466
- Item.prototype.save = function () {
467
- try {
468
- if (!this.filename) return;
469
-
470
- const fullPath = this.filename.fullname(this.dir);
471
- const text = fullPath.loadText();
472
-
473
- if (text && text.trim().startsWith('[')) {
474
- // 数组格式配置
475
- const configArray = text.toJson();
476
- if (Array.isArray(configArray)) {
477
- // 查找并更新现有配置项
478
- const existingIndex = configArray.findIndex(item => item.name === this.config.name);
479
- if (existingIndex !== -1) {
480
- configArray[existingIndex] = this.config;
481
- } else {
482
- // 如果不存在,添加新配置项
483
- configArray.push(this.config);
484
- }
485
- fullPath.saveText(JSON.stringify(configArray, null, 4));
486
- return;
487
- }
488
- }
489
-
490
- // 单对象格式配置,直接保存
491
- fullPath.saveText(JSON.stringify(this.config, null, 4));
492
- } catch (err) {
493
- $.log.error(`[${this.config.name}] 保存配置失败: `, err);
494
- }
495
- };
496
-
497
- /**
498
- * 主要执行函数
499
- * @param {*} params 参数集合
500
- * @returns {Promise<any>} 执行结果
501
- */
502
- Item.prototype.main = async function (...params) {
503
- // 空实现,供子类重写
504
- return null;
505
- };
506
-
507
- /**
508
- * 运行主函数
509
- * @param {*} params 参数集合
510
- * @returns {Promise<any>} 执行结果
511
- */
512
- Item.prototype.run = async function (...params) {
513
- return await this.exec('main', ...params);
514
- };
515
-
516
- /**
517
- * 调用函数(核心执行方法)
518
- * @param {String} method 函数名
519
- * @param {*} params 参数集合
520
- * @returns {Promise<any>} 执行结果
521
- */
522
- Item.prototype.exec = async function (method, ...params) {
523
- // 优先从加载的模块中获取方法
524
- let methodFunc = this._mod && this._mod[method];
525
- // 如果模块中没有,再检查实例自身
526
- if (!methodFunc) {
527
- methodFunc = this[method];
528
- }
529
-
530
- if (!methodFunc) {
531
- return null;
532
- }
533
-
534
- let result;
535
-
536
- try {
537
- // 执行前置钩子
538
- const beforeMethod = `${method}_before`;
539
- let beforeFunc = this._mod && this._mod[beforeMethod];
540
- if (!beforeFunc) {
541
- beforeFunc = this[beforeMethod];
542
- }
543
- if (beforeFunc) {
544
- try {
545
- result = beforeFunc.call(this._mod || this, ...params);
546
- if (util.types.isPromise(result)) {
547
- result = await result;
548
- }
549
- } catch (err) {
550
- $.log.error(`[${this.config.name}] 执行前置钩子 ${beforeMethod} 失败: `, err);
551
- }
552
- }
553
-
554
- // 执行主方法
555
- const methodResult = methodFunc.call(this._mod || this, ...params);
556
- result = util.types.isPromise(methodResult) ? await methodResult : methodResult;
557
-
558
- // 执行后置钩子
559
- const afterMethod = `${method}_after`;
560
- let afterFunc = this._mod && this._mod[afterMethod];
561
- if (!afterFunc) {
562
- afterFunc = this[afterMethod];
563
- }
564
- if (afterFunc) {
565
- try {
566
- const afterResult = afterFunc.call(this._mod || this, result, ...params);
567
- if (util.types.isPromise(afterResult)) {
568
- const resolvedAfterResult = await afterResult;
569
- if (resolvedAfterResult !== undefined) {
570
- result = resolvedAfterResult;
571
- }
572
- } else if (afterResult !== undefined) {
573
- result = afterResult;
574
- }
575
- } catch (err) {
576
- $.log.error(`[${this.config.name}] 执行后置钩子 ${afterMethod} 失败: `, err);
577
- }
578
- }
579
- } catch (err) {
580
- $.log.error(`[${this.config.name}] 执行方法 ${method} 失败: `, err);
581
- return null;
582
- }
583
- return result;
584
- };
585
-
586
- /**
587
- * @module 导出Item类
588
- */
589
- module.exports = Item;
1
+ const util = require('util');
2
+ const { conf } = require('mm_config');
3
+ const { HotReload } = require('mm_hot_reload');
4
+
5
+ // 初始化全局对象
6
+ if (typeof global.$ === 'undefined') {
7
+ global.$ = {};
8
+ }
9
+
10
+ if (!$.mod) {
11
+ $.mod = new HotReload();
12
+ }
13
+
14
+ /**
15
+ * 增强require函数,支持热更新
16
+ * @param {string} file 文件路径
17
+ * @param {Function} func 回调函数
18
+ * @returns {object} 加载的模块
19
+ */
20
+ $.require = function (file, func) {
21
+ return $.mod.load(file, func);
22
+ };
23
+
24
+ /**
25
+ * 增强JSON加载函数,支持热更新
26
+ * @param {string} file 文件路径
27
+ * @param {Function} func 回调函数
28
+ * @returns {object} 解析的JSON对象
29
+ */
30
+ $.loadJson = function (file, func) {
31
+ return $.mod.load(file, func);
32
+ };
33
+
34
+ /**
35
+ * 驱动基础类
36
+ * @class
37
+ */
38
+ class Item {
39
+ /**
40
+ * 构造函数
41
+ * @param {string} dir 当前目录
42
+ * @param {string} dir_base 模块目录
43
+ * @class
44
+ */
45
+ constructor(dir, dir_base) {
46
+ // 当前路径
47
+ this.dir = dir;
48
+
49
+ // 默认文件
50
+ this.default_file = './sys.json';
51
+
52
+ // 当前文件
53
+ this.filename = null;
54
+
55
+ // 配置参数
56
+ this.config = {
57
+ // 名称, 由中英文和下"_"组成, 用于卸载接口 例如: demo
58
+ name: '',
59
+ // 标题, 介绍作用
60
+ title: '',
61
+ // 描述, 用于描述该有什么用的
62
+ description: '',
63
+ // 文件路径, 当调用函数不存在时,会先从文件中加载
64
+ func_file: './index.js',
65
+ // 回调函数名 用于决定调用脚本的哪个函数
66
+ func_name: '',
67
+ // 排序
68
+ sort: 10,
69
+ // 状态, 0表示未启用, 1表示启用
70
+ state: 1,
71
+ // 显示, 0表示不显示, 1表示显示
72
+ show: 0
73
+ };
74
+
75
+ // 模块目录
76
+ this.dir_base = dir_base;
77
+
78
+ // 模式: 1.生产模式 2.热更新模式 3.热重载模式 4.热更新+重载模式 5.重载模式
79
+ this.mode = 1;
80
+
81
+ // 加载完成
82
+ this.complete = false;
83
+
84
+ this._logger = $.log || console;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * 日志输出
90
+ * @param {string} level - 日志级别(debug/info/warn/error)
91
+ * @param {string} message - 日志消息
92
+ * @param {...*} args - 可选参数
93
+ */
94
+ Item.prototype.log = function (level, message, ...args) {
95
+ this._logger[level](`[${this.config.name}] ${message}`, ...args);
96
+ };
97
+
98
+ /**
99
+ * 设置配置
100
+ * @param {object} config 配置对象
101
+ * @returns {Promise<void>}
102
+ */
103
+ Item.prototype.setConfig = async function (config) {
104
+ if (!config) return;
105
+
106
+ // 如果func_file改变,重置加载状态
107
+ if (config.func_file !== this.config.func_file) {
108
+ this.complete = false;
109
+ }
110
+
111
+ // 合并配置并应用配置处理
112
+ const result = await conf({ ...this.config, ...config }, this.filename);
113
+ this.config = result;
114
+ };
115
+
116
+ /**
117
+ * 设置配置后钩子方法
118
+ */
119
+ Item.prototype.setConfigAfter = function () {
120
+ // 空实现,供子类重写
121
+ };
122
+
123
+ /**
124
+ * 新建脚本文件
125
+ * @param {string} file 目标文件路径
126
+ */
127
+ Item.prototype.newScript = function (file) {
128
+ const template_file = './script.js'.fullname(this.dir_base);
129
+ if (template_file.hasFile()) {
130
+ template_file.copyFile(file);
131
+ }
132
+ };
133
+
134
+ /**
135
+ * 移除模块
136
+ * @param {object | string} module 模块对象或路径
137
+ */
138
+ Item.prototype._remove = function (module) {
139
+ if (!module) return;
140
+
141
+ try {
142
+ if (this.mode === 3 || this.mode === 4) {
143
+ // 移除模块和监听
144
+ $.mod.unload(module);
145
+ } else {
146
+ // 移除模块缓存
147
+ const filename = require.resolve(module);
148
+ if (require.cache[filename]) {
149
+ require.cache[filename] = null;
150
+ delete require.cache[filename];
151
+ }
152
+ }
153
+ } catch (err) {
154
+ this.log('error', `移除模块失败: `, err);
155
+ }
156
+ };
157
+
158
+ /**
159
+ * 卸载脚本
160
+ * @param {string} file 脚本文件路径
161
+ */
162
+ Item.prototype.unloadScript = function (file) {
163
+ let target_file = file;
164
+ if (!target_file) {
165
+ const func_file = this.config.func_file;
166
+ if (func_file) {
167
+ target_file = func_file.fullname(this.dir);
168
+ }
169
+ }
170
+
171
+ if (target_file) {
172
+ this._remove(target_file);
173
+ }
174
+
175
+ this.complete = false;
176
+ };
177
+
178
+ /**
179
+ * 重新加载脚本
180
+ */
181
+ Item.prototype.reloadScript = function () {
182
+ this.unloadScript();
183
+ this.loadScript();
184
+ };
185
+
186
+ /**
187
+ * 卸载钩子方法
188
+ */
189
+ Item.prototype.unload = async function () {
190
+ // 空实现,供子类重写
191
+ };
192
+
193
+ /**
194
+ * 卸载之后处理
195
+ * @param {boolean} remove 是否删除文件
196
+ */
197
+ Item.prototype.unloadAfter = async function (remove) {
198
+ // 删除脚本
199
+ this.unloadScript();
200
+ if (remove) {
201
+ this.removeFile();
202
+ }
203
+ };
204
+
205
+ /**
206
+ * 加载脚本
207
+ * @param {string} file 文件路径
208
+ * @param {string} name 函数名
209
+ * @returns {object | null} 返回加载的模块对象
210
+ */
211
+ Item.prototype.loadScript = function (file, name = '') {
212
+ const target_file = this._getScriptFile(file);
213
+ if (!target_file) return null;
214
+
215
+ const target_name = this._getScriptName(name);
216
+
217
+ try {
218
+ const cs = this._loadScript(target_file, target_name);
219
+ this._setMainMethod(cs, target_name);
220
+ return cs;
221
+ } catch (err) {
222
+ this.log('error', `加载脚本失败: `, err);
223
+ return null;
224
+ }
225
+ };
226
+
227
+ /**
228
+ * 获取脚本文件路径
229
+ * @param {string} file 文件路径
230
+ * @returns {string | null} 完整文件路径
231
+ */
232
+ Item.prototype._getScriptFile = function (file) {
233
+ if (file) return file;
234
+
235
+ const func_file = this.config.func_file;
236
+ if (!func_file) return null;
237
+
238
+ const target_file = func_file.fullname(this.dir);
239
+ if (!target_file.hasFile()) {
240
+ this.newScript(target_file);
241
+ }
242
+
243
+ return target_file;
244
+ };
245
+
246
+ /**
247
+ * 获取脚本函数名
248
+ * @param {string} name 函数名
249
+ * @returns {string} 函数名
250
+ */
251
+ Item.prototype._getScriptName = function (name) {
252
+ return name || this.config.func_name;
253
+ };
254
+
255
+ /**
256
+ * 加载脚本模块
257
+ * @param {string} file 文件路径
258
+ * @param {string} name 函数名
259
+ * @returns {object | null} 模块对象
260
+ */
261
+ Item.prototype._loadScript = function (file, name) {
262
+ if (this.mode === 3 || this.mode === 4) {
263
+ return $.require(file, this._handleHotReload.bind(this, name));
264
+ }
265
+ return require(file);
266
+ };
267
+
268
+ /**
269
+ * 处理热重载回调
270
+ * @param {string} name 函数名
271
+ * @param {object} loaded_module 加载的模块
272
+ * @param {string} change_type 变更类型
273
+ */
274
+ Item.prototype._handleHotReload = function (name, loaded_module, change_type) {
275
+ if (change_type === 'change' && loaded_module) {
276
+ this._setMainMethod(loaded_module, name);
277
+ }
278
+ };
279
+
280
+ /**
281
+ * 设置主方法
282
+ * @param {object} module 模块对象
283
+ * @param {string} name 函数名
284
+ */
285
+ Item.prototype._setMainMethod = function (module, name) {
286
+ if (!module) return;
287
+
288
+ if (name) {
289
+ if (module[name]) {
290
+ this.main = module[name];
291
+ }
292
+ } else if ($.push) {
293
+ $.push(this, module, true);
294
+ }
295
+ };
296
+
297
+ /**
298
+ * 新建配置文件
299
+ * @param {string} file 目标文件路径
300
+ */
301
+ Item.prototype.newConfig = function (file) {
302
+ const template_file = './config.tpl.json'.fullname(this.dir_base);
303
+ template_file.copyFile(file);
304
+ };
305
+
306
+ /**
307
+ * 加载配置文件
308
+ * @param {string} file 文件路径
309
+ * @param {string} name 配置项名称
310
+ * @returns {object | null} 配置对象
311
+ */
312
+ Item.prototype.loadFile = function (file, name) {
313
+ try {
314
+ const full_path = file.fullname(this.dir);
315
+ let text = full_path.loadText();
316
+
317
+ // 如果文件不存在,创建新的配置文件
318
+ if (!text) {
319
+ this.newConfig(full_path);
320
+ text = full_path.loadText();
321
+ }
322
+
323
+ if (!text) return null;
324
+
325
+ const config = this._loadHotReload(full_path);
326
+ const final_config = this._findConfigByName(config, name);
327
+
328
+ // 更新目录和文件名引用
329
+ this.dir = full_path.dirname();
330
+ this.filename = full_path;
331
+ return final_config;
332
+ } catch (err) {
333
+ this.log('error', `加载配置文件失败: `, err);
334
+ return null;
335
+ }
336
+ };
337
+
338
+ /**
339
+ * 根据模式加载配置(支持热更新)
340
+ * @param {string} full_path 完整文件路径
341
+ * @returns {object | Array | null} 配置对象
342
+ */
343
+ Item.prototype._loadHotReload = function (full_path) {
344
+ if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
345
+ return $.loadJson(full_path, this._handleHotReload.bind(this));
346
+ }
347
+ return full_path.loadJson();
348
+ };
349
+
350
+ /**
351
+ * 处理热更新回调
352
+ * @param {object | Array} loaded_config 加载的配置
353
+ * @param {string} change_type 变更类型
354
+ */
355
+ Item.prototype._handleHotReload = function (loaded_config, change_type) {
356
+ if (change_type !== 'change' || !loaded_config) return;
357
+
358
+ try {
359
+ const target_config = this._findConfigByName(loaded_config, this.config.name);
360
+ if (target_config) {
361
+ this.setConfig(target_config);
362
+ this.setConfigAfter();
363
+ this._reloadIfNeeded();
364
+ }
365
+ } catch (err) {
366
+ this.log('error', `配置热更新失败: `, err);
367
+ }
368
+ };
369
+
370
+ /**
371
+ * 根据名称在配置数组中查找配置项
372
+ * @param {object | Array} config 配置对象或数组
373
+ * @param {string} name 配置项名称
374
+ * @returns {object | null} 匹配的配置项
375
+ */
376
+ Item.prototype._findConfigByName = function (config, name) {
377
+ if (!name || !Array.isArray(config)) return config;
378
+ return config.find((item) => item.name === name) || null;
379
+ };
380
+
381
+ /**
382
+ * 根据模式决定是否重新加载脚本
383
+ */
384
+ Item.prototype._reloadIfNeeded = function () {
385
+ if (this.mode === 3 || this.mode === 4) {
386
+ this.reloadScript();
387
+ }
388
+ };
389
+
390
+ /**
391
+ * 删除目录
392
+ */
393
+ Item.prototype.delDir = function () {
394
+ const func_file = this.config.func_file;
395
+ if (func_file && $.dir && $.dir.del) {
396
+ $.dir.del(this.dir);
397
+ }
398
+ };
399
+
400
+ /**
401
+ * 删除配置和脚本文件
402
+ * @returns {string | null} 错误消息,如果没有错误则返回null
403
+ */
404
+ Item.prototype.removeFile = function () {
405
+ const name = this.config.name;
406
+ const file = this.filename;
407
+
408
+ let error_message = null;
409
+ try {
410
+ if (!file || !file.hasFile) return null;
411
+
412
+ if (!file.hasFile()) {
413
+ return '配置文件不存在';
414
+ }
415
+
416
+ error_message = this._removeConfigFile(file, name);
417
+ } catch (err) {
418
+ this.log('error', `删除文件失败: `, err);
419
+ error_message = `删除失败: ${err.message}`;
420
+ }
421
+ return error_message;
422
+ };
423
+
424
+ /**
425
+ * 删除配置文件
426
+ * @param {object} file 文件对象
427
+ * @param {string} name 配置名称
428
+ * @returns {string | null} 错误消息
429
+ */
430
+ Item.prototype._removeConfigFile = function (file, name) {
431
+ const text = file.loadText();
432
+ if (!text) {
433
+ this.delDir();
434
+ return null;
435
+ }
436
+
437
+ const config = text.toJson();
438
+ if (Array.isArray(config)) {
439
+ return this._removeArrayItem(file, config, name);
440
+ }
441
+
442
+ this.delDir();
443
+ return null;
444
+ };
445
+
446
+ /**
447
+ * 从数组配置中删除指定项
448
+ * @param {object} file 文件对象
449
+ * @param {Array} config 配置数组
450
+ * @param {string} name 配置名称
451
+ * @returns {string | null} 错误消息
452
+ */
453
+ Item.prototype._removeArrayItem = function (file, config, name) {
454
+ const index = config.findIndex((item) => item.name === name);
455
+ if (index === -1) return null;
456
+
457
+ this.delDir();
458
+ config.splice(index, 1);
459
+
460
+ if (config.length > 0) {
461
+ file.saveText(JSON.stringify(config, null, 4));
462
+ } else if (file.delFile) {
463
+ file.delFile();
464
+ }
465
+
466
+ return null;
467
+ };
468
+
469
+ /**
470
+ * 载入配置
471
+ * @param {object | string} config_data 配置对象或配置路径
472
+ * @param {string} name 配置名称
473
+ */
474
+ Item.prototype.loadConfig = async function (config_data, name) {
475
+ let config;
476
+ try {
477
+ if (config_data) {
478
+ if (typeof config_data === 'string') {
479
+ config = this.loadFile(config_data, name);
480
+ } else {
481
+ config = config_data;
482
+ }
483
+ } else {
484
+ config = this.loadFile(this.filename, name);
485
+ }
486
+
487
+ await this.setConfig(config);
488
+ this.setConfigAfter();
489
+ } catch (err) {
490
+ this.log('error', `载入配置失败: `, err);
491
+ }
492
+ };
493
+
494
+ /**
495
+ * 加载前处理
496
+ */
497
+ Item.prototype.loadBefore = async function () {
498
+ try {
499
+ const module = this.loadScript();
500
+ if (module) {
501
+ this.complete = true;
502
+ }
503
+ } catch (err) {
504
+ this.log('error', `加载前处理失败: `, err);
505
+ this.complete = false;
506
+ }
507
+ };
508
+
509
+ /**
510
+ * 加载处理
511
+ */
512
+ Item.prototype.load = async function () {
513
+ // 空实现,供子类重写
514
+ };
515
+
516
+ /**
517
+ * 重载配置和脚本
518
+ */
519
+ Item.prototype.reload = async function () {
520
+ await this.run('unload');
521
+ await this.run('load');
522
+ };
523
+
524
+ /**
525
+ * 保存配置
526
+ */
527
+ Item.prototype.save = function () {
528
+ try {
529
+ if (!this.filename) return;
530
+
531
+ const full_path = this.filename.fullname(this.dir);
532
+ const text = full_path.loadText();
533
+
534
+ if (text && text.trim().startsWith('[')) {
535
+ // 数组格式配置
536
+ const config_array = text.toJson();
537
+ if (Array.isArray(config_array)) {
538
+ // 查找并更新现有配置项
539
+ const existing_index = config_array.findIndex((item) => item.name === this.config.name);
540
+ if (existing_index !== -1) {
541
+ config_array[existing_index] = this.config;
542
+ } else {
543
+ // 如果不存在,添加新配置项
544
+ config_array.push(this.config);
545
+ }
546
+ full_path.saveText(JSON.stringify(config_array, null, 4));
547
+ return;
548
+ }
549
+ }
550
+
551
+ // 单对象格式配置,直接保存
552
+ full_path.saveText(JSON.stringify(this.config, null, 4));
553
+ } catch (err) {
554
+ this.log('error', `保存配置失败: `, err);
555
+ }
556
+ };
557
+
558
+ /**
559
+ * 主要执行函数
560
+ * @param {*} _params 参数集合
561
+ * @returns {Promise<any>} 执行结果
562
+ */
563
+ Item.prototype.main = async function (..._params) {
564
+ // 空实现,供子类重写
565
+ return null;
566
+ };
567
+
568
+ /**
569
+ * 运行主函数
570
+ * @param {*} params 参数集合
571
+ * @returns {Promise<any>} 执行结果
572
+ */
573
+ Item.prototype.run = async function (...params) {
574
+ return await this.exec('main', ...params);
575
+ };
576
+
577
+ /**
578
+ * 调用函数(核心执行方法)
579
+ * @param {string} method 函数名
580
+ * @param {*} params 参数集合
581
+ * @returns {Promise<any>} 执行结果
582
+ */
583
+ Item.prototype.exec = async function (method, ...params) {
584
+ const method_func = this._getMethod(method);
585
+ if (!method_func) return null;
586
+
587
+ let result;
588
+
589
+ try {
590
+ result = await this._execBefore(method, params);
591
+ result = await this._execMain(method_func, params, result);
592
+ result = await this._execAfter(method, params, result);
593
+ } catch (err) {
594
+ this.log('error', `执行方法 ${method} 失败: `, err);
595
+ return null;
596
+ }
597
+
598
+ return result;
599
+ };
600
+
601
+ /**
602
+ * 获取方法函数
603
+ * @param {string} method 方法名
604
+ * @returns {Function|null} 方法函数
605
+ */
606
+ Item.prototype._getMethod = function (method) {
607
+ let method_func = this._mod && this._mod[method];
608
+ if (!method_func) {
609
+ method_func = this[method];
610
+ }
611
+ return method_func || null;
612
+ };
613
+
614
+ /**
615
+ * 执行前置钩子
616
+ * @param {string} method 方法名
617
+ * @param {Array} params 参数
618
+ * @returns {Promise<any>} 前置钩子结果
619
+ */
620
+ Item.prototype._execBefore = async function (method, params) {
621
+ const before_method = `${method}Before`;
622
+ const before_func = this._getMethod(before_method);
623
+
624
+ if (!before_func) return undefined;
625
+
626
+ try {
627
+ let result = before_func.call(this._mod || this, ...params);
628
+ if (util.types.isPromise(result)) {
629
+ result = await result;
630
+ }
631
+ return result;
632
+ } catch (err) {
633
+ this.log('error', `执行前置钩子 ${before_method} 失败: `, err);
634
+ return undefined;
635
+ }
636
+ };
637
+
638
+ /**
639
+ * 执行主方法
640
+ * @param {Function} method_func 方法函数
641
+ * @param {Array} params 参数
642
+ * @param {any} before_result 前置钩子结果
643
+ * @returns {Promise<any>} 主方法结果
644
+ */
645
+ Item.prototype._execMain = async function (method_func, params, before_result) {
646
+ const method_result = method_func.call(this._mod || this, ...params);
647
+ const result = util.types.isPromise(method_result) ? await method_result : method_result;
648
+ return result;
649
+ };
650
+
651
+ /**
652
+ * 执行后置钩子
653
+ * @param {string} method 方法名
654
+ * @param {Array} params 参数
655
+ * @param {any} main_result 主方法结果
656
+ * @returns {Promise<any>} 后置钩子结果
657
+ */
658
+ Item.prototype._execAfter = async function (method, params, main_result) {
659
+ const after_method = `${method}After`;
660
+ const after_func = this._getMethod(after_method);
661
+
662
+ if (!after_func) return main_result;
663
+
664
+ try {
665
+ const after_result = after_func.call(this._mod || this, main_result, ...params);
666
+
667
+ if (util.types.isPromise(after_result)) {
668
+ const resolved_result = await after_result;
669
+ return resolved_result !== undefined ? resolved_result : main_result;
670
+ }
671
+
672
+ return after_result !== undefined ? after_result : main_result;
673
+ } catch (err) {
674
+ this.log('error', `执行后置钩子 ${after_method} 失败: `, err);
675
+ return main_result;
676
+ }
677
+ };
678
+
679
+ /**
680
+ * @module 导出Item类
681
+ */
682
+ module.exports = {
683
+ Item
684
+ };