GitHub 代码库

前言

Egg是一个企业级应用框架,它奉行 「 约定优于配置 」,按照一套统一约定进行应用开发。Egg 不直接提供功能,只是集成各种功能插件。Egg 的插件机制有很高的可扩展性,一个插件只做一件事(比如 Nunjucks 模板封装成了 egg-view-nunjucks、MySQL 数据库封装成了 egg-mysql)。Egg 通过框架聚合这些插件,并根据自己的业务场景定制配置,这样应用的开发成本就变得很低。

egg 目录结构

我们来看一下 Egg 的目录结构

  1. egg-project
  2. ├── package.json
  3. ├── app.js (可选)
  4. ├── agent.js (可选)
  5. ├── app
  6. | ├── router.js
  7. ├── controller
  8. | └── home.js
  9. ├── service (可选)
  10. | └── user.js
  11. ├── middleware (可选)
  12. | └── response_time.js
  13. ├── schedule (可选)
  14. | └── my_task.js
  15. ├── public (可选)
  16. | └── reset.css
  17. ├── view (可选)
  18. | └── home.tpl
  19. └── extend (可选)
  20. ├── helper.js (可选)
  21. ├── request.js (可选)
  22. ├── response.js (可选)
  23. ├── context.js (可选)
  24. ├── application.js (可选)
  25. └── agent.js (可选)
  26. ├── config
  27. | ├── plugin.js
  28. | ├── config.default.js
  29. ├── config.prod.js
  30. | ├── config.test.js (可选)
  31. | ├── config.local.js (可选)
  32. | └── config.unittest.js (可选)
  33. └── test
  34. ├── middleware
  35. | └── response_time.test.js
  36. └── controller
  37. └── home.test.js

