import type Koa from 'koa'; import KoaRouter from '@koa/router'; import { getRouteMetadata, getControllerPrefix, type RouteMetadata } from '../decorators/route.ts'; import { createAutoSuccessResponse } from '../middlewares/exception.ts'; /** * 控制器类型 */ export interface ControllerClass { new (...args: any[]): any; } /** * 路由扫描器,用于自动注册装饰器标注的路由 */ export class RouteScanner { private router: KoaRouter; private controllers: ControllerClass[] = []; constructor(prefix: string = '/api') { this.router = new KoaRouter({ prefix }); } /** * 注册控制器类 */ registerController(ControllerClass: ControllerClass): void { this.controllers.push(ControllerClass); this.scanController(ControllerClass); } /** * 注册多个控制器类 */ registerControllers(controllers: ControllerClass[]): void { controllers.forEach(controller => this.registerController(controller)); } /** * 扫描控制器并注册路由 */ private scanController(ControllerClass: ControllerClass): void { // 创建控制器实例 const controllerInstance = new ControllerClass(); // 获取控制器的路由前缀 const controllerPrefix = getControllerPrefix(ControllerClass); // 获取控制器的路由元数据 const routes: RouteMetadata[] = getRouteMetadata(ControllerClass); // 注册每个路由 routes.forEach(route => { const fullPath = this.buildFullPath(controllerPrefix, route.path); const handler = this.wrapControllerMethod(controllerInstance, route.propertyKey); // 根据HTTP方法注册路由 switch (route.method) { case 'GET': this.router.get(fullPath, handler); break; case 'POST': this.router.post(fullPath, handler); break; case 'PUT': this.router.put(fullPath, handler); break; case 'DELETE': this.router.delete(fullPath, handler); break; case 'PATCH': this.router.patch(fullPath, handler); break; default: console.warn(`未支持的HTTP方法: ${route.method}`); } }); } /** * 构建完整的路由路径 */ private buildFullPath(controllerPrefix: string, routePath: string): string { // 清理和拼接路径 const cleanControllerPrefix = controllerPrefix.replace(/^\/+|\/+$/g, ''); const cleanRoutePath = routePath.replace(/^\/+|\/+$/g, ''); let fullPath = ''; if (cleanControllerPrefix) { fullPath += '/' + cleanControllerPrefix; } if (cleanRoutePath) { fullPath += '/' + cleanRoutePath; } // 如果路径为空,返回根路径 return fullPath || '/'; } /** * 包装控制器方法,统一处理响应格式 */ private wrapControllerMethod(instance: any, methodName: string) { return async (ctx: Koa.Context, next: Koa.Next) => { try { // 调用控制器方法 const method = instance[methodName]; if (typeof method !== 'function') { throw new Error(`控制器方法 ${methodName} 不存在或不是函数`); } // 绑定this并调用方法 const result = await method.call(instance, ctx, next); // 如果控制器返回了数据,则包装成统一响应格式 ctx.body = createAutoSuccessResponse(result); } catch (error) { // 错误由全局异常处理中间件处理 throw error; } }; } /** * 获取Koa路由器实例,用于应用到Koa应用中 */ getRouter(): KoaRouter { return this.router; } /** * 应用路由到Koa应用 */ applyToApp(app: Koa): void { app.use(this.router.routes()); app.use(this.router.allowedMethods()); } /** * 获取已注册的路由信息(用于调试) */ getRegisteredRoutes(): Array<{ method: string; path: string; controller: string; action: string }> { const routes: Array<{ method: string; path: string; controller: string; action: string }> = []; this.controllers.forEach(ControllerClass => { const controllerPrefix = getControllerPrefix(ControllerClass); const routeMetadata = getRouteMetadata(ControllerClass); routeMetadata.forEach(route => { routes.push({ method: route.method, path: this.buildFullPath(controllerPrefix, route.path), controller: ControllerClass.name, action: route.propertyKey }); }); }); return routes; } }