Browse Source

api收集完成

master
hejl 3 days ago
parent
commit
a2dca55854
  1. 150
      gofaster/app/plugins/modules/api-collector.js
  2. 24
      gofaster/app/plugins/modules/file-generator.js
  3. 14
      gofaster/app/plugins/route-mapping-plugin.js
  4. 160
      gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js
  5. 11
      gofaster/app/test-optimized-collection.js

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

@ -12,6 +12,39 @@ class ApiCollector {
this.apiMappings = [] this.apiMappings = []
} }
/**
* 获取API基础URL
* @returns {string} API基础URL
*/
getApiBaseUrl() {
try {
// 读取配置文件
const configPath = resolve(__dirname, '../../src/config/app.config.js')
if (existsSync(configPath)) {
const configContent = readFileSync(configPath, 'utf-8')
// 解析配置文件,提取apiBaseUrl
// 匹配 development 环境中的 apiBaseUrl 配置
const developmentMatch = configContent.match(/development:\s*\{[^}]*apiBaseUrl:\s*[^:]*:\s*['"`]([^'"`]+)['"`]/s)
if (developmentMatch) {
return developmentMatch[1]
}
// 如果上面没匹配到,尝试匹配简单的字符串格式
const simpleMatch = configContent.match(/apiBaseUrl:\s*['"`]([^'"`]+)['"`]/)
if (simpleMatch) {
return simpleMatch[1]
}
}
// 如果无法从配置文件获取,返回空字符串(使用相对路径)
return ''
} catch (error) {
console.warn('⚠ 无法获取API基础URL,使用相对路径:', error.message)
return ''
}
}
/** /**
* 收集所有模块的API映射 * 收集所有模块的API映射
* @param {Array} moduleDirs - 模块目录列表 * @param {Array} moduleDirs - 模块目录列表
@ -25,8 +58,7 @@ class ApiCollector {
if (moduleApiMappings.length > 0) { if (moduleApiMappings.length > 0) {
this.apiMappings.push({ this.apiMappings.push({
module: moduleName, module: moduleName,
serviceName: moduleApiMappings[0].serviceName, serviceName: moduleApiMappings.serviceName,
servicePath: moduleApiMappings[0].servicePath,
apiMappings: moduleApiMappings apiMappings: moduleApiMappings
}) })
} }
@ -50,18 +82,31 @@ class ApiCollector {
const serviceFiles = readdirSync(servicesPath).filter(file => file.endsWith('.js')) const serviceFiles = readdirSync(servicesPath).filter(file => file.endsWith('.js'))
const moduleApiMappings = [] const moduleApiMappings = []
let firstServiceName = null
serviceFiles.forEach(serviceFile => { serviceFiles.forEach(serviceFile => {
const servicePath = resolve(servicesPath, serviceFile) const servicePath = resolve(servicesPath, serviceFile)
const serviceName = serviceFile.replace('.js', '') const serviceName = serviceFile.replace('.js', '')
// 记录第一个服务名称(第一层需要)
if (!firstServiceName) {
firstServiceName = serviceName
}
try { try {
const serviceApiMappings = this.analyzeServiceFile(servicePath, serviceName, moduleName) const serviceApiMappings = this.analyzeServiceFile(servicePath, serviceName, moduleName)
// 不再为每个API映射添加serviceName(第二层冗余信息)
moduleApiMappings.push(...serviceApiMappings) moduleApiMappings.push(...serviceApiMappings)
} catch (error) { } catch (error) {
// 静默处理错误 // 静默处理错误
} }
}) })
// 为模块API映射添加serviceName(第一层需要)
if (firstServiceName) {
moduleApiMappings.serviceName = firstServiceName
}
return moduleApiMappings return moduleApiMappings
} }
@ -113,7 +158,7 @@ class ApiCollector {
} }
/** /**
* 从AST节点中提取API映射信息 * 从AST节点中提取API映射信息 - 优化版本只使用@serverApiMethod注解
* @param {Object} methodPath - 方法路径 * @param {Object} methodPath - 方法路径
* @param {string} methodName - 方法名称 * @param {string} methodName - 方法名称
* @param {string} serviceName - 服务名称 * @param {string} serviceName - 服务名称
@ -121,51 +166,88 @@ class ApiCollector {
* @returns {Object|null} API映射对象 * @returns {Object|null} API映射对象
*/ */
extractApiMapping(methodPath, methodName, serviceName, servicePath) { extractApiMapping(methodPath, methodName, serviceName, servicePath) {
let httpMethod = null // 只从@serverApiMethod注解中提取信息(强规则)
let apiPath = null const annotationInfo = this.extractFromAnnotation(methodPath, servicePath)
if (annotationInfo) {
// 查找HTTP方法调用 // 获取API基础URL
traverse(methodPath.node, { const apiBaseUrl = this.getApiBaseUrl()
CallExpression(path) {
const node = path.node
if (node.callee && node.callee.type === 'MemberExpression') {
const object = node.callee.object
const property = node.callee.property
if (object && property && // 组合完整的API路径
object.type === 'Identifier' && const fullPath = `${apiBaseUrl}${annotationInfo.path}`
property.type === 'Identifier') {
const method = property.name.toLowerCase() // 提取路径部分,去掉协议、服务器地址和端口
if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) { const pathOnly = this.extractPathFromUrl(fullPath)
httpMethod = method.toUpperCase()
// 提取API路径 return {
if (node.arguments && node.arguments.length > 0) { apiMethodName: methodName,
const firstArg = node.arguments[0] method: annotationInfo.method,
if (firstArg.type === 'StringLiteral') { path: pathOnly
apiPath = firstArg.value
} else if (firstArg.type === 'TemplateLiteral') {
apiPath = firstArg.quasis[0].value.raw
} }
} }
// 如果没有注解,返回null(不再回退到代码分析)
return null
} }
/**
* 从完整URL中提取路径部分
* @param {string} fullUrl - 完整的URL
* @returns {string} 路径部分
*/
extractPathFromUrl(fullUrl) {
try {
// 如果URL包含协议,使用URL对象解析
if (fullUrl.includes('://')) {
const url = new URL(fullUrl)
// 解码路径,确保路径参数保持原始格式
return decodeURIComponent(url.pathname)
} }
// 如果没有协议,直接返回(已经是路径格式)
return fullUrl
} catch (error) {
// 如果解析失败,返回原始字符串
return fullUrl
} }
} }
}, methodPath.scope, methodPath)
if (httpMethod && apiPath) { /**
* @serverApiMethod注解中提取API信息
* @param {Object} methodPath - 方法路径
* @param {string} servicePath - 服务文件路径
* @returns {Object|null} 注解信息
*/
extractFromAnnotation(methodPath, servicePath) {
try {
const content = readFileSync(servicePath, 'utf-8')
const lines = content.split('\n')
const methodLine = methodPath.node.loc ? methodPath.node.loc.start.line : 0
// 查找方法定义前的注释行(从方法行开始向上搜索)
for (let i = methodLine - 2; i >= 0; i--) {
const line = lines[i].trim()
// 查找@serverApiMethod注解
const annotationMatch = line.match(/\/\/\s*@serverApiMethod\s+(\w+)\s+(.+)/)
if (annotationMatch) {
const method = annotationMatch[1].toUpperCase()
const path = annotationMatch[2].trim()
return { return {
apiMethodName: methodName, method: method,
method: httpMethod, path: path
path: apiPath,
line: methodPath.node.loc ? methodPath.node.loc.start.line : 0,
serviceName: serviceName,
servicePath: servicePath
} }
} }
// 如果遇到非注释行且不是空行,停止搜索
if (line && !line.startsWith('//') && !line.startsWith('*') && !line.startsWith('/*')) {
break
}
}
} catch (error) {
console.error(`❌ 解析注解失败: ${servicePath}`, error.message)
}
return null return null
} }
} }

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

