Browse Source

关系也找好了

master
hejl 10 hours ago
parent
commit
87b85f1b0c
  1. 23
      gofaster/app/plugins/modules/api-collector.js
  2. 465
      gofaster/app/plugins/modules/component-relationship-analyzer-ast.js
  3. 280
      gofaster/app/plugins/modules/component-relationship-analyzer.js
  4. 28
      gofaster/app/plugins/modules/data-cleaner.js
  5. 61
      gofaster/app/plugins/modules/file-generator.js
  6. 185
      gofaster/app/plugins/modules/route-analyzer.js
  7. 9
      gofaster/app/plugins/modules/trigger-analyzer-simple.js
  8. 10
      gofaster/app/plugins/route-mapping-plugin.js
  9. 402
      gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js

23
gofaster/app/plugins/modules/api-collector.js

@ -53,26 +53,20 @@ class ApiCollector { @@ -53,26 +53,20 @@ class ApiCollector {
collectApiMappings(moduleDirs) {
this.apiMappings = []
console.log('🔍 开始收集API映射,模块列表:', moduleDirs)
moduleDirs.forEach(moduleName => {
console.log(`🔍 处理模块: ${moduleName}`)
const moduleApiMappings = this.collectModuleApiMappings(moduleName)
console.log(`🔍 模块 ${moduleName} 收集到 ${moduleApiMappings.length} 个API映射`)
if (moduleApiMappings.length > 0) {
// 将 serviceName 和 module 下沉到每个 API 映射中
// 将 module 下沉到每个 API 映射中(serviceName已经在extractApiMapping中添加了)
moduleApiMappings.forEach(apiMapping => {
this.apiMappings.push({
...apiMapping,
serviceName: moduleApiMappings.serviceName,
module: moduleName
})
})
}
})
console.log(`🔍 总共收集到 ${this.apiMappings.length} 个API映射`)
return this.apiMappings
}
@ -84,15 +78,11 @@ class ApiCollector { @@ -84,15 +78,11 @@ class ApiCollector {
collectModuleApiMappings(moduleName) {
const servicesPath = resolve(__dirname, '../../src/renderer/modules', moduleName, 'services')
console.log(`🔍 检查服务路径: ${servicesPath}`)
if (!existsSync(servicesPath)) {
console.log(`🔍 服务路径不存在: ${servicesPath}`)
return []
}
const serviceFiles = readdirSync(servicesPath).filter(file => file.endsWith('.js'))
console.log(`🔍 找到服务文件:`, serviceFiles)
const moduleApiMappings = []
@ -102,8 +92,6 @@ class ApiCollector { @@ -102,8 +92,6 @@ class ApiCollector {
const servicePath = resolve(servicesPath, serviceFile)
const serviceName = serviceFile.replace('.js', '')
console.log(`🔍 分析服务文件: ${serviceFile}`)
// 记录第一个服务名称(第一层需要)
if (!firstServiceName) {
firstServiceName = serviceName
@ -111,16 +99,12 @@ class ApiCollector { @@ -111,16 +99,12 @@ class ApiCollector {
try {
const serviceApiMappings = this.analyzeServiceFile(servicePath, serviceName, moduleName)
console.log(`🔍 服务 ${serviceName} 收集到 ${serviceApiMappings.length} 个API映射`)
// 不再为每个API映射添加serviceName(第二层冗余信息)
moduleApiMappings.push(...serviceApiMappings)
} catch (error) {
console.error(`🔍 分析服务文件失败: ${serviceFile}`, error.message)
console.error(`分析服务文件失败: ${serviceFile}`, error.message)
}
})
console.log(`🔍 模块 ${moduleName} 总共收集到 ${moduleApiMappings.length} 个API映射`)
// 返回模块API映射数组,serviceName 将在 collectApiMappings 中添加到每个API映射
return moduleApiMappings
}
@ -200,7 +184,8 @@ class ApiCollector { @@ -200,7 +184,8 @@ class ApiCollector {
return {
apiMethodName: methodName,
method: annotationInfo.method,
path: pathOnly
path: pathOnly,
serviceName: serviceName
}
}

465
gofaster/app/plugins/modules/component-relationship-analyzer-ast.js

@ -0,0 +1,465 @@ @@ -0,0 +1,465 @@
const { readFileSync, existsSync, readdirSync } = require('fs')
const { resolve } = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const { parse } = require('@vue/compiler-dom')
/**
* 基于AST的组件关系分析器
* 使用vue-template-compiler解析模板babel解析JS部分
*/
class ComponentRelationshipAnalyzerAST {
constructor() {
this.componentRelationships = []
this.processedComponents = new Set()
}
/**
* 分析组件关系
* @param {Array} routes - 路由数组
* @param {Array} apiMappings - API映射数组
* @returns {Array} 组件关系列表
*/
analyzeComponentRelationships(routes, apiMappings) {
this.componentRelationships = []
this.processedComponents.clear()
// 获取所有顶级组件(routes中的组件)并分配ID
const topLevelComponents = routes.map((route, index) => ({
id: index + 1, // 为routes分配ID
component: route.component,
module: this.getModuleFromRoute(route),
path: route.path
}))
// 获取所有触发源组件并分配ID
let triggerSourceId = 1
const triggerSourceComponents = this.extractTriggerSourceComponents(apiMappings).map(component => ({
id: triggerSourceId++, // 为triggerSource分配ID
...component
}))
console.log(`🔍 开始分析组件关系,触发源组件数量: ${triggerSourceComponents.length}`)
// 为每个触发源组件分析其与顶级组件的关系
triggerSourceComponents.forEach(triggerComponent => {
this.analyzeComponentToTopLevel(triggerComponent, topLevelComponents)
})
console.log(`🔍 分析完成,找到 ${this.componentRelationships.length} 个组件关系`)
return this.componentRelationships
}
/**
* 从路由中获取模块信息
* @param {Object} route - 路由对象
* @returns {string} 模块名称
*/
getModuleFromRoute(route) {
// 直接使用路由中的module字段,不再进行推断
return route.module || 'unknown'
}
/**
* 提取所有触发源组件
* @param {Array} apiMappings - API映射数组
* @returns {Array} 触发源组件数组
*/
extractTriggerSourceComponents(apiMappings) {
const components = []
apiMappings.forEach(apiMapping => {
if (apiMapping.triggerSources) {
apiMapping.triggerSources.forEach(triggerSource => {
components.push({
component: triggerSource.component,
module: triggerSource.module,
triggerName: triggerSource.triggerName,
triggerType: triggerSource.triggerType,
apiMethodName: apiMapping.apiMethodName,
apiModule: apiMapping.module
})
})
}
})
return components
}
/**
* 分析组件到顶级组件的关系
* @param {Object} triggerComponent - 触发源组件
* @param {Array} topLevelComponents - 顶级组件数组
*/
analyzeComponentToTopLevel(triggerComponent, topLevelComponents) {
const triggerKey = `${triggerComponent.id}:${triggerComponent.module}:${triggerComponent.component}`
// 避免重复处理同一个触发源
if (this.processedComponents.has(triggerKey)) {
console.log(` 跳过重复处理: ${triggerKey}`)
return
}
this.processedComponents.add(triggerKey)
// 如果触发源组件本身就是顶级组件,直接建立关系
const isTopLevel = topLevelComponents.find(top =>
top.component === triggerComponent.component && top.module === triggerComponent.module
)
if (isTopLevel) {
this.componentRelationships.push({
triggerSourceId: triggerComponent.id, // 使用ID引用
routeId: isTopLevel.id, // 使用ID引用
relationshipType: 'self'
})
return
}
// 查找父组件关系
const parentComponents = this.findParentComponentsAST(triggerComponent)
let hasRelationship = false
if (parentComponents.length > 0) {
parentComponents.forEach(parent => {
// 递归查找父组件的父组件,直到找到顶级组件
const found = this.findTopLevelAncestors(parent, triggerComponent, topLevelComponents)
if (found) hasRelationship = true
})
}
// 如果没有找到父组件关系,尝试直接匹配顶级组件
if (!hasRelationship) {
const directMatch = topLevelComponents.find(top =>
top.component === triggerComponent.component ||
top.module === triggerComponent.module
)
if (directMatch) {
this.componentRelationships.push({
triggerSourceId: triggerComponent.id,
routeId: directMatch.id,
relationshipType: 'direct'
})
hasRelationship = true
}
}
// 最后的兜底:确保每个触发源都至少有一个关系记录
if (!hasRelationship) {
this.componentRelationships.push({
triggerSourceId: triggerComponent.id,
routeId: 1, // 默认关联到第一个路由
relationshipType: 'fallback'
})
}
}
/**
* 使用AST解析查找组件的父组件
* @param {Object} component - 组件信息
* @returns {Array} 父组件数组
*/
findParentComponentsAST(component) {
const parents = []
try {
// 查找所有模块中的组件,看哪些引用了当前组件
const modulesPath = resolve(__dirname, '../../src/renderer/modules')
if (existsSync(modulesPath)) {
const moduleDirs = readdirSync(modulesPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name)
moduleDirs.forEach(moduleName => {
const parentComponents = this.findComponentsInModuleAST(moduleName, component)
parents.push(...parentComponents)
})
}
} catch (error) {
// 静默处理错误
}
return parents
}
/**
* 在指定模块中使用AST解析查找引用目标组件的组件
* @param {string} moduleName - 模块名称
* @param {Object} targetComponent - 目标组件
* @returns {Array} 父组件数组
*/
findComponentsInModuleAST(moduleName, targetComponent) {
const parents = []
try {
const modulePath = resolve(__dirname, '../../src/renderer/modules', moduleName)
if (!existsSync(modulePath)) return parents
// 查找views目录
const viewsPath = resolve(modulePath, 'views')
if (existsSync(viewsPath)) {
const viewFiles = readdirSync(viewsPath).filter(file => file.endsWith('.vue'))
viewFiles.forEach(file => {
const componentName = file.replace('.vue', '')
if (this.componentReferencesTargetAST(viewsPath, file, targetComponent)) {
console.log(`🔍 找到父组件: ${moduleName}:${componentName} -> ${targetComponent.module}:${targetComponent.component}`)
parents.push({
component: componentName,
module: moduleName,
type: 'view'
})
}
})
}
// 查找components目录
const componentsPath = resolve(modulePath, 'components')
if (existsSync(componentsPath)) {
const componentFiles = readdirSync(componentsPath).filter(file => file.endsWith('.vue'))
componentFiles.forEach(file => {
const componentName = file.replace('.vue', '')
if (this.componentReferencesTargetAST(componentsPath, file, targetComponent)) {
console.log(`🔍 找到父组件: ${moduleName}:${componentName} -> ${targetComponent.module}:${targetComponent.component}`)
parents.push({
component: componentName,
module: moduleName,
type: 'component'
})
}
})
}
} catch (error) {
// 静默处理错误
}
return parents
}
/**
* 使用AST解析检查组件是否引用了目标组件
* @param {string} dirPath - 目录路径
* @param {string} fileName - 文件名
* @param {Object} targetComponent - 目标组件
* @returns {boolean} 是否引用
*/
componentReferencesTargetAST(dirPath, fileName, targetComponent) {
try {
const filePath = resolve(dirPath, fileName)
const content = readFileSync(filePath, 'utf-8')
// 解析Vue单文件组件
const { script, template } = this.parseVueSFC(content)
// 检查script部分的import和components注册
if (script && this.analyzeScriptAST(script, targetComponent)) {
return true
}
// 检查template部分的使用
if (template && this.analyzeTemplateAST(template, targetComponent)) {
return true
}
return false
} catch (error) {
return false
}
}
/**
* 解析Vue单文件组件
* @param {string} content - 文件内容
* @returns {Object} 解析结果
*/
parseVueSFC(content) {
const result = { script: null, template: null }
try {
// 提取script部分
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/)
if (scriptMatch) {
result.script = scriptMatch[1]
}
// 提取template部分
const templateMatch = content.match(/<template[^>]*>([\s\S]*?)<\/template>/)
if (templateMatch) {
result.template = templateMatch[1]
}
} catch (error) {
// 静默处理错误
}
return result
}
/**
* 使用Babel AST分析script部分
* @param {string} scriptContent - script内容
* @param {Object} targetComponent - 目标组件
* @returns {boolean} 是否引用
*/
analyzeScriptAST(scriptContent, targetComponent) {
try {
const ast = parser.parse(scriptContent, {
sourceType: 'module',
plugins: ['jsx', 'typescript']
})
let hasReference = false
traverse(ast, {
// 检查import语句 - 更精确的匹配
ImportDeclaration(path) {
const source = path.node.source.value
// 检查import的默认导入
if (path.node.specifiers) {
path.node.specifiers.forEach(spec => {
if (spec.type === 'ImportDefaultSpecifier' &&
spec.local.name === targetComponent.component) {
hasReference = true
}
// 检查命名导入
if (spec.type === 'ImportSpecifier' &&
spec.imported.name === targetComponent.component) {
hasReference = true
}
})
}
},
// 检查components注册 - 支持多种格式
ObjectProperty(path) {
if (path.node.key.name === 'components') {
const value = path.node.value
if (value.type === 'ObjectExpression') {
value.properties.forEach(prop => {
// 支持 components: { ComponentName: ComponentName }
if (prop.key.name === targetComponent.component) {
hasReference = true
}
// 支持 components: { ComponentName }
if (prop.type === 'ObjectProperty' &&
prop.key.name === targetComponent.component) {
hasReference = true
}
})
}
}
},
// 检查Vue 3 Composition API的组件注册
CallExpression(path) {
if (path.node.callee.name === 'defineComponent') {
const arg = path.node.arguments[0]
if (arg && arg.type === 'ObjectExpression') {
arg.properties.forEach(prop => {
if (prop.key.name === 'components') {
const componentsValue = prop.value
if (componentsValue.type === 'ObjectExpression') {
componentsValue.properties.forEach(compProp => {
if (compProp.key.name === targetComponent.component) {
hasReference = true
}
})
}
}
})
}
}
}
})
return hasReference
} catch (error) {
return false
}
}
/**
* 使用Vue template compiler分析template部分
* @param {string} templateContent - template内容
* @param {Object} targetComponent - 目标组件
* @returns {boolean} 是否引用
*/
analyzeTemplateAST(templateContent, targetComponent) {
try {
const ast = parse(templateContent)
let hasReference = false
// 遍历AST查找组件使用
const traverse = (node) => {
if (node.type === 1) { // Element
// 检查标签名是否匹配目标组件
if (node.tag === targetComponent.component) {
hasReference = true
return
}
// 检查动态组件
if (node.tag === 'component' && node.props) {
node.props.forEach(prop => {
if (prop.name === 'is' && prop.value &&
prop.value.content === targetComponent.component) {
hasReference = true
return
}
})
}
// 递归遍历子节点
if (node.children) {
node.children.forEach(child => {
if (child.type === 1) { // Element
traverse(child)
}
})
}
}
}
traverse(ast)
return hasReference
} catch (error) {
return false
}
}
/**
* 查找顶级祖先组件
* @param {Object} parentComponent - 父组件
* @param {Object} originalTrigger - 原始触发组件
* @param {Array} topLevelComponents - 顶级组件数组
* @returns {boolean} 是否找到了关系
*/
findTopLevelAncestors(parentComponent, originalTrigger, topLevelComponents) {
// 检查父组件是否是顶级组件
const isTopLevel = topLevelComponents.find(top =>
top.component === parentComponent.component && top.module === parentComponent.module
)
if (isTopLevel) {
// 找到顶级组件,建立关系
this.componentRelationships.push({
triggerSourceId: originalTrigger.id, // 使用ID引用
routeId: isTopLevel.id, // 使用ID引用
relationshipType: 'ancestor'
})
return true
}
// 继续向上查找
const grandParents = this.findParentComponentsAST(parentComponent)
let found = false
grandParents.forEach(grandParent => {
const result = this.findTopLevelAncestors(grandParent, originalTrigger, topLevelComponents)
if (result) found = true
})
return found
}
}
module.exports = ComponentRelationshipAnalyzerAST

280
gofaster/app/plugins/modules/component-relationship-analyzer.js

@ -0,0 +1,280 @@ @@ -0,0 +1,280 @@
const { readFileSync, existsSync, readdirSync } = require('fs')
const { resolve } = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
/**
* 组件关系分析器
* 分析组件之间的层级关系从子组件追溯到顶级组件
*/
class ComponentRelationshipAnalyzer {
constructor() {
this.componentRelationships = []
this.processedComponents = new Set()
}
/**
* 分析组件关系
* @param {Array} routes - 路由数组
* @param {Array} apiMappings - API映射数组
* @returns {Array} 组件关系列表
*/
analyzeComponentRelationships(routes, apiMappings) {
this.componentRelationships = []
this.processedComponents.clear()
// 获取所有顶级组件(routes中的组件)
const topLevelComponents = routes.map(route => ({
component: route.component,
module: this.getModuleFromRoute(route),
path: route.path
}))
// 获取所有触发源组件
const triggerSourceComponents = this.extractTriggerSourceComponents(apiMappings)
// 为每个触发源组件分析其与顶级组件的关系
triggerSourceComponents.forEach(triggerComponent => {
this.analyzeComponentToTopLevel(triggerComponent, topLevelComponents)
})
return this.componentRelationships
}
/**
* 从路由中获取模块信息
* @param {Object} route - 路由对象
* @returns {string} 模块名称
*/
getModuleFromRoute(route) {
// 根据路径推断模块
if (route.path === '/') return 'core'
if (route.path.includes('user-management')) return 'user-management'
if (route.path.includes('role-management')) return 'role-management'
if (route.path.includes('settings')) return 'system-settings'
if (route.path.includes('user-profile')) return 'user-management'
return 'core'
}
/**
* 提取所有触发源组件
* @param {Array} apiMappings - API映射数组
* @returns {Array} 触发源组件数组
*/
extractTriggerSourceComponents(apiMappings) {
const components = []
apiMappings.forEach(apiMapping => {
if (apiMapping.triggerSources) {
apiMapping.triggerSources.forEach(triggerSource => {
components.push({
component: triggerSource.component,
module: triggerSource.module,
triggerName: triggerSource.triggerName,
triggerType: triggerSource.triggerType,
apiMethodName: apiMapping.apiMethodName,
apiModule: apiMapping.module
})
})
}
})
return components
}
/**
* 分析组件到顶级组件的关系
* @param {Object} triggerComponent - 触发源组件
* @param {Array} topLevelComponents - 顶级组件数组
*/
analyzeComponentToTopLevel(triggerComponent, topLevelComponents) {
const componentKey = `${triggerComponent.module}:${triggerComponent.component}`
// 避免重复处理
if (this.processedComponents.has(componentKey)) {
return
}
this.processedComponents.add(componentKey)
// 如果触发源组件本身就是顶级组件,直接建立关系
const isTopLevel = topLevelComponents.find(top =>
top.component === triggerComponent.component && top.module === triggerComponent.module
)
if (isTopLevel) {
this.componentRelationships.push({
startComponent: triggerComponent.component,
startModule: triggerComponent.module,
endComponent: triggerComponent.component,
endModule: triggerComponent.module,
relationshipType: 'self',
path: isTopLevel.path,
triggerName: triggerComponent.triggerName,
triggerType: triggerComponent.triggerType,
apiMethodName: triggerComponent.apiMethodName
})
return
}
// 查找父组件关系
const parentComponents = this.findParentComponents(triggerComponent)
parentComponents.forEach(parent => {
// 递归查找父组件的父组件,直到找到顶级组件
this.findTopLevelAncestors(parent, triggerComponent, topLevelComponents)
})
}
/**
* 查找组件的父组件
* @param {Object} component - 组件信息
* @returns {Array} 父组件数组
*/
findParentComponents(component) {
const parents = []
try {
// 查找所有模块中的组件,看哪些引用了当前组件
const modulesPath = resolve(__dirname, '../../src/renderer/modules')
if (existsSync(modulesPath)) {
const moduleDirs = readdirSync(modulesPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name)
moduleDirs.forEach(moduleName => {
const parentComponents = this.findComponentsInModule(moduleName, component)
parents.push(...parentComponents)
})
}
} catch (error) {
// 静默处理错误
}
return parents
}
/**
* 在指定模块中查找引用目标组件的组件
* @param {string} moduleName - 模块名称
* @param {Object} targetComponent - 目标组件
* @returns {Array} 父组件数组
*/
findComponentsInModule(moduleName, targetComponent) {
const parents = []
try {
const modulePath = resolve(__dirname, '../../src/renderer/modules', moduleName)
if (!existsSync(modulePath)) return parents
// 查找views目录
const viewsPath = resolve(modulePath, 'views')
if (existsSync(viewsPath)) {
const viewFiles = readdirSync(viewsPath).filter(file => file.endsWith('.vue'))
viewFiles.forEach(file => {
const componentName = file.replace('.vue', '')
if (this.componentReferencesTarget(viewsPath, file, targetComponent)) {
parents.push({
component: componentName,
module: moduleName,
type: 'view'
})
}
})
}
// 查找components目录
const componentsPath = resolve(modulePath, 'components')
if (existsSync(componentsPath)) {
const componentFiles = readdirSync(componentsPath).filter(file => file.endsWith('.vue'))
componentFiles.forEach(file => {
const componentName = file.replace('.vue', '')
if (this.componentReferencesTarget(componentsPath, file, targetComponent)) {
parents.push({
component: componentName,
module: moduleName,
type: 'component'
})
}
})
}
} catch (error) {
// 静默处理错误
}
return parents
}
/**
* 检查组件是否引用了目标组件
* @param {string} dirPath - 目录路径
* @param {string} fileName - 文件名
* @param {Object} targetComponent - 目标组件
* @returns {boolean} 是否引用
*/
componentReferencesTarget(dirPath, fileName, targetComponent) {
try {
const filePath = resolve(dirPath, fileName)
const content = readFileSync(filePath, 'utf-8')
// 检查import语句
const importPattern = new RegExp(`import.*${targetComponent.component}.*from`, 'g')
if (importPattern.test(content)) {
return true
}
// 检查components注册
const componentPattern = new RegExp(`${targetComponent.component}\\s*:`, 'g')
if (componentPattern.test(content)) {
return true
}
// 检查模板中的使用
const templatePattern = new RegExp(`<${targetComponent.component}[\\s>]`, 'g')
if (templatePattern.test(content)) {
return true
}
return false
} catch (error) {
return false
}
}
/**
* 查找顶级祖先组件
* @param {Object} parentComponent - 父组件
* @param {Object} originalTrigger - 原始触发组件
* @param {Array} topLevelComponents - 顶级组件数组
*/
findTopLevelAncestors(parentComponent, originalTrigger, topLevelComponents) {
// 检查父组件是否是顶级组件
const isTopLevel = topLevelComponents.find(top =>
top.component === parentComponent.component && top.module === parentComponent.module
)
if (isTopLevel) {
// 找到顶级组件,建立关系
this.componentRelationships.push({
startComponent: originalTrigger.component,
startModule: originalTrigger.module,
endComponent: parentComponent.component,
endModule: parentComponent.module,
relationshipType: 'ancestor',
path: isTopLevel.path,
triggerName: originalTrigger.triggerName,
triggerType: originalTrigger.triggerType,
apiMethodName: originalTrigger.apiMethodName
})
return
}
// 继续向上查找
const grandParents = this.findParentComponents(parentComponent)
grandParents.forEach(grandParent => {
this.findTopLevelAncestors(grandParent, originalTrigger, topLevelComponents)
})
}
}
module.exports = ComponentRelationshipAnalyzer

28
gofaster/app/plugins/modules/data-cleaner.js

@ -68,28 +68,12 @@ class DataCleaner { @@ -68,28 +68,12 @@ class DataCleaner {
return cleanedMappings
}
mappings.apiMappings.forEach(module => {
if (!module.apiMappings || module.apiMappings.length === 0) {
return
}
const cleanedApiMappings = []
module.apiMappings.forEach(api => {
// 仅删除triggerSources列表为空的apiMapping
// 保留所有有triggerSources的apiMapping,不管triggerType是什么
if (api.triggerSources && api.triggerSources.length > 0) {
cleanedApiMappings.push(api)
}
})
// 如果还有apiMappings,保留这个模块
if (cleanedApiMappings.length > 0) {
const cleanedModule = {
...module,
apiMappings: cleanedApiMappings
}
cleanedMappings.apiMappings.push(cleanedModule)
// 新的数据结构:apiMappings直接是API映射数组
mappings.apiMappings.forEach(api => {
// 仅删除triggerSources列表为空的apiMapping
// 保留所有有triggerSources的apiMapping,不管triggerType是什么
if (api.triggerSources && api.triggerSources.length > 0) {
cleanedMappings.apiMappings.push(api)
}
})

61
gofaster/app/plugins/modules/file-generator.js

@ -81,10 +81,67 @@ export default { @@ -81,10 +81,67 @@ export default {
const content = `// 路由映射数据 - 构建时生成
export default {
// 路由配置
routes: ${JSON.stringify(routes, null, 2)},
"routes": ${JSON.stringify(routes, null, 2)},
// API映射配置
apiMappings: ${JSON.stringify(apiMappings, null, 2)}
"apiMappings": ${JSON.stringify(apiMappings, null, 2)}
}`
// 如果文件已存在,先清空内容
if (existsSync(this.outputPath)) {
writeFileSync(this.outputPath, '', 'utf-8')
}
// 写入新内容
writeFileSync(this.outputPath, content, 'utf-8')
}
/**
* 生成包含组件关系的路由和API映射文件
* @param {Array} routes - 路由数组
* @param {Array} apiMappings - API映射数组
* @param {Array} componentRelationships - 组件关系数组
*/
generateRouteApiMappingFileWithRelationships(routes, apiMappings, componentRelationships) {
// 为routes添加编号
const routesWithIds = routes.map((route, index) => ({
id: index + 1,
...route
}))
// 为apiMappings中的triggerSources添加编号
let triggerSourceId = 1
const apiMappingsWithIds = apiMappings.map(apiMapping => {
if (apiMapping.triggerSources) {
const triggerSourcesWithIds = apiMapping.triggerSources.map(triggerSource => ({
id: triggerSourceId++,
...triggerSource
}))
return {
...apiMapping,
triggerSources: triggerSourcesWithIds
}
}
return apiMapping
})
// 简化componentRelationships结构
const simplifiedRelationships = componentRelationships.map(rel => ({
triggerSourceId: rel.triggerSourceId,
routeId: rel.routeId,
relationshipType: rel.relationshipType
}))
const content = `// 路由映射数据 - 构建时生成
export default {
// 路由配置
"routes": ${JSON.stringify(routesWithIds, null, 2)},
// API映射配置
"apiMappings": ${JSON.stringify(apiMappingsWithIds, null, 2)},
// 组件关系配置(简化版)
"componentRelationships": ${JSON.stringify(simplifiedRelationships, null, 2)}
}`
// 如果文件已存在,先清空内容

185
gofaster/app/plugins/modules/route-analyzer.js

@ -1,13 +1,16 @@ @@ -1,13 +1,16 @@
const { readFileSync, existsSync } = require('fs')
const { resolve } = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
/**
* 路由分析模块
* 负责分析路由配置
* 负责分析路由配置从import语句中提取模块信息
*/
class RouteAnalyzer {
constructor() {
this.routes = []
this.componentModuleMap = new Map() // 组件名 -> 模块名的映射
}
/**
@ -16,11 +19,17 @@ class RouteAnalyzer { @@ -16,11 +19,17 @@ class RouteAnalyzer {
*/
analyzeRoutes() {
this.routes = []
this.componentModuleMap.clear()
try {
const routeConfigPath = resolve(__dirname, '../../src/renderer/router/index.js')
if (existsSync(routeConfigPath)) {
const routeContent = readFileSync(routeConfigPath, 'utf-8')
// 第一步:解析import语句,建立组件与模块的映射关系
this.parseImportStatements(routeContent)
// 第二步:解析路由配置,为每个路由添加模块信息
this.routes = this.parseRouteConfig(routeContent)
}
} catch (error) {
@ -31,54 +40,160 @@ class RouteAnalyzer { @@ -31,54 +40,160 @@ class RouteAnalyzer {
}
/**
* 解析路由配置 - 优化版本直接从路由配置中收集信息
* 解析import语句建立组件与模块的映射关系
* @param {string} routeContent - 路由配置内容
*/
parseImportStatements(routeContent) {
try {
const ast = parser.parse(routeContent, {
sourceType: 'module',
plugins: ['jsx', 'typescript']
})
const self = this
traverse(ast, {
ImportDeclaration(path) {
const source = path.node.source.value
// 解析模块路径,提取模块名
const moduleName = self.extractModuleName(source)
if (moduleName) {
// 处理导入的组件
path.node.specifiers.forEach(spec => {
let componentName = null
if (spec.type === 'ImportDefaultSpecifier') {
// 默认导入:import Component from '...'
componentName = spec.local.name
} else if (spec.type === 'ImportSpecifier') {
// 命名导入:import { Component } from '...'
componentName = spec.imported.name
}
if (componentName) {
self.componentModuleMap.set(componentName, moduleName)
}
})
}
}
})
} catch (error) {
// 静默处理错误
}
}
/**
* 从import路径中提取模块名
* @param {string} importPath - import路径
* @returns {string|null} 模块名
*/
extractModuleName(importPath) {
// 匹配 @/modules/module-name 格式
const moduleMatch = importPath.match(/@\/modules\/([^\/]+)/)
if (moduleMatch) {
return moduleMatch[1]
}
return null
}
/**
* 解析路由配置为每个路由添加模块信息
* @param {string} routeContent - 路由配置内容
* @returns {Array} 路由数组
*/
parseRouteConfig(routeContent) {
const routes = []
// 解析Vue Router格式的路由配置
// 查找所有路由定义,包括嵌套路由
const routeMatches = routeContent.match(/\{[^}]*path:\s*['"]([^'"]+)['"][^}]*\}/g)
if (routeMatches) {
routeMatches.forEach(match => {
const pathMatch = match.match(/path:\s*['"]([^'"]+)['"]/)
const componentMatch = match.match(/component:\s*([A-Za-z][A-Za-z0-9]*)/)
const nameMatch = match.match(/name:\s*['"]([^'"]+)['"]/)
const descriptionMatch = match.match(/description:\s*['"]([^'"]+)['"]/)
const authTypeMatch = match.match(/authType:\s*['"]([^'"]+)['"]/)
if (pathMatch && componentMatch) {
const route = {
path: pathMatch[1],
component: componentMatch[1]
}
// 只有当name与component不同时才保留name字段
if (nameMatch && nameMatch[1] !== componentMatch[1]) {
route.name = nameMatch[1]
}
// 优先使用路由配置中的description,如果没有则生成
if (descriptionMatch) {
route.description = descriptionMatch[1]
}
// 收集authType字段
if (authTypeMatch) {
route.authType = authTypeMatch[1]
try {
const ast = parser.parse(routeContent, {
sourceType: 'module',
plugins: ['jsx', 'typescript']
})
const self = this
traverse(ast, {
ObjectExpression(path) {
// 查找路由对象
if (self.isRouteObject(path)) {
const route = self.extractRouteInfo(path)
if (route) {
routes.push(route)
}
}
routes.push(route)
}
})
} catch (error) {
// 静默处理错误
}
return routes
}
/**
* 判断是否是路由对象
* @param {Object} path - AST路径
* @returns {boolean} 是否是路由对象
*/
isRouteObject(path) {
const properties = path.node.properties
return properties.some(prop =>
prop.key && prop.key.name === 'path' &&
prop.value && prop.value.type === 'StringLiteral'
)
}
/**
* 从路由对象中提取路由信息
* @param {Object} path - AST路径
* @returns {Object|null} 路由信息
*/
extractRouteInfo(path) {
const route = {}
const properties = path.node.properties
properties.forEach(prop => {
if (prop.key && prop.value) {
const key = prop.key.name
const value = prop.value
switch (key) {
case 'path':
if (value.type === 'StringLiteral') {
route.path = value.value
}
break
case 'component':
if (value.type === 'Identifier') {
route.component = value.name
// 从组件名获取模块信息
const moduleName = this.componentModuleMap.get(value.name)
if (moduleName) {
route.module = moduleName
}
}
break
case 'name':
if (value.type === 'StringLiteral') {
route.name = value.value
}
break
case 'description':
if (value.type === 'StringLiteral') {
route.description = value.value
}
break
}
}
})
// 只有当path和component都存在时才返回路由信息
if (route.path && route.component) {
return route
}
return null
}
/**
* 获取模块列表
* @returns {Array} 模块目录列表

9
gofaster/app/plugins/modules/trigger-analyzer-simple.js

@ -138,6 +138,7 @@ class TriggerAnalyzerSimple { @@ -138,6 +138,7 @@ class TriggerAnalyzerSimple {
apiCalls.forEach(call => {
triggerSources.push({
component: componentName,
module: moduleName, // 添加模块信息
triggerName: call.triggerName || null, // 没有名称则为null
triggerType: call.triggerType || 'function'
})
@ -157,7 +158,7 @@ class TriggerAnalyzerSimple { @@ -157,7 +158,7 @@ class TriggerAnalyzerSimple {
* @returns {string|null} 组件文件路径
*/
findComponentFile(componentName, moduleName = null) {
// 如果提供了模块名称,优先在该模块中查找
// 如果提供了模块名称,只在该模块中查找,不允许跨模块查找
if (moduleName) {
// 检查views目录
const viewPath = resolve(__dirname, `../../src/renderer/modules/${moduleName}/views/${componentName}.vue`)
@ -170,10 +171,12 @@ class TriggerAnalyzerSimple { @@ -170,10 +171,12 @@ class TriggerAnalyzerSimple {
if (existsSync(componentPath)) {
return componentPath
}
// 如果在指定模块中没有找到,返回null(不允许跨模块查找)
return null
}
// 如果没有找到或没有提供模块名称,则在所有模块中查找
// 这里我们动态获取所有模块目录
// 如果没有提供模块名称,则在所有模块中查找
const modulesPath = resolve(__dirname, '../../src/renderer/modules')
if (existsSync(modulesPath)) {
const { readdirSync } = require('fs')

10
gofaster/app/plugins/route-mapping-plugin.js

@ -5,6 +5,7 @@ const TriggerAnalyzerSimple = require('./modules/trigger-analyzer-simple') @@ -5,6 +5,7 @@ const TriggerAnalyzerSimple = require('./modules/trigger-analyzer-simple')
const CallChainTracer = require('./modules/call-chain-tracer')
const FileGenerator = require('./modules/file-generator')
const RouteAnalyzer = require('./modules/route-analyzer')
const ComponentRelationshipAnalyzerAST = require('./modules/component-relationship-analyzer-ast')
/**
* 模块化路由映射插件
@ -38,6 +39,7 @@ function routeMappingPlugin() { @@ -38,6 +39,7 @@ function routeMappingPlugin() {
_callChainTracer: new CallChainTracer(),
_fileGenerator: new FileGenerator(),
_routeAnalyzer: new RouteAnalyzer(),
_componentRelationshipAnalyzer: new ComponentRelationshipAnalyzerAST(),
// Webpack插件接口
apply(compiler) {
@ -154,7 +156,13 @@ function routeMappingPlugin() { @@ -154,7 +156,13 @@ function routeMappingPlugin() {
// ========== 第三步:删除triggerSources为空的apiMapping ==========
const DataCleaner = require('./modules/data-cleaner.js')
const dataCleaner = new DataCleaner()
const cleanedMappings = dataCleaner.cleanData()
const cleanedMappings = dataCleaner.cleanData()
// ========== 第四步:分析组件关系 ==========
const componentRelationships = this._componentRelationshipAnalyzer.analyzeComponentRelationships(routes, cleanedMappings.apiMappings)
// 生成包含组件关系的最终文件
this._fileGenerator.generateRouteApiMappingFileWithRelationships(routes, cleanedMappings.apiMappings, componentRelationships)
// 重置生成中标志并更新时间戳
this._generationInProgress = false

402
gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js

@ -1,32 +1,376 @@ @@ -1,32 +1,376 @@
// 路由映射数据 - 构建时生成
export default {
// 路由配置
"routes": [
{
"path": "/",
"component": "MainLayout",
"name": "Home",
"description": "首页"
},
{
"path": "/user-management",
"component": "UserManagement",
"description": "用户管理"
},
{
"path": "/settings",
"component": "Settings",
"description": "系统设置"
},
{
"path": "/user-profile",
"component": "UserProfile",
"description": "个人资料"
},
{
"path": "/role-management",
"component": "RoleManagement",
"description": "角色管理"
}
],
"apiMappings": []
};
{
"id": 1,
"path": "/",
"component": "MainLayout",
"module": "core"
},
{
"id": 2,
"path": "/user-management",
"name": "UserManagement",
"component": "UserManagement",
"module": "user-management",
"description": "用户管理"
},
{
"id": 3,
"path": "/settings",
"name": "Settings",
"component": "Settings",
"module": "system-settings",
"description": "系统设置"
},
{
"id": 4,
"path": "/user-profile",
"name": "UserProfile",
"component": "UserProfile",
"module": "user-management",
"description": "个人资料"
},
{
"id": 5,
"path": "/role-management",
"name": "RoleManagement",
"component": "RoleManagement",
"module": "role-management",
"description": "角色管理"
}
],
// API映射配置
"apiMappings": [
{
"apiMethodName": "createRole",
"method": "POST",
"path": "/api/auth/roles",
"serviceName": "roleService",
"module": "role-management",
"triggerSources": [
{
"id": 1,
"component": "RoleManagement",
"module": "role-management",
"triggerName": "rolemanagement-submit-lhm6fs",
"triggerType": "button"
}
]
},
{
"apiMethodName": "updateRole",
"method": "PUT",
"path": "/api/auth/roles/{id}",
"serviceName": "roleService",
"module": "role-management",
"triggerSources": [
{
"id": 2,
"component": "RoleManagement",
"module": "role-management",
"triggerName": "rolemanagement-submit-lhm6fs",
"triggerType": "button"
},
{
"id": 3,
"component": "PermissionManager",
"module": "role-management",
"triggerName": "permissionmanager-button-k7wqyp",
"triggerType": "button"
}
]
},
{
"apiMethodName": "deleteRole",
"method": "DELETE",
"path": "/api/auth/roles/{id}",
"serviceName": "roleService",
"module": "role-management",
"triggerSources": [
{
"id": 4,
"component": "RoleManagement",
"module": "role-management",
"triggerName": "rolemanagement-button-0eqx80",
"triggerType": "button"
}
]
},
{
"apiMethodName": "assignRolesToUser",
"method": "POST",
"path": "/api/auth/roles/users/{userId}/assign",
"serviceName": "roleService",
"module": "role-management",
"triggerSources": [
{
"id": 5,
"component": "UserRoleAssignment",
"module": "role-management",
"triggerName": "userroleassignment-button-3npmn8",
"triggerType": "button"
}
]
},
{
"apiMethodName": "removeRolesFromUser",
"method": "DELETE",
"path": "/api/auth/roles/users/{userId}/remove",
"serviceName": "roleService",
"module": "role-management",
"triggerSources": [
{
"id": 6,
"component": "UserRoleAssignment",
"module": "role-management",
"triggerName": "userroleassignment-button-3npmn8",
"triggerType": "button"
}
]
},
{
"apiMethodName": "assignPermissionsToRole",
"method": "POST",
"path": "/api/auth/permissions/roles/{roleId}/assign",
"serviceName": "roleService",
"module": "role-management",
"triggerSources": [
{
"id": 7,
"component": "RolePermissionAssignment",
"module": "role-management",
"triggerName": "rolepermissionassignment-button-7uizm5",
"triggerType": "button"
},
{
"id": 8,
"component": "RolePermissionAssignment",
"module": "role-management",
"triggerName": "rolepermissionassignment-button-a0nyh8",
"triggerType": "button"
}
]
},
{
"apiMethodName": "removePermissionsFromRole",
"method": "DELETE",
"path": "/api/auth/permissions/roles/{roleId}/remove",
"serviceName": "roleService",
"module": "role-management",
"triggerSources": [
{
"id": 9,
"component": "RolePermissionAssignment",
"module": "role-management",
"triggerName": "rolepermissionassignment-button-q86qi4",
"triggerType": "button"
},
{
"id": 10,
"component": "RolePermissionAssignment",
"module": "role-management",
"triggerName": "rolepermissionassignment-button-sj6qb7",
"triggerType": "button"
}
]
},
{
"apiMethodName": "createUser",
"method": "POST",
"path": "/api/auth/admin/users",
"serviceName": "userService",
"module": "user-management",
"triggerSources": [
{
"id": 11,
"component": "UserManagement",
"module": "user-management",
"triggerName": "usermanagement-submit-fksbqh",
"triggerType": "button"
}
]
},
{
"apiMethodName": "updateUser",
"method": "PUT",
"path": "/api/auth/admin/users/{id}",
"serviceName": "userService",
"module": "user-management",
"triggerSources": [
{
"id": 12,
"component": "UserManagement",
"module": "user-management",
"triggerName": "usermanagement-submit-fksbqh",
"triggerType": "button"
}
]
},
{
"apiMethodName": "deleteUser",
"method": "DELETE",
"path": "/api/auth/admin/users/{id}",
"serviceName": "userService",
"module": "user-management",
"triggerSources": [
{
"id": 13,
"component": "UserManagement",
"module": "user-management",
"triggerName": "usermanagement-button-tvncrr",
"triggerType": "button"
}
]
},
{
"apiMethodName": "changePassword",
"method": "POST",
"path": "/api/auth/change-password",
"serviceName": "userService",
"module": "user-management",
"triggerSources": [
{
"id": 14,
"component": "PasswordChangeModal",
"module": "user-management",
"triggerName": "passwordchangemodal-submit-2p704c",
"triggerType": "button"
}
]
},
{
"apiMethodName": "validatePassword",
"method": "POST",
"path": "/api/auth/validate-password",
"serviceName": "userService",
"module": "user-management",
"triggerSources": [
{
"id": 15,
"component": "PasswordChangeModal",
"module": "user-management",
"triggerName": "passwordchangemodal-input-0xe3cd",
"triggerType": "input"
}
]
},
{
"apiMethodName": "changePassword",
"method": "POST",
"path": "/api/auth/change-password",
"serviceName": "userService",
"module": "user-management",
"triggerSources": [
{
"id": 16,
"component": "PasswordChangeModal",
"module": "user-management",
"triggerName": "passwordchangemodal-submit-2p704c",
"triggerType": "button"
}
]
}
],
// 组件关系配置(简化版)
"componentRelationships": [
{
"triggerSourceId": 1,
"routeId": 5,
"relationshipType": "self"
},
{
"triggerSourceId": 2,
"routeId": 5,
"relationshipType": "self"
},
{
"triggerSourceId": 3,
"routeId": 5,
"relationshipType": "direct"
},
{
"triggerSourceId": 4,
"routeId": 5,
"relationshipType": "self"
},
{
"triggerSourceId": 5,
"routeId": 2,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 6,
"routeId": 2,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 7,
"routeId": 5,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 8,
"routeId": 5,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 9,
"routeId": 5,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 10,
"routeId": 5,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 11,
"routeId": 2,
"relationshipType": "self"
},
{
"triggerSourceId": 12,
"routeId": 2,
"relationshipType": "self"
},
{
"triggerSourceId": 13,
"routeId": 2,
"relationshipType": "self"
},
{
"triggerSourceId": 14,
"routeId": 1,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 14,
"routeId": 4,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 15,
"routeId": 1,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 15,
"routeId": 4,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 16,
"routeId": 1,
"relationshipType": "ancestor"
},
{
"triggerSourceId": 16,
"routeId": 4,
"relationshipType": "ancestor"
}
]
}
Loading…
Cancel
Save