如上,框架约定的目录:

  • app/router.js 用于配置 URL 路由规则
  • app/controller/** 用于解析用户的输入,处理后返回响应的结果
  • app/service/** 用于编写业务逻辑层,该目录可选
  • app/middleware/** 用于编写中间件,该目录可选
  • app/public/** 用于放置静态资源,该目录可选
  • app/extend/** 用于框架的扩展,该目录可选
  • config/config.{env}.js 用于编写配置文件
  • config/plugin.js 用于配置需要加载的插件
  • test/** 用于单元测试
  • app.js 和 agent.js 用于自定义启动时的初始化工作,可选

在团队开发中,按照约定的目录规范进行开发,可以帮助开发团队和开发人员降低开发和维护成本。

简易版 Egg 实现

我们参照 Egg 的目录规范,实现一个简易版的 Egg 框架。我们的框架选择 koa 为基础框架。

框架目录

image.png

  • index.js 项目入口文件,实例化 Egg 类并启动服务
  • egg.js 挂载 koa 实例、路由、controller、service、config 等,定义 start 方法
  • loader.js 解析 config、controller、middleware、model、routes、schedule、service
  • config/** 用于编写配置文件
  • controller/** 用于解析用户输入
  • middleware/** 用于编写中间件
  • model/** 用于编写数据层代码
  • routes/** 用于配置 URL 路由规则
  • schedule/** 用于编写定时任务
  • service/** 用于编写业务逻辑层

入口文件

入口文件:index.js

在 index.js 中,我们期望实现的功能是:初始化一个 app 实例,然后调用 app 实例的 start 方法来启动服务。
代码实现如下:

  1. // 初始化一个 app 实例,并启动
  2. const egg = require('./egg');
  3. // 初始化 app 实例
  4. const app = new egg();
  5. // 启动服务
  6. app.start(3000);

接下来我们要实现的是 egg.js 文件

Egg 类

Egg 类:egg.js

在 egg.js 中,实现一个 Egg 类,在 Egg 类的 constructor 构造函数中挂载 koa 实例、路由、controller、service、config 等。并在 Egg 类中定义 start 方法,调用 koa 实例来启动服务器。代码如下:

  1. const koa = require('koa')
  2. class Egg {
  3. constructor(conf) {
  4. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  5. this.$app = new koa(conf)
  6. // 其它实例方法挂载
  7. }
  8. // 启动服务器
  9. start(port) {
  10. this.$app.listen(port, () => {
  11. console.log('服务器启动 端口:' + port)
  12. })
  13. }
  14. }
  15. module.exports = Egg

框架的关键实现在于目录和文件的解析。接下来,我们对约定的目录进行解析。对目录的解析代码我们统一放在 loader.js 文件中

目录解析:loader.js

读取目录

我们先来实现一个读取目录的方法,这个方法在后面的目录解析中都会调用到。

load 方法接收两个参数,dir 为目录名称;cb 为回调函数,业务上的具体处理都会在 cb 中处理。在 load 方法中,使用 fs.readdirSync 方法读取目录的内容,然后遍历读取到的目录内容,将文件名和文件内容在 cb 中返回出去。

  1. // 读取目录
  2. function load(dir, cb) {
  3. // 获取绝对路径
  4. const url = path.resolve(__dirname, dir)
  5. // 同步的读取目录的内容
  6. const files = fs.readdirSync(url)
  7. // 遍历
  8. files.forEach(filename => {
  9. // 去掉后缀,提取文件名
  10. filename = filename.replace('.js', '')
  11. // 导入文件
  12. const file = require(url + '/' + filename)
  13. // 将文件名和文件内容返回出去
  14. cb(filename, file)
  15. })
  16. }

routes 目录解析

routers 目录存放的是路由文件,比如在 routers 目录有定义了 user.js 文件,在该文件中配置的就是 user 的路由,如配置了路由 get /info ,则访问时的路径为 /user/info

  1. // 初始化路由
  2. function initRouter(app) {
  3. const router = new Router()
  4. // 调用 load 方法,读取 routers 目录,对目录下的文件内容初始化成路由
  5. load('routes', (filename, routes) => {
  6. // index前缀处理
  7. const prefix = filename === 'index' ? '' : `/${filename}`
  8. // 路由类型判断
  9. routes = typeof routes === 'function' ? routes(app) : routes
  10. // 遍历添加路由
  11. Object.keys(routes).forEach(key => {
  12. const [method, path] = key.split(' ')
  13. let routerPath = `${prefix}${path}`
  14. if (/^\/.+\/$/.test(routerPath)) routerPath = routerPath.substr(0, routerPath.length - 1)
  15. console.log(`正在映射地址 ${method.toLocaleUpperCase()} routerPath`)
  16. // 注册
  17. // router[method](prefix + path, routes[key])
  18. router[method](routerPath, async ctx => {
  19. app.ctx = ctx
  20. await routes[key](app)
  21. })
  22. })
  23. })
  24. return router
  25. }

在 initRouter 方法中,将将 routers 目录下的文件批量注册成路由,省去了我们手动一个一个的注册路由的麻烦。

我们从路由文件中定义的路由内容提取出路由path和路由对应的处理方法,将他们注册成如下的形式:

  1. // koa-router 路由注册
  2. router.get('/', async (ctx, next) => {
  3. console.log('index');
  4. ctx.body = 'index';
  5. });

如我们在 user 路由文件中定义了如下的路由:

  1. "get /info": app => {
  2. app.ctx.body = "用户年龄" + app.$service.user.getAge();
  3. }

initRouter 方法会帮我们注册成:

  1. router.get('/user/info', app => {
  2. app.ctx.body = "用户年龄" + app.$service.user.getAge();
  3. });

initRouter 方法定义好后,在 loader.js 文件中导出:

  1. module.exports = { initRouter }

然后在 egg.js 文件中导入 initRouter 方法,并将其挂载到 egg 实例上,egg.js 文件变成如下:

  1. const koa = require('koa')
  2. const { initRouter } = require('./kkb-loader')
  3. class Egg {
  4. constructor(conf) {
  5. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  6. this.$app = new koa(conf)
  7. // 其它实例方法挂载
  8. this.$router = initRouter(this)
  9. }
  10. // 启动服务器
  11. start(port) {
  12. this.$app.listen(port, () => {
  13. console.log('服务器启动 端口:' + port)
  14. })
  15. }
  16. }
  17. module.exports = Egg

controller 目录解析

controller 目录存放的文件主要是对用户的请求参数进行处理(校验,转换),然后调用对应的 service 方法处理业务,得的业务结果后将其返回。

  1. function initController(app) {
  2. const controllers = {}
  3. // 读取目录
  4. // load 方法的第一个参数为 目录名称
  5. // load 方法的第二个参数为一个回调函数,参数 filename 为文件名称,controller 为在文件中定义的业务处理方法
  6. load('controller', (filename, controller) => {
  7. // 将文件名称与文件中定义的方法映射成 key/value 的形式
  8. // 便于在定义路由的时候,可以通过 ${fileName}.${functionName} 的方式指定对应的 Controller
  9. controllers[filename] = controller(app)
  10. })
  11. return controllers
  12. }

initController 方法解析 controller 目录下的文件,将文件名称与文件中定义的 function 映射成 key/value 的形式,便于在定义路由的时候,可以通过 ${fileName}.${functionName} 的方式指定对应的 Controller。

在 controller 目录下有一个 home.js 文件,在 home.js 文件中定义了一个 detail 的处理函数,如下:

  1. module.exports = app => ({
  2. detail: ctx => {
  3. app.ctx.body = '详细页面内容'
  4. }
  5. })

在路由文件中调用 detail 处理函数:

  1. module.exports = app => ({
  2. 'get /detail': app.$controller.home.detail
  3. })

app.controller.home.detail 中,app 为我们的 egg 实例,$controller 代表 controller 目录的名称,home 代表 controller 目录下的home.js 文件,detail 就是我们在 home.js 文件中定义的处理函数。

initController 方法定义好后,在 loader.js 文件中导出:

  1. module.exports = { initController }

然后在 egg.js 文件中导入 initController 方法,并将其挂载到 egg 实例上,egg.js 文件变成如下:

  1. const koa = require('koa')
  2. const { initRouter, initController } = require('./kkb-loader')
  3. class Egg {
  4. constructor(conf) {
  5. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  6. this.$app = new koa(conf)
  7. // 其它实例方法挂载
  8. this.$controller = initController(this) // 加载ctrl
  9. this.$router = initRouter(this)
  10. }
  11. // 启动服务器
  12. start(port) {
  13. this.$app.listen(port, () => {
  14. console.log('服务器启动 端口:' + port)
  15. })
  16. }
  17. }
  18. module.exports = Egg

service 目录解析

service 目录下的文件用于对复杂数据和第三方服务的调用。

  • 复杂数据的处理,比如要展现的信息需要从数据库获取,还要经过一定的规则计算,才能返回给用户显示。或者计算完成后,更新到数据库。
  • 第三方服务的调用,比如 GitHub 信息获取等。
    1. // service 目录解析
    2. function initService() {
    3. const services = {}
    4. // 将文件名称与文件中定义的方法映射成 key/value 的形式
    5. // 可以通过 ${fileName}.${functionName} 的方式指定对应的 Service
    6. load('service', (filename, service) => {
    7. services[filename] = service
    8. })
    9. return services
    10. }
    initService 方法解析 service 目录下的文件,将文件名称与文件中定义的 function 映射成 key/value 的形式,可以在 controller 中通过 ${fileName}.${functionName} 的方式指定对应的 service。

比如在 service 目录下有一个 user.js 文件,在 user.js 文件中定义了一个 getName 的处理函数,如下:

  1. module.exports = {
  2. getName() {
  3. return 'Tom'
  4. }
  5. }

在 controller 中调用 getName 方法:

  1. module.exports = app => ({
  2. index: async ctx => {
  3. ctx.body = '首页Ctrl'
  4. const name = await app.$service.user.getName()
  5. pp.ctx.body = 'ctrl user' + name
  6. }
  7. })

app.$service.user.getName() 中,app 为我们的 egg 实例,$service 代表 service 目录的名称,user 代表 service 目录下的 user.js 文件,getName 就是我们在 user.js 文件中定义的处理函数。

initService 方法定义好后,在 loader.js 文件中导出:

  1. module.exports = { initService }

然后在 egg.js 文件中导入 initService 方法,并将其挂载到 egg 实例上,egg.js 文件变成如下:

  1. const koa = require('koa')
  2. const { initRouter, initController, initService } = require('./kkb-loader')
  3. class Egg {
  4. constructor(conf) {
  5. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  6. this.$app = new koa(conf)
  7. // 其它实例方法挂载
  8. this.$service = initService() // 加载 service
  9. this.$controller = initController(this) // 加载 controller
  10. this.$router = initRouter(this) // 加载路由
  11. }
  12. // 启动服务器
  13. start(port) {
  14. this.$app.listen(port, () => {
  15. console.log('服务器启动 端口:' + port)
  16. })
  17. }
  18. }
  19. module.exports = Egg

config 目录解析

config 目录用于编写配置文件。config 目录下的文件将会被自动合并,合并后的配置可以直接成 app.$config 获取。

  1. function loadConfig(app) {
  2. let config = {}
  3. load('config', (filename, conf) => {
  4. // 合并 config 目录下的配置
  5. config = Object.assign(config, conf)
  6. })
  7. return config
  8. }

loadConfig 方法定义好后,在 loader.js 文件中导出:

  1. module.exports = { loadConfig }

然后在 egg.js 文件中导入 loadConfig 方法,并将其挂载到 egg 实例上,egg.js 文件变成如下:

  1. const koa = require('koa')
  2. const { initRouter, initController, initService, loadConfig } = require('./kkb-loader')
  3. class Egg {
  4. constructor(conf) {
  5. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  6. this.$app = new koa(conf)
  7. // 其它实例方法挂载
  8. this.$config = loadConfig(this) 加载 config
  9. this.$service = initService() // 加载 service
  10. this.$controller = initController(this) // 加载 controller
  11. this.$router = initRouter(this) // 加载路由
  12. }
  13. // 启动服务器
  14. start(port) {
  15. this.$app.listen(port, () => {
  16. console.log('服务器启动 端口:' + port)
  17. })
  18. }
  19. }
  20. module.exports = Egg

model 目录解析

model 目录下的文件是用来和数据库打交道的。在框架中,我们使用 sequelize 框架来帮助我们管理数据层的代码。sequelize 是一个广泛使用的 ORM 框架,它支持 MySQL、PostgreSQL、SQLite 和 MSSQL 等多个数据源。

  1. const Sequelize = require('sequelize')
  2. function initModel(app) {
  3. const conf = app.$config
  4. if (conf.db) {
  5. app.$db = new Sequelize(conf.db)
  6. // 加载模型
  7. app.$model = {}
  8. load('model', (filename, { schema, options }) => {
  9. // 将模型映射成 key/value 的形式,挂载到 app 实例上
  10. app.$model[filename] = app.$db.define(filename, schema, options)
  11. })
  12. app.$db.sync()
  13. }
  14. }

initModel 方法解析 目录下的文件,将文件名称与文件中定义的 Sequelize 模型 映射成 key/value 的形式,就可以通过 ${fileName}.${functionName} 的方式调用对应的模型方法了。

比如在 model 目录下定义了一个 User 的模型,也就是在数据库中建一个 user 表,

  1. const { STRING } = require("sequelize");
  2. module.exports = {
  3. schema: {
  4. name: STRING(30)
  5. },
  6. options: {
  7. timestamps: false
  8. }
  9. };

使用 initModel 方法解析后,我们可以通过 ${fileName}.${functionName} 的方式调用对应的模型方法:

  1. module.exports = app => ({
  2. index: async ctx => {
  3. // 调用 User 模型的 findAll() 方法,查找 user 表中的所有用户
  4. app.ctx.body = await app.$model.user.findAll()
  5. }
  6. })

initModel 方法定义好后,在 loader.js 文件中导出:

  1. module.exports = { initModel }

然后在 egg.js 文件中导入 loadConfig 方法,并将其挂载到 egg 实例上,egg.js 文件变成如下:

  1. const koa = require('koa')
  2. const { initRouter, initController, initService, loadConfig, initModel } = require('./kkb-loader')
  3. class Egg {
  4. constructor(conf) {
  5. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  6. this.$app = new koa(conf)
  7. // 其它实例方法挂载
  8. this.$config = loadConfig(this) 加载 config
  9. initModel(app) // 初始化 model
  10. this.$service = initService() // 加载 service
  11. this.$controller = initController(this) // 加载 controller
  12. this.$router = initRouter(this) // 加载路由
  13. }
  14. // 启动服务器
  15. start(port) {
  16. this.$app.listen(port, () => {
  17. console.log('服务器启动 端口:' + port)
  18. })
  19. }
  20. }
  21. module.exports = Egg

middleware目录解析

middleware 目录用于编写中间件函数,然后在 config 文件中应用写好的中间件函数

  1. function initMiddleware(app) {
  2. // 从 egg 实例上获取配置
  3. const conf = app.$config;
  4. // 读取配置中的 middleware 属性
  5. if (conf.middleware) {
  6. // 通过 koa 实例使用中间件
  7. conf.middleware.forEach(mid => {
  8. const midPath = path.resolve(__dirname, 'middleware', mid)
  9. // $app 为 koa 实例
  10. app.$app.use(require(midPath))
  11. })
  12. }
  13. }

在 initMiddleware 函数中,首先从我们的 egg 实例上获取配置,然后读取在配置中配置使用的中间件,并使用 koa 实例来加载中间件。

比如我们再 middleware 目录定义了一个 logger 的中间件函数:

  1. module.exports = async (ctx, next) => {
  2. console.log(ctx.method + " " + ctx.path);
  3. const start = new Date();
  4. await next();
  5. const duration = new Date() - start;
  6. console.log(
  7. ctx.method + " " + ctx.path + " " + ctx.status + " " + duration + "ms"
  8. );
  9. };

在config文件中配置使用 logger 中间件:

  1. module.exports = {
  2. middleware: ['logger']
  3. }

经过 initMiddleware 的解析,就可以使用我们编写的中间件函数了。

initMiddleware 方法定义好后,在 loader.js 文件中导出:

  1. module.exports = { initMiddleware }

然后在 egg.js 文件中导入 loadConfig 方法,并将其挂载到 egg 实例上,egg.js 文件变成如下:

  1. const koa = require('koa')
  2. const { initRouter, initController, initService, loadConfig, initMiddleware } = require('./kkb-loader')
  3. class Egg {
  4. constructor(conf) {
  5. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  6. this.$app = new koa(conf)
  7. // 其它实例方法挂载
  8. this.$config = loadConfig(this) 加载 config
  9. initModel(app) // 初始化 model
  10. initMiddleware(app) // 初始化中间件
  11. this.$service = initService() // 加载 service
  12. this.$controller = initController(this) // 加载 controller
  13. this.$router = initRouter(this) // 加载路由
  14. }
  15. // 启动服务器
  16. start(port) {
  17. this.$app.listen(port, () => {
  18. console.log('服务器启动 端口:' + port)
  19. })
  20. }
  21. }
  22. module.exports = Egg

schedule 目录解析

schedule 目录用于编写定时任务的文件。如我们需要定时爬取某个站点上的文章,我们就可以编写一个爬虫文件存放在 schedule 目录下。

  1. const schedule = require('node-schedule')
  2. function initSchedule(app) {
  3. const conf = app.$config;
  4. // 加载 schedule
  5. load('schedule', (filename, scheduleConfig) => {
  6. if (conf.schedule) {
  7. // 根据配置的 schedule 来启动指定的定时任务
  8. conf.schedule.forEach(job => {
  9. if (filename === job) {
  10. // schedule.scheduleJob() 创建一个定时任务
  11. schedule.scheduleJob(scheduleConfig.interval, scheduleConfig.handler)
  12. }
  13. })
  14. }
  15. })
  16. }

在 initSchedule 喊数中,调用 load 方法加载 schedule 目录下的文件,然后根据用户的配置来启动对应的定时任务。使用 node-schedule 的 scheduleJob() 方法来创建一个定时任务。

initSchedule 方法定义好后,在 loader.js 文件中导出:

  1. module.exports = { initSchedule }

然后在 egg.js 文件中导入 loadConfig 方法,并将其挂载到 egg 实例上,egg.js 文件变成如下:

  1. const koa = require('koa')
  2. const { initRouter, initController, initService, loadConfig, initMiddleware, initSchedule } = require('./kkb-loader')
  3. class Egg {
  4. constructor(conf) {
  5. // 初始化 koa 实例,挂载到 我们的 egg 实例上
  6. this.$app = new koa(conf)
  7. // 其它实例方法挂载
  8. this.$config = loadConfig(this) 加载 config
  9. initMiddleware(app) // 初始化中间件
  10. initModel(app) // 初始化 model
  11. this.$service = initService() // 加载 service
  12. this.$controller = initController(this) // 加载 controller
  13. this.$router = initRouter(this) // 加载路由
  14. initSchedule(this) // 初始化定时任务
  15. }
  16. // 启动服务器
  17. start(port) {
  18. this.$app.listen(port, () => {
  19. console.log('服务器启动 端口:' + port)
  20. })
  21. }
  22. }
  23. module.exports = Egg

完整代码

index.js

  1. const egg = require('./egg')
  2. const app = new egg()
  3. app.start(7001)

egg.js

  1. const koa = require('koa')
  2. const { initRouter,
  3. initController,
  4. initService,
  5. loadConfig,
  6. initMiddleware,
  7. initModel,
  8. initSchedule
  9. } = require('./loader')
  10. class Egg {
  11. constructor(conf) {
  12. this.$app = new koa(conf)
  13. // loadConfig(this)
  14. this.$config = loadConfig(this)
  15. initMiddleware(this)
  16. initModel(this)
  17. this.$service = initService()
  18. this.$ctrl = initController(this) // 加载ctrl
  19. this.$router = initRouter(this)
  20. this.$app.use(this.$router.routes())
  21. initSchedule(this)
  22. }
  23. start(port) {
  24. this.$app.listen(port, () => {
  25. console.log('服务器启动 端口:' + port)
  26. })
  27. }
  28. }
  29. module.exports = Egg

loader.js

  1. const fs = require('fs')
  2. const path = require('path')
  3. const Router = require('koa-router')
  4. // 读取目录
  5. function load(dir, cb) {
  6. // 获取绝对路径
  7. const url = path.resolve(__dirname, dir)
  8. const files = fs.readdirSync(url)
  9. // 遍历
  10. files.forEach(filename => {
  11. // 去掉后缀
  12. filename = filename.replace('.js', '')
  13. // 导入文件
  14. const file = require(url + '/' + filename)
  15. cb(filename, file)
  16. })
  17. }
  18. function initRouter(app) {
  19. const router = new Router()
  20. load('routes', (filename, routes) => {
  21. // index前缀处理
  22. const prefix = filename === 'index' ? '' : `/${filename}`
  23. // 路由类型判断
  24. routes = typeof routes === 'function' ? routes(app) : routes
  25. // 遍历添加路由
  26. Object.keys(routes).forEach(key => {
  27. const [method, path] = key.split(' ')
  28. let routerPath = `${prefix}${path}`
  29. if (/^\/.+\/$/.test(routerPath)) routerPath = routerPath.substr(0, routerPath.length - 1)
  30. console.log(`正在映射地址 ${method.toLocaleUpperCase()} ${routerPath}`)
  31. // 注册
  32. // router[method](prefix + path, routes[key])
  33. router[method](routerPath, async ctx => {
  34. app.ctx = ctx
  35. await routes[key](app)
  36. })
  37. })
  38. })
  39. return router
  40. }
  41. function initController(app) {
  42. const controllers = {}
  43. // 读取目录
  44. load('controller', (filename, controller) => {
  45. controllers[filename] = controller(app)
  46. })
  47. return controllers
  48. }
  49. function initService() {
  50. const services = {}
  51. load('service', (filename, service) => {
  52. services[filename] = service
  53. })
  54. return services
  55. }
  56. function loadConfig(app) {
  57. let config = {}
  58. load('config', (filename, conf) => {
  59. config = Object.assign(config, conf)
  60. })
  61. return config
  62. }
  63. const Sequelize = require('sequelize')
  64. function initModel(app) {
  65. const conf = app.$config
  66. if (conf.db) {
  67. app.$db = new Sequelize(conf.db)
  68. // 加载模型
  69. app.$model = {}
  70. load('model', (filename, { schema, options }) => {
  71. app.$model[filename] = app.$db.define(filename, schema, options)
  72. })
  73. app.$db.sync()
  74. }
  75. }
  76. function initMiddleware(app) {
  77. const conf = app.$config;
  78. if (conf.middleware) {
  79. conf.middleware.forEach(mid => {
  80. const midPath = path.resolve(__dirname, 'middleware', mid)
  81. // $app 为 koa 实例
  82. app.$app.use(require(midPath))
  83. })
  84. }
  85. }
  86. const schedule = require('node-schedule')
  87. function initSchedule(app) {
  88. const conf = app.$config;
  89. load('schedule', (filename, scheduleConfig) => {
  90. if (conf.schedule) {
  91. conf.schedule.forEach(job => {
  92. if (filename === job) {
  93. schedule.scheduleJob(scheduleConfig.interval, scheduleConfig.handler)
  94. }
  95. })
  96. }
  97. })
  98. }
  99. module.exports = {
  100. initRouter,
  101. initController,
  102. initService,
  103. loadConfig,
  104. initMiddleware,
  105. initModel,
  106. initSchedule
  107. }