基于midwayjs编写的一个自动更新框架结构说明

midwayjs为eggjs的ts版本+依赖注入。这边由于接入的业务类型名,利用eggjs的插件形式,做成一个平台化,所有程序以插件的形式挂入的形式。所有模块单独进行模块管理。类似小程序。

  • TypeScript体系调研报告:https://juejin.im/post/59c46bc86fb9a00a4636f939
  • 淘宝开发实践:https://www.infoq.cn/article/L1yoBP2qbJ5yW*2D7Mv7
  • 语法:https://github.com/joye61/typescript-tutorial
  • javascript迁移:https://jkchao.github.io/typescript-book-chinese/typings/migrating.html
  • 装饰器:https://www.xiaoyulive.top/books/typescript/decorator-definition.html#%E8%A3%85%E9%A5%B0%E5%99%A8%E7%BB%84%E5%90%88
  • EGGJS文档:https://eggjs.org/zh-cn/intro/
  • EGGJS(typescript)文档:https://eggjs.org/zh-cn/tutorials/typescript.html
  • MIDWAY文档:https://midwayjs.org/midway/guide.html
  • MIDWAY依赖注入文档:https://midwayjs.org/injection/guide.html
  • 参考开源项目:
    只做参考,有兴趣再看

    • DoraCMS
      基于EGGJS(ES)
      一个完成度很高的CMS管理系统​
    • cool-admin
      基于EGGJS(TS)
      一个通用的后台管理系统,不过要付费。 这边有一部分源码,可以学习用。​
    • doodoo.js
      基于KOA
      一个微服务开源框架​
    • cabloy
      基于EGGJS(ES)
      一个全栈开发框架​
    • apshop
      基于EGGJS(ES)
      开源网店​
    • lin-cms-koa
      基于KOA
  • 自动更新结构说明
    • EGG 后台更新推送结构
      全部结构采用统一结构形式, 在补充后台的情况下,需要后台同时进行模块版本号的依赖管理。再推送配置列表到客户端

      • jy-tmall
        文件目录清单:
        /src/plugins/jy-tmall
        说明: 所有模块都以模块和独立的版本代号进行管理,更新,维护。在客户有特殊定制需求的情况下,可以复制,并调整名字做为一个新插件推送到客户端。在有时间的情况下,再考虑标准化。

        • version
          版本编号,不同时,则更新
        • type
          类型,暂定两种类型,一种是插件,一种是基础类型,更新形式相同,只有启动方式,程序插件需要做特殊化处理​
        • path
          压缩包解压路径
        • script
          程序初始化脚本,程序解压后,如果有指定初始化脚本,则会进行相应的初始化操作
        • download
          下载文件路径,做了权限判断,只有有权限的,才能下载到文件。​
      • jy-test
        文件目录清单:
        /src/plugins/jy-tmall

      • base_application
        文件目录清单:
        ​/src(除了/src/plugins目录)
        /​install.js
        /upgrade.js
        /common
        说明:
        系统基础程序结构,相对稳定,按需更新。​​​​​​

      • base_static
        文件目录清单:
        /public
        说明:​​
        更新频率很少​

      • base_node_modules
        文件目录清单:
        ​/node_modules
        ​/package.json
        /package-lock.json​
        说明:
        ​更新频率相对较少。同时插件目录本身也支持,也可以考虑直接在程序插件中进行特殊化管理。​

    • EGG TYPESCRIPT 自动更新结构整理
      • common
        程序相关辅助数据

        • sql
          程序建表语句

          • table
            • goods
            • test
              • mssql
                • JKPT_TEST.sql
                  建表语句,可以包含字段
                • JKPT_TEST_COLUMN_ADD_TEST.sql
                  字段扩张,相当于用于不同版本的字段修正
              • oracle
                • JKPT_TEST.sql
                • JKPT_TEST_COLUMN_ADD_TEST.sql
      • data
        系统数据,不对外, 系统生成

        • plugins
          插件相关文件数据

          • jy-tmall
          • jy-test
        • download
          下载的数据​
      • dist
        程序自动编译目录
      • local_test
        本地测试目录
      • node_modules
        node 基础模块
      • public_file
        插件相关下载,同时需要对外的数据,必须放这里

        • uploads
          • plugins
            • jy-tmall
            • jy-test
      • public
        静态资源文件,且对外。​
      • run
        ​系统运行时文件,用于分析系统当前依赖的情况
      • src
        系统源码目录,插件和系统主程序代码都放在这个目录中。插件目录移动到外面,会出现路由映射等识别不到的问题​

        • app
          • controller
            • base
              基础模块

              • BaseController.ts
                控制器命名规则:骆驼命名+Controller.ts
                ​基础控制器,其它控制器继承于这个文件
                ​内置通用频率拦截,用于当计划任务使用时或防止异常扫描时,进行数据拦截
                所有控制器,都通过声明式语法进行路由指定和控制:https://midwayjs.org/midway/guide.html#web-%E4%B8%AD%E9%97%B4%E4%BB%B6​​
          • extend
            需要声明:/src/index.d.ts
            ​框架扩展

            • context.ts
              上下文扩展
            • request.ts
              请求扩展
          • mode
            需要声明:/src/index.d.ts
            ​按需要,构建数据模型​
          • schedule
            需要声明:/src/index.d.ts
            声明结构:[{title:“标题”,url:”请求地址”}​]​
            ​通用计划任务,所有计划任务挂到/src/config/config.default.ts:config.tasks上,计划任务会以指定频率,进行异步请求,频率限制直接通过base.limitAccess(second)限制,也可考虑再补充参数到计划任务中。实现一个计划任务管理器,管理所有任务列表。​
          • view
            模板文件
          • router.ts
            路由器暂时只有socket使用。
        • config
          • config.default.ts
            配置文件,不同启动方式,可以配置不同的配置文件
          • plugin.ts
            插件配置文件,内置插件基本不用调整,需要的插件按需添加。同时做了特殊处理,会将/config.json:plugins插件列表的插件加载进来.​
        • lib
          公共代码库

          • common
            通用库,暂时是用js,主要保留js脚本需要执行特殊代码如更新和初始化时使用,同时挂到了全局​
            常用工具库​
            调用方式:
            ts:​globalThis.common​
            js:common​

            • arrayHelper.js
              数组
            • commonHelper.js
              通用函数
            • dbHelper.js
              通用数据库操作
              如果是更新脚本和初始化脚本访问数据库时,需要先common.dbHelper.getDb()进行初始化,数据库配置在:/config.json:db​​
            • fileHelper.js
              通用文件操作
            • httpHelper.js
              通用网络请求:下载,GET,POST,JSON,请求编码,请求解码
            • imgHelper.js
              通用图象处理:二维码,条码等
            • numberHelper.js
              通用数字操作:精度计算(精度计算提供了公式封装)
            • stringHelper.js
              通用字符串操作
            • timeHelper.js
              通用时间操作
        • plugins
          程序插件目录,每个程序插件,都可以做为一个独立的程序,挂到接口平台。只要在/config.json 中有,启动时会自动挂进来,并进行相关初始化。 前台可完成访问
          所有ts程序,必须放在/src目录中。​

          • jy-test
            • app
              • controller
                • GoodsController.ts
                  不同模块,控制器名可相同,需要进行@provide(‘/jy-test/GoodsController’)声明作用域
              • public
                程序插件静态资源文件,下载和上传的数据。必须放/public,不能放这里。避免更新时的覆盖。通过/src/plugins/jy-test/app.ts初始化,挂静态路径进来。
              • service
                需要进行声明
                ​不能出现同名的业务类,需要按模块,按插件,进行变量名规范。如:jyTestUser.ts
              • view
                程序插件模板目录
            • app.ts
              文档:https://eggjs.org/zh-cn/basics/app-start.html
              ​​egg初始化时,在声明周期需要进行特殊处理的地方。
              程序插件在启动初始化时,会挂菜单,挂计划任务​, 挂插件静态目录映射等,按需要调整。
            • install_plugin.js
              数据初始化脚本,当更新和安装时,如果推送的配置有指定更新脚本,则会执行。
              初始化时,尽量利用knex的兼容性(判断表,字段,视图等是否存在,以及字段长度,类型),大代码块或knex处理麻烦的以sql形式放置(建表)(knex搞太多东西,会带来一定的阅读困难),再通过knex.raw进行代码块的执行。
              ​/common/sql/table/test/mssql/JKPT_TEST.sql
              /common/sql/table/test/mssql/JKPT_TEST_COLUMN_ADD_TEST.sql​
            • package.json
              程序插件配置
          • jy-tmall
            插件目录结构相同

        • service
          • admin
            业务模块

            • AdminAdminService.ts
              命名规则:插件名+业务名+Service.ts
              ​业务类,不能出现同名,需要定个规定,按模块和插件进行业务类型命名.相关接口声明,也需要按类似形式,避免不规范导致的冲突
        • app.ts
          文档:https://eggjs.org/zh-cn/basics/app-start.html
          ​​egg初始化时,在声明周期需要进行特殊处理的地方。
          程序插件在启动初始化时,会挂菜单,挂计划任务​, 挂插件静态目录映射等,按需要调整。
        • global.d.ts
          全局声明​
        • index.d.ts
          通用声明,局部声明,需要按需单独防止。命名格式:*.d.ts
        • install_base.js
          程序初始化
          ​脚本初始化全部做了特殊处理,支持单独测试。​
      • test
        单元测试
      • config.json
        系统配置文件,由这边建立,非EGG自带。
        进行数据库配置,用户信息配置。​
        程序插件以及各模块的版本记录
        程序更新是以该文件的版本号为判断依据。同时/src/plugins的插件引入也是通过该文件读取​​
      • install.js
        脚本初始化全部做了特殊处理,支持单独测试。​
        将更新脚本独立出现,方便测试。/upgrade.js在接收到推送时,再调用该脚本​
      • upgrade.js
        程序更新脚本,会启动一个socket,并绑定后台的企业帐号。如果有推送更新,则执行/install.js脚本进行安装.

 

