From 077eb3a7cd2c07a4dea78b78b54a6b863fae3596 Mon Sep 17 00:00:00 2001 From: snowgitea Date: Wed, 1 Jul 2026 17:57:04 +0800 Subject: [PATCH] chore: src/modules/plugin/service/center.ts --- src/modules/plugin/service/center.ts | 224 +++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 src/modules/plugin/service/center.ts diff --git a/src/modules/plugin/service/center.ts b/src/modules/plugin/service/center.ts new file mode 100644 index 0000000..3a65bf9 --- /dev/null +++ b/src/modules/plugin/service/center.ts @@ -0,0 +1,224 @@ +import { + App, + IMidwayApplication, + Inject, + InjectClient, + Scope, + Provide, + ScopeEnum, +} from '@midwayjs/core'; +import * as fs from 'fs'; +import * as path from 'path'; +import { PluginInfoEntity } from '../entity/info'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { PluginInfo } from '../interface'; +import * as _ from 'lodash'; +import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager'; +import { CoolEventManager } from '@cool-midway/core'; +import { PluginService } from './info'; + +export const PLUGIN_CACHE_KEY = 'plugin:init'; + +export const EVENT_PLUGIN_READY = 'EVENT_PLUGIN_READY'; + +/** + * 插件中心 + */ +@Provide() +@Scope(ScopeEnum.Singleton) +export class PluginCenterService { + // 插件列表 + plugins: Map = new Map(); + + // 插件配置 + pluginInfos: Map = new Map(); + + @App() + app: IMidwayApplication; + + @InjectEntityModel(PluginInfoEntity) + pluginInfoEntity: Repository; + + @InjectClient(CachingFactory, 'default') + midwayCache: MidwayCache; + + @Inject() + coolEventManager: CoolEventManager; + + @Inject() + pluginService: PluginService; + + /** + * 初始化 + * @returns + */ + async init() { + this.plugins.clear(); + await this.initHooks(); + await this.initPlugin(); + this.coolEventManager.emit(EVENT_PLUGIN_READY); + } + + /** + * 初始化一个 + * @param keyName key名 + */ + async initOne(keyName: string) { + await this.initPlugin({ + keyName, + }); + this.coolEventManager.emit(EVENT_PLUGIN_READY, keyName); + } + + /** + * 移除插件 + * @param keyName + * @param isHook + */ + async remove(keyName: string, isHook = false) { + this.plugins.delete(keyName); + this.pluginInfos.delete(keyName); + if (isHook) { + await this.initHooks(); + } + } + + /** + * 注册插件 + * @param key 唯一标识 + * @param cls 类 + * @param pluginInfo 插件信息 + */ + async register(key: string, cls: any, pluginInfo?: PluginInfo) { + // 单例插件 + if (pluginInfo?.singleton) { + const instance = new cls(); + await instance.init(this.pluginInfos.get(key), null, this.app, { + cache: this.midwayCache, + pluginService: this.pluginService, + }); + this.plugins.set(key, instance); + } else { + // 普通插件 + this.plugins.set(key, cls); + } + } + + /** + * 初始化钩子 + */ + async initHooks() { + const hooksPath = path.join( + this.app.getBaseDir(), + 'modules', + 'plugin', + 'hooks' + ); + for (const key of fs.readdirSync(hooksPath)) { + const stat = fs.statSync(path.join(hooksPath, key)); + if (!stat.isDirectory()) { + continue; + } + const { Plugin } = await import(path.join(hooksPath, key, 'index')); + await this.register(key, Plugin); + this.pluginInfos.set(key, { + name: key, + config: this.app.getConfig('module.plugin.hooks.' + key), + }); + } + } + + /** + * 初始化插件 + * @param condition 插件条件 + */ + async initPlugin(condition?: { + hook?: string; + id?: number; + keyName?: string; + }) { + let find: any = { status: 1 }; + if (condition) { + find = { + ...find, + ...condition, + }; + } + const plugins = await this.pluginInfoEntity.find({ + where: find, + select: [ + 'id', + 'name', + 'description', + 'keyName', + 'hook', + 'version', + 'pluginJson', + 'config', + ], + }); + for (const plugin of plugins) { + const data = await this.pluginService.getData(plugin.keyName); + if (!data) { + continue; + } + const instance = await this.getInstance(data.content.data); + const pluginInfo = { + ...plugin.pluginJson, + config: this.getConfig(plugin.config), + }; + if (plugin.hook) { + this.pluginInfos.set(plugin.hook, pluginInfo); + await this.register(plugin.hook, instance, pluginInfo); + } else { + this.pluginInfos.set(plugin.keyName, pluginInfo); + await this.register(plugin.keyName, instance, pluginInfo); + } + } + } + + /** + * 获得配置 + * @param config + * @returns + */ + private getConfig(config: any) { + // 处理配置为字符串的情况 + if (typeof config === 'string') { + try { + config = JSON.parse(config); + } catch (e) { + return {}; + } + } + // 如果配置为空或非对象类型,则返回空对象 + if (!config || typeof config !== 'object') { + return {}; + } + const env = this.app.getEnv(); + let isMulti = false; + for (const key in config) { + if (key.includes('@')) { + isMulti = true; + break; + } + } + return isMulti ? config[`@${env}`] : config; + } + + /** + * 获得实例 + * @param content + * @returns + */ + async getInstance(content: string) { + let _instance; + const script = ` + ${content} + _instance = Plugin; + `; + eval(script); + return _instance; + } +}