@ -72,6 +72,30 @@ export default {
writeFileSync(this.outputPath, content, 'utf-8') writeFileSync(this.outputPath, content, 'utf-8')
} }
/**
* 生成路由和API映射文件 - 第三步第一小步包含API信息
* @param {Array} routes - 路由数组
* @param {Array} apiMappings - API映射数组
*/
generateRouteApiMappingFile(routes, apiMappings) {
const content = `// 路由映射数据 - 构建时生成
export default {
// 路由配置
routes: ${JSON.stringify(routes, null, 2)},
// API映射配置
apiMappings: ${JSON.stringify(apiMappings, null, 2)}
}`
// 如果文件已存在,先清空内容
if (existsSync(this.outputPath)) {
writeFileSync(this.outputPath, '', 'utf-8')
}
// 写入新内容
writeFileSync(this.outputPath, content, 'utf-8')
}
/** /**
* 生成文件内容 * 生成文件内容
* @param {Array} routes - 路由数组 * @param {Array} routes - 路由数组

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

@ -130,12 +130,16 @@ function routeMappingPlugin() {
return return
} }
// 第二步:启用路由分析功能 // 第三步第一小步:启用API收集功能
// 1. 分析路由配置 // 1. 分析路由配置
const routes = this._routeAnalyzer.analyzeRoutes() const routes = this._routeAnalyzer.analyzeRoutes()
// 生成包含路由信息的文件 // 2. 收集API信息(第一层)
this._fileGenerator.generateRouteMappingFile(routes) const moduleDirs = this._routeAnalyzer.getModuleDirs()
const apiMappings = this._apiCollector.collectApiMappings(moduleDirs)
// 生成包含路由和API信息的文件
this._fileGenerator.generateRouteApiMappingFile(routes, apiMappings)
// 重置生成中标志并更新时间戳 // 重置生成中标志并更新时间戳
this._generationInProgress = false this._generationInProgress = false
@ -148,10 +152,6 @@ function routeMappingPlugin() {
// TODO: 后续步骤将在优化过程中逐步添加 // TODO: 后续步骤将在优化过程中逐步添加
// 2. 收集API信息(第一层)
// const moduleDirs = this._routeAnalyzer.getModuleDirs()
// const apiMappings = this._apiCollector.collectApiMappings(moduleDirs)
// 3. 关联页面与API(第三层) // 3. 关联页面与API(第三层)
// const enhancedApiMappings = this._triggerAnalyzer.findTriggerSourcesForApiMappings(apiMappings) // const enhancedApiMappings = this._triggerAnalyzer.findTriggerSourcesForApiMappings(apiMappings)

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

@ -31,5 +31,163 @@ export default {
], ],
// API映射配置 // API映射配置
apiMappings: [] apiMappings: [
{
"module": "role-management",
"serviceName": "roleService",
"apiMappings": [
{
"apiMethodName": "getRoles",
"method": "GET",
"path": "/api/auth/roles"
},
{
"apiMethodName": "getRole",
"method": "GET",
"path": "/api/auth/roles/{id}"
},
{
"apiMethodName": "createRole",
"method": "POST",
"path": "/api/auth/roles"
},
{
"apiMethodName": "updateRole",
"method": "PUT",
"path": "/api/auth/roles/{id}"
},
{
"apiMethodName": "deleteRole",
"method": "DELETE",
"path": "/api/auth/roles/{id}"
},
{
"apiMethodName": "getPermissions",
"method": "GET",
"path": "/api/auth/permissions"
},
{
"apiMethodName": "assignRolesToUser",
"method": "POST",
"path": "/api/auth/roles/users/{userId}/assign"
},
{
"apiMethodName": "getUserRoles",
"method": "GET",
"path": "/api/auth/roles/users/{userId}"
},
{
"apiMethodName": "removeRolesFromUser",
"method": "DELETE",
"path": "/api/auth/roles/users/{userId}/remove"
},
{
"apiMethodName": "getRolePermissions",
"method": "GET",
"path": "/api/auth/permissions/roles/{roleId}"
},
{
"apiMethodName": "assignPermissionsToRole",
"method": "POST",
"path": "/api/auth/permissions/roles/{roleId}/assign"
},
{
"apiMethodName": "removePermissionsFromRole",
"method": "DELETE",
"path": "/api/auth/permissions/roles/{roleId}/remove"
}
]
},
{
"module": "user-management",
"serviceName": "userService",
"apiMappings": [
{
"apiMethodName": "getUsers",
"method": "GET",
"path": "/api/auth/admin/users"
},
{
"apiMethodName": "getUser",
"method": "GET",
"path": "/api/auth/admin/users/{id}"
},
{
"apiMethodName": "createUser",
"method": "POST",
"path": "/api/auth/admin/users"
},
{
"apiMethodName": "updateUser",
"method": "PUT",
"path": "/api/auth/admin/users/{id}"
},
{
"apiMethodName": "deleteUser",
"method": "DELETE",
"path": "/api/auth/admin/users/{id}"
},
{
"apiMethodName": "getRoles",
"method": "GET",
"path": "/api/auth/admin/roles"
},
{
"apiMethodName": "changePassword",
"method": "POST",
"path": "/api/auth/change-password"
},
{
"apiMethodName": "getPasswordPolicy",
"method": "GET",
"path": "/api/auth/password-policy"
},
{
"apiMethodName": "validatePassword",
"method": "POST",
"path": "/api/auth/validate-password"
},
{
"apiMethodName": "checkPasswordStatus",
"method": "GET",
"path": "/api/auth/password-status"
},
{
"apiMethodName": "getPermissions",
"method": "GET",
"path": "/api/permissions"
},
{
"apiMethodName": "getCaptcha",
"method": "GET",
"path": "/api/auth/captcha"
},
{
"apiMethodName": "login",
"method": "POST",
"path": "/api/auth/login"
},
{
"apiMethodName": "logout",
"method": "POST",
"path": "/api/auth/logout"
},
{
"apiMethodName": "getCurrentUser",
"method": "GET",
"path": "/api/auth/me"
},
{
"apiMethodName": "changePassword",
"method": "POST",
"path": "/api/auth/change-password"
},
{
"apiMethodName": "resetPassword",
"method": "POST",
"path": "/api/auth/reset-password"
}
]
}
]
} }

11
gofaster/app/test-optimized-collection.js

@ -1,11 +0,0 @@
import { RouteCollector } from './src/renderer/modules/route-sync/RouteCollector.js';
const collector = new RouteCollector();
const routes = collector.collectRoutes();
console.log('优化后的路由收集结果:');
console.log('路由数量:', routes.length);
console.log('路由详情:');
routes.forEach((route, index) => {
console.log(`${index + 1}. ${route.path} - name: ${route.name} - component: ${route.component} - description: ${route.description}`);
});
Loading…
Cancel
Save