eggjs-框架, 最近入了nodejs的坑…写给新东家的新框架,基于eggjs

 框架结构

  • app[框架主目录]
    • controller[控制器]
      控制器已实现路由的自动装载,只需要建立具体的控制器. 具体功能业务按需建立目录. 不要将不相干的业务揉在同一模块,同一控制器里.

      • admin[业务模块]
        • admin.js[控制器]
        • system.js[控制器]
    • extend[扩展]
      扩展目录中,可以给控制器,请求类,上下文扩展通用函数(https://eggjs.org/zh-cn/basics/extend.html)
    • io[socket控制器]
      特别注意,静态资源,控制器请求以及io请求,都是同一个端口.通过特殊解析实现具体访问.

      • controller[socket控制器]
        • client[socket模块]
          保留模块, 后续新socket编写需要单独建立模块目录,并在具体动作中实现业务逻辑. 同样在egg的基础上补充了自动路由装载的实现.

          • client.js[控制器]
            这个socket控制器为保留控制器, 为兼容原客户端的socket请求以及回调的特殊处理类. 所有原socket请求都会通过映射, 跳转到/app/service/client/*里的业务类进行特殊处理,并回调.原请求方式. io.emit(‘message’,{api:’login’,param:{username:’xxx’,password:’xxx’}},function(res){})新请求方式. io.emit(‘/admin/admin/login​​’,{param:{username:’xxx’,password:’xxx’}},function(res){})需要明确注意, socket本质上是不存在回调的. 只是socket.io-client内部和服务器实现了packetid为标志的特殊实现. 其它任何语言都没有…但然. 通过原理编写也行..原理:https://www.cnblogs.com/lightsong/p/10226940.html​​
      • extend[socket扩展]
        同/app/extend
      • middleware[socket中间件]
        • connection.js[连接中间件]
          建立链接时, 可以做权限校验
        • packet.js[数据包中间件]
          数据请求过程中触发
    • lib[工具类]
      引用方式按最原始的方式引用. 不封装引用的主要目的是保留代码提示.

      • common[通用工具目录]
        • commonHelper.js[原通用类 ]
          按需要加,不需要的不要乱加进来,明确功能的.建立目录单独存放​
        • expressHelper.js[快递通用类]
          快递一些通用的获取调用, 在各自模块中实现, 并提供统一调用. 通用类,根据类型去调用具体的帮助类, 减少过程式代码, 增加代码可读性
      • cngg[功能模块目录]
        不同于业务, 一般功能模块都有其特殊实现和封装. 单独放这里. 业务功能再在业务目录里实现. 最大限度避免代码混乱. 当扩展达到一定的重复性和可用性, 可以考虑写成插件

        • cnggHelper.js[菜鸟裹裹帮助类]
      • kdn[快递鸟目录]
    • middleware[中间件]
      个人建议称为中间处理件会相对好理解, 需要先了解下洋葱模型(https://eggjs.org/zh-cn/basics/middleware.html), 类似过滤器

      • commonRequest.js[通用请求中间件]
        将常规请求进行前置处理, 如get和body合并到ctx.request.params中. 不区分get和post.
      • mwAdminUser.js[用户请求权限中间件]
        实现用户的权限判断和拦截
    • model[数据模型]
      暂时使用的数据库是mongodb, 然后暂时选用的访问方式为mongose(https://mongoosejs.com/docs/guide.html), 同时提供原访问方式, 方便快速复用原代码(尽量能写成新的写成新的…别只复制粘贴…)

      • Admin[业务模块]
        特别注意,其它目录都是小写开头, 只有数据模型的目录和类是大写开头….

        • Admin.js[数据模型类]
          数据层可复用代码,尽量复用到数据模型. 业务层复用业务模型. 尽量减少重复代码. 如可以写一个函数load(id), 找不到直接报错. 后续通用调用即可.
    • public[静态文件]
      egg-static提供静态文件访问支持​, 同端口访问时. 如果路径为/public/则会自动进入该目录​, 客户端和小访问量服务器可以直接使用. 大访问量, 需要实际压力测试看看. 最好还是走nginx做反向代理.

      • assets[静态资源目录]
      • backstage[后端静态资源目录,主要是组件类封装]
        复制原资源文件, 暂时按习惯的来
      • upload[上传文件目录]
    • router[路由配置]
      • router.dev.js
        路由实际配置脚本, 自动化脚本能满足基本路由情况, 如果需要特殊路由或者兼容原路由. 可以单独在这个地方再补充映射https://eggjs.org/zh-cn/basics/router.html
    • schedule[计划任务]
      计划任务只需要复制即可使用, 但是发现需要重启服务才会引入, 需要研究下热部署?
    • service[业务]
      需要理解下新的设计分层, 该形式接近MVC, 但是不是MVC.常规MVC是模型,视图,控制器, egg 结构层是 数据层(model), 控制器(controller), 业务(service), 视图(view), 控制器只做权限以及参数校验, 具体功能封装到业务层中. ​主要目的是实现代码的高复用性.如登陆,封装成service.user.login(username,password), 后续任何地方都是调用这个业务, 即可实现登陆. ​同时后续的单元测试, 主要是测试业务类.同时, 业务需要以抛出异常的形式代替返回值判断, 并再最外层进行异常处理. 可大大增加代码的可读写以及编写. 避免没必要的各种判断​

      • admin[业务模块]
        • adminService.js[业务类]
          结构类似控制器, 全是只为处理具体业务代码
      • client[保留模块]
        原客户端的io调用使用的方式和egg-socket不一样. 未避免冲突, 单独配置一个io路由,并将原请求动作全部映射到这里的具体业务动作. 同时兼容了socket.io-client的回调

        • enterpriseService.js[原客户端所有socket调用]
    • view[视图]
      所有页面文件以*.ejs做后缀, webstorm,vscode等格式化和开发过程中<% %>会有代码提示, 会极大的提高开发效率, 测试过程中, 也可以考虑页面代码在<%%>中测试再贴回去. 页面保存是实时更新的. nodejs其它功能模块修改在ssd的情况下, 依然需要4,5 秒的重启

      • admin[模块目录]
        • site[控制器目录]
          • login.ejs[登陆页面]
            页面对应具体的控制器
      • layout[通用页面组件]
        • layout.ejs[图层]
          包含常用的静态资源文件引入, 控制器里渲染页面时, 图层会包裹具体的页面代码, 可以方便全局性调整. 对于可复用的页面组件, 都可以考虑封装成*.ejs或者作成vue组件, 按需要来
      • 200.ejs[通用消息提示页面]
      • 500.ejs[通用错误消息提示页面]
    • router.js[路由脚本]
      用于引用/app/router里的实际路由脚本​, 路由脚本在egg的基础上加了自动配置, 只需要复制或新增控制器, 就会自动添加路由
  • test[单元测试目录]
  • local_test[自己编写的框架外的测试脚本]
  • cache[缓存目录]
    如果使用缓存, 则目标缓存会生成到该路径下, 如果服务器有多节点. 缓存必需存放到redis或数据库, 不能配置单机的fs或内存缓存. 除非是代码或页面类缓存,视具体情况选择
  • config[配置目录]
    • locale[翻译目录]
      • zh-CN.js[翻译语言配置脚本]
        egg-validate的翻译,需要在这个地方进行配置
    • config.default.js[默认配置]
    • config.dev.js[具体环境配置脚本]
      可根据启动命令不同,加载不同配置文件,方便测试
    • plugin.js[插件配置]
      框架使用插件,必须在这个地方启用, 然后如果有特殊参数配置,在/config/config.default.js里进行详细参数配置. egg默认启用的插件需要到/node_modules/eggjs 里进行查看.
  • logs[日志目录]
    • test_egg[项目名]
      • common-error.log[常规日志]
      • egg-agent.log[agent日志]
      • egg-schedule.log[计划任务日志]
      • egg-web.log[egg内部日志]
  • package.json[配置文件]
    • 启动命令
      • 启动开发环境:npm run dev
        • 实际脚本:egg-bin dev –sticky –port 7001
      • 启动生产环境:npm run start
        • 实际脚本:egg-scripts start –daemon –sticky –port 7001
    • 启动模式
      • 框架是以 Cluster 方式启动的,而 socket.io 协议实现需要 sticky 特性支持,否则在多进程模式下无法正常工作。
    • 注意事项
      • 最好指定端口, 否则开发工具崩溃后, 重新运行, 会被重新分配端口, 避免带来不必要的问题
  • 框架保留问题, 需要找时间进行优化
    • 框架文件过多(超过3w+文件…)
      • 优化方向:减少不必要的扩展.
    • 重启速度需要再进行研究优化,在ssd的情况下, 重启服务需要大概4,5秒, 较影响开发, 不过配合调试模式, 可以弥补.
      • 优化方向:考虑单独写个启动脚本, 抛弃一些特性来实现热更新, 具体再观望看看别人有没有其它实现. 尽可能实现修改后马上可见.
    • 页面组件化
      • 优化方向:无
        • 还在学习和了解中.