const { readFileSync, writeFileSync, existsSync, readdirSync } = require('fs') const { resolve } = require('path') const parser = require('@babel/parser') const traverse = require('@babel/traverse').default const { parse } = require('@vue/compiler-sfc') const AstAnalyzer = require('./ast-analyzer') /** * 触发器分析模块 * 负责分析组件中的按钮和事件触发器 */ class TriggerAnalyzer { constructor() { this.astAnalyzer = new AstAnalyzer() } /** * 查找API方法的触发器源 - 第一步:第一层触发源分析 * @param {Array} apiMappings - API映射数组 * @param {Array} routes - 路由配置数组 * @returns {Array} 增强的API映射数组 */ findTriggerSourcesForApiMappings(apiMappings, routes) { const enhancedApiMappings = [] // 创建组件到authType的映射 const componentAuthMap = this.createComponentAuthMap(routes) apiMappings.forEach(moduleMapping => { const enhancedModuleMapping = { ...moduleMapping, apiMappings: [] } moduleMapping.apiMappings.forEach(apiMapping => { // 第一步:查找第一层触发源 const triggerSources = this.findFirstLevelTriggerSources( moduleMapping.serviceName, // 使用第一层的serviceName apiMapping.apiMethodName, moduleMapping.module ) // 过滤掉authType为public的组件的触发源 const filteredTriggerSources = this.filterPublicComponentTriggers( triggerSources, componentAuthMap ) // 只有当有触发源时才添加API映射 if (filteredTriggerSources.length > 0) { enhancedModuleMapping.apiMappings.push({ ...apiMapping, triggerSources: filteredTriggerSources }) } }) // 只有当模块有API映射时才添加模块映射 if (enhancedModuleMapping.apiMappings.length > 0) { enhancedApiMappings.push(enhancedModuleMapping) } }) return enhancedApiMappings } /** * 查找第一层触发源 - 使用Babel分析真正的调用关系 * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @param {string} moduleName - 模块名称 * @returns {Array} 触发器源数组 */ findFirstLevelTriggerSources(serviceName, apiMethodName, moduleName) { const triggerSources = [] try { // 1. 遍历模块中的所有Vue组件文件 const modulePath = resolve(__dirname, `../../src/renderer/modules/${moduleName}`) if (existsSync(modulePath)) { const files = readdirSync(modulePath, { recursive: true }) files.forEach(file => { if (typeof file === 'string' && file.endsWith('.vue')) { const filePath = resolve(modulePath, file) const componentName = this.extractComponentNameFromPath(file) // 2. 使用Babel分析Vue组件中的API调用 const componentTriggerSources = this.analyzeComponentForApiCalls( filePath, serviceName, apiMethodName, componentName ) triggerSources.push(...componentTriggerSources) } }) } } catch (error) { } // 去重 const uniqueTriggerSources = this.deduplicateTriggerSources(triggerSources) return uniqueTriggerSources } /** * 查找API方法的触发器源(保留原方法用于后续扩展) * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @param {string} moduleName - 模块名称 * @returns {Array} 触发器源数组 */ findTriggerSourcesForApiMethod(serviceName, apiMethodName, moduleName) { const triggerSources = [] // 1. 检查路由组件 const routeComponents = this.getRouteComponents(moduleName) routeComponents.forEach(route => { const triggerAnalysis = this.analyzeComponentWithAST( route.component, serviceName, apiMethodName ) if (triggerAnalysis.triggerSources.length > 0) { triggerSources.push(...triggerAnalysis.triggerSources) } }) // 2. 检查模块中的所有组件 const componentsInModule = this.getModuleComponents(moduleName) componentsInModule.forEach(componentName => { const triggerAnalysis = this.analyzeComponentWithAST( componentName, serviceName, apiMethodName ) if (triggerAnalysis.triggerSources.length > 0) { triggerSources.push(...triggerAnalysis.triggerSources) } }) // 去重 const uniqueTriggerSources = this.deduplicateTriggerSources(triggerSources) return uniqueTriggerSources } /** * 获取路由组件 * @param {string} moduleName - 模块名称 * @returns {Array} 路由组件数组 */ getRouteComponents(moduleName) { const routeComponents = [] try { const routeConfigPath = resolve(__dirname, '../../src/renderer/router/index.js') if (existsSync(routeConfigPath)) { const routeContent = readFileSync(routeConfigPath, 'utf-8') // 根据模块名称映射到对应的组件名称 const moduleToComponentMap = { 'role-management': 'RoleManagement', 'user-management': 'UserManagement', 'system-settings': 'Settings', 'user-profile': 'UserProfile' } const componentName = moduleToComponentMap[moduleName] if (componentName) { // 查找包含该组件的路由 const routeMatches = routeContent.match(new RegExp(`component:\\s*${componentName}`, 'g')) if (routeMatches) { routeComponents.push({ component: componentName }) } } } } catch (error) { // 静默处理错误 } return routeComponents } /** * 获取模块中的所有组件 * @param {string} moduleName - 模块名称 * @returns {Array} 组件名称数组 */ getModuleComponents(moduleName) { const components = [] try { const modulePath = resolve(__dirname, '../../src/renderer/modules', moduleName) if (existsSync(modulePath)) { // 查找views目录 const viewsPath = resolve(modulePath, 'views') if (existsSync(viewsPath)) { const viewFiles = readdirSync(viewsPath).filter(file => file.endsWith('.vue')) viewFiles.forEach(file => { components.push(file.replace('.vue', '')) }) } // 查找components目录 const componentsPath = resolve(modulePath, 'components') if (existsSync(componentsPath)) { const componentFiles = readdirSync(componentsPath).filter(file => file.endsWith('.vue')) componentFiles.forEach(file => { components.push(file.replace('.vue', '')) }) } } } catch (error) { // 静默处理错误 } return components } /** * 使用AST分析组件 * @param {string} componentName - 组件名称 * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @returns {Object} 触发器分析结果 */ analyzeComponentWithAST(componentName, serviceName, apiMethodName) { const triggerSources = [] try { const filePath = this.findComponentFile(componentName) if (!filePath) { return { triggerSources: [] } } const content = readFileSync(filePath, 'utf-8') // 1. 检查组件的authType属性,如果是public则跳过 const authTypeMatch = content.match(/authType\s*[=:]\s*["']([^"']+)["']/) if (authTypeMatch && authTypeMatch[1] === 'public') { return { triggerSources: [] } } // 2. 检查组件是否包含目标API调用 if (!content.includes(`${serviceName}.${apiMethodName}`)) { return { triggerSources: [] } } // 3. 使用Babel AST分析找到调用该API的方法 const callingMethods = this.astAnalyzer.findMethodsCallingServiceWithBabel(content, serviceName, apiMethodName) if (callingMethods.length > 0) { // 4. 分析每个调用方法的触发源 const methodTriggers = this.analyzeMethodTriggerSources(content, componentName, callingMethods, filePath) triggerSources.push(...methodTriggers) } else { // 如果没有找到按钮触发器,记录页面级触发器 triggerSources.push({ component: componentName, triggerName: null, triggerType: 'page' }) } } catch (error) { // 静默处理错误 } return { triggerSources } } /** * 查找组件文件 * @param {string} componentName - 组件名称 * @returns {string|null} 组件文件路径 */ findComponentFile(componentName) { const possiblePaths = [ resolve(__dirname, '../../src/renderer/modules', componentName, 'views', `${componentName}.vue`), resolve(__dirname, '../../src/renderer/modules', componentName, 'components', `${componentName}.vue`), resolve(__dirname, '../../src/renderer/components', `${componentName}.vue`), resolve(__dirname, '../../src/renderer/views', `${componentName}.vue`) ] for (const path of possiblePaths) { if (existsSync(path)) { return path } } return null } /** * 分析方法的触发源 * @param {string} content - 组件内容 * @param {string} componentName - 组件名称 * @param {Array} methodNames - 方法名数组 * @param {string} filePath - 文件路径 * @returns {Array} 触发器源数组 */ analyzeMethodTriggerSources(content, componentName, methodNames, filePath = null) { const triggerSources = [] // 提取模板部分 const templateMatch = content.match(/