// 自动扫描 controllers 目录并注册路由 // 兼容传统 routes 方式和自动注册 controller 方式 import fs from "fs" import path from "path" import { logger } from "@/logger.js" import routeCache from "./cache/RouteCache.js" // 保证不会被摇树(tree-shaking),即使在生产环境也会被打包 if (import.meta.env.PROD) { // 通过引用返回值,防止被摇树优化 let controllers = import.meta.glob("../controllers/**/*Controller.js", { eager: true }) controllers = null console.log(controllers); } /** * 自动扫描 controllers 目录,注册所有导出的路由 * 自动检测 routes 目录下已手动注册的 controller,避免重复注册 * @param {Koa} app - Koa 实例 * @param {string} controllersDir - controllers 目录路径 * @param {string} prefix - 路由前缀 * @param {Set} [manualControllers] - 可选,手动传入已注册 controller 文件名集合,优先于自动扫描 */ export function autoRegisterControllers(app, controllersDir) { let allRouter = [] function scan(dir, routePrefix = "") { try { for (const file of fs.readdirSync(dir)) { const fullPath = path.join(dir, file) const stat = fs.statSync(fullPath) if (stat.isDirectory()) { if (!file.startsWith("_")) { scan(fullPath, routePrefix + "/" + file) } } else if (file.endsWith("Controller.js") && !file.startsWith("_")) { try { const stat = fs.statSync(fullPath) const mtime = stat.mtime.getTime() // 尝试从缓存获取路由注册结果 let cachedRoutes = routeCache.getRegistration(fullPath, mtime) if (cachedRoutes) { // 缓存命中,直接使用缓存结果 allRouter.push(...cachedRoutes) logger.info(`[控制器注册] ✨ ${file} - 从缓存加载路由成功`) continue } // 使用动态导入ES模块 const controllerModule = require(fullPath) const controller = controllerModule.default || controllerModule if (!controller) { logger.warn(`[控制器注册] ${file} - 缺少默认导出,跳过注册`) continue } // 尝试从缓存获取控制器实例 const className = controller.name || file.replace('.js', '') let cachedController = routeCache.getController(className) const routes = controller.createRoutes || controller.default?.createRoutes || controller.default || controller if (typeof routes === "function") { try { const routerResult = routes() const routersToProcess = Array.isArray(routerResult) ? routerResult : [routerResult] const validRouters = [] for (const router of routersToProcess) { if (router && typeof router.middleware === "function") { validRouters.push(router) } else { logger.warn(`[控制器注册] ⚠️ ${file} - createRoutes() 返回的部分路由器对象无效`) } } if (validRouters.length > 0) { allRouter.push(...validRouters) // 将路由注册结果存入缓存(如果缓存启用) routeCache.setRegistration(fullPath, mtime, validRouters) // 将控制器类存入缓存以便复用(如果缓存启用) if (!cachedController) { routeCache.setController(className, controller) } // 根据缓存状态显示不同的日志信息 const cacheEnabled = routeCache.config.enabled if (cacheEnabled) { logger.info(`[控制器注册] ✅ ${file} - 路由创建成功,已缓存`) } else { logger.info(`[控制器注册] ✅ ${file} - 路由创建成功`) } } else { logger.warn(`[控制器注册] ⚠️ ${file} - createRoutes() 返回的不是有效的路由器对象`) } } catch (error) { logger.error(`[控制器注册] ❌ ${file} - createRoutes() 执行失败: ${error.message}`) } } else { logger.warn(`[控制器注册] ⚠️ ${file} - 未找到 createRoutes 方法或导出对象`) } } catch (importError) { logger.error(`[控制器注册] ❌ ${file} - 模块导入失败: ${importError.message}`) } } } } catch (error) { logger.error(`[控制器注册] ❌ 扫描目录失败 ${dir}: ${error.message}`) } } try { scan(controllersDir) if (allRouter.length === 0) { logger.warn("[路由注册] ⚠️ 未发现任何可注册的控制器") return } logger.info(`[路由注册] 📋 发现 ${allRouter.length} 个控制器,开始注册到应用`) // 按顺序注册路由,确保中间件执行顺序 for (let i = 0; i < allRouter.length; i++) { const router = allRouter[i] try { app.use(router.middleware()) logger.debug(`[路由注册] 🔗 路由 ${i + 1}/${allRouter.length} 注册成功`) // 枚举并紧凑输出该路由器下的所有路由方法与路径(单行聚合) const methodEntries = Object.entries(router.routes || {}) const items = [] for (const [method, list] of methodEntries) { if (!Array.isArray(list) || list.length === 0) continue for (const r of list) { if (!r || !r.path) continue items.push(`${method.toUpperCase()} ${r.path}`) } } if (items.length > 0) { const prefix = router.options && router.options.prefix ? router.options.prefix : "" logger.info(`[路由注册] ✅ ${prefix || "/"} 共 ${items.length} 条 -> ${items.join("; ")}`) } else { logger.warn(`[路由注册] ⚠️ 该控制器未包含任何可用路由`) } } catch (error) { logger.error(`[路由注册] ❌ 路由 ${i + 1}/${allRouter.length} 注册失败: ${error.message}`) } } logger.info(`[路由注册] ✅ 完成!成功注册 ${allRouter.length} 个控制器路由`) // 输出缓存统计信息 const cacheStats = routeCache.getStats() logger.info(`[路由缓存] 缓存状态: ${cacheStats.enabled ? '启用' : '禁用'}, 总命中率: ${cacheStats.hitRate}`) if (cacheStats.enabled) { logger.debug(`[路由缓存] 详细统计:`, cacheStats.caches) } } catch (error) { logger.error(`[路由注册] ❌ 自动注册过程中发生严重错误: ${error.message}`) } }