You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

168 lines
8.0 KiB

// 自动扫描 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<string>} [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}`)
}
}