开发者社区> 咖啡机(K.F.J)> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

从零开始搞后台管理系统(2)——shin-server

简介:   shin 的读音是[ʃɪn],谐音就是行,寓意可行的后端系统服务,shin-server 的特点是:
+关注继续查看

  shin 的读音是[ʃɪn],谐音就是行,寓意可行的后端系统服务,shin-server 的特点是:

  • 站在巨人的肩膀上,依托KOA2bunyanSequelize等优秀的框架和库所搭建的定制化后端系统服务。
  • 一套完整的 Node.js 后端服务解决方案。
  • 调试便捷,实时打印出各类请求、日志和所有的查询语句。
  • 配合独立的配置文件可连接 MongoDB、MySQL 以及 Redis。
  • 已开辟脚本和定时任务目录,可将相应文件补充进来。
  • 容易扩展,可引入第三方库,例如队列、云服务等。

  与shin-admin配合使用的话,大致架构如下图。


7.png

准备工作


1)安装

  在将项目下载下来后,来到其根目录,运行安装命令,自动将依赖包下载到本地。

$ npm install

2)启动

  在启动服务器之前,需要确保本地已经安装并已开启 MongoDB、MySQL 以及 Redis。

  • mongo 启动命令:mongod
  • redis 启动命令:redis-server

  在 docs/SQL 中有个数据库文件,可初始化所需的表,并且注意将 config/development.js 中数据库的账号和密码修改成本机的。

  执行项目启动命令,成功后的终端如下图所示,端口号默认是 6060,可在 config/development.js 中自定义端口号。

$ npm start


8.png


  运行 http://localhost:6060/user/init 可初始化超级管理员账号,后台账号和权限都保存在 MongoDB 中,其他一些业务保存在 MySQL 中。

  • 账号:admin@shin.com
  • 密码:admin

3)运行流程

  当向这套后端系统服务请求一个接口时,其大致流程如下图所示。


9.png


目录结构


├── shin-server
│   ├── config --------------------------------- 全局配置文件
│   ├── db ------------------------------------- 数据库连接
│   ├── docs ----------------------------------- 说明文档
│   ├── middlewares ---------------------------- 自定义的中间件
│   ├── models --------------------------------- 数据表映射
│   ├── routers -------------------------------- api 路由层
│   ├── scripts -------------------------------- 脚本文件
│   ├── services ------------------------------- api 服务层
│   ├── static --------------------------------- 静态资源
│   ├── test ----------------------------------- 单元测试
│   ├── utils ---------------------------------- 公用工具
│   ├── worker --------------------------------- 定时任务
│   ├── app.js --------------------------------- 启动文件
│   ├── index.js ------------------------------- 入口文件
└───└── index-worker.js ------------------------ 任务的入口文件


1)app.js

  在启动文件中,初始化了 bunyan 日志框架,并声明了一个全局的 logger 变量(可调用的方法包括 info、error、warn、debug等) ,可随时写日志,所有的请求信息(引入了koa-bunyan-logger)、数据库查询语句、响应数据等,都会写入到服务器的日志中。

  JWT 认证 HTTP 请求,引入了 koa-jwt 中间件,会在 checkAuth.js 中间件(如下代码所示)中调用 ctx.state.user,以此来判断权限。而判断当前是否是登录会以 GET 方式向 ”api/user“ 接口发送一次请求。

  还引入了 routers() 函数(位于 routers 目录的 index.js 中),将 services 和 middlewares 两个目录下的文件作为参数传入,这两个目录下都包含 index.js 文件,引用方式为 middlewares.checkAuth()、 services.android 等。

 

import requireIndex from 'es6-requireindex';
import services from '../services/';
import middlewares from '../middlewares';
 
export default (router) => {
  const dir = requireIndex(__dirname);
  Object.keys(dir).forEach((item) => {
    dir[item](router, services, middlewares);
  });
};


2)config

  默认只包含 development.js,即开发环境的配置文件,可包含数据库的地址、各类账号密码等。

  使用node-config后,就能根据当前环境(NODE_ENV)调用相应名称的配置文件,例如 production.js、test.js 等。

3)db

  MySQL 数据库 ORM 系统采用的是 Sequelize,MongoDB 数据库 ORM系统采用的是 Mongoose,redis 库采用的是 ioredis

4)models

  声明各张表的结构,可用驼峰,也可用下划线的命名方式,函数的参数为 mysql 或 mongodb,可通过 mysql.backend 来指定要使用的数据库名称。


export default ({ mysql }) =>
  mysql.backend.define("AppGlobalConfig",
    {
      id: {
        type: Sequelize.INTEGER,
        field: "id",
        autoIncrement: true,
        primaryKey: true
      },
      title: {
        type: Sequelize.STRING,
        field: "title"
      },
    },
    {
      tableName: "app_global_config",
      timestamps: false
    }
);


  models 目录中的 index.js 文件会将当前所有的 model 文件映射到一个 models 对象中。

5)routers

  前端访问的接口,在此目录下声明,此处代码相当于 MVC 中的 Control 层。

  在下面的示例中,完成了一次 GET 请求,middlewares.checkAuth()用于检查权限,其值就是在 authority.js 声明的 id,ctx.body 会返回响应。


router.get(
  "/tool/short/query",
  middlewares.checkAuth("backend.tool.shortChain"),
  async (ctx) => {
    const { curPage = 1, short, url } = ctx.query;
    const { rows, count } = await services.tool.getShortChainList({
      curPage,
      short,
      url
    });
    ctx.body = { code: 0, data: rows, count };
  }
);


6)services

  处理数据,包括读写数据表、调用后端服务、读写缓存等。

  services 目录中的 index.js 文件会初始化各个 service 文件,并将之前的 models 对象作为参数传入。

  注意,MySQL中查询数据返回值中会包含各种信息,如果只要表的数据需要在查询条件中加 ”raw:true“(如下所示)或将返回值调用 toJSON()。

async getConfigContent(where) {
    return this.models.AppGlobalConfig.findOne({
      where,
      raw: true
    });
  }

7)scripts

  如果要跑脚本,首先修改 scripts/index.js 文件中的最后一行 require() 的参数,即修改 “./demo”。


global.env = process.env.NODE_ENV;
global.logger = {
  trace: console.log,
  info: console.log,
  debug: console.log,
  error: console.log,
  warn: console.log,
};
require('./demo');


  当前位置如果与 scripts 目录平级,则执行命令:

$ NODE_ENV=development node scripts/index.js

  其中 NODE_ENV 为环境常量,test、pre 和 production。

8)static

  上传的文件默认会保存在 static/upload 目录中,git 会忽略该文件,不会提交到仓库中。


开发步骤


  1. 首先是在 models 目录中新建对应的表(如果不是新表,该步骤可省略)。
  2. 然后是在 routes 目录中新建或修改某个路由文件。
  3. 最后是在 services 目录中新建或修改某个服务文件。


定时任务


  本地调试全任务可执行:

$ npm run worker

  本地调试单任务可执行下面的命令,其中 ? 代表任务名称,即文件名,不用加后缀。

$ JOB_TYPES=? npm run worker

  在 worker 目录中还包含两个目录:cronJobs 和 triggerJobs。

  前者是定时类任务 (指定时间点执行),使用了 node-schedule 库。

module.exports = async () => {
  //每 30 秒执行一次定时任务
  schedule.scheduleJob({ rule: "*/30 * * * * *" }, () => {
    test(result);
  });
};

  后者是触发类任务,在代码中输入指令触发执行,使用 agenda 库。

module.exports = (agenda) => {
  // 例如满足某种条件触发邮件通知
  agenda.define('send email report', (job, done) => {
    // 传递进来的数据
    const data = job.attrs.data;
    console.log(data);
    // 触发此任务,需要先引入 agenda.js,然后调用 now() 方法
    // import agenda from '../worker/agenda';
    // agenda.now('send email report', {
    //   username: realName,
    // });
  });
};


  注意,写好的任务记得添加进入口文件 index-worker.js。

require('./worker/cronJobs/demo')();
require('./worker/triggerJobs/demo')(agenda);


单元测试


  运行下面的命令就会执行单元测试。

$ npm test

  单元测试使用的框架是 mocha 3.4,采用的断言是 chai 4.0,API测试库是 supertest 3.0


// routers 测试
describe('GET /user/list', () => {
  const url = '/user/list';
  it('获取用户列表成功', (done) => {
    api
    .get(url)
    .set('Authorization', authToken)
    .expect(200, done);
  });
});

// serveices 测试
import backendUserRole from '../../services/backendUserRole';
describe('用户角色', () => {
  it('获取指定id的角色信息', async () => {
    const service = new backendUserRole(models);
    const res = await service.getInfoById('584a4dc24c886205bd771afe');
    // expect(2).toBe(2);
    // expect(res.rolePermisson).to.be.an('array');
  });
});

 


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
几行代码实现shiro超级管理员的功能
在项目开发中,需要实现shiro的超级管理员功能。 但网上查了一下相关实现,发现很多实现都是在用户获取权限资源时,如果是超级管理员,就将系统的全部权限,角色赋给用户。
1110 0
SQL Server 权限管理
原文:SQL Server 权限管理 标签:SQL SERVER/MSSQL SERVER/数据库/DBA/权限控制/管理/分配/登入名/数据库用户/角色 概述       对数据库系统而言,保证数据的安全性永远都是最重要的问题之一。
1377 0
SQL Server管理员专用连接的使用
原文:SQL Server管理员专用连接的使用   作为一名DBA,经常会处理一些比较棘手的服务无响应问题,鉴于事态的严重性,多数DBA可能直接用“重启”大法,以便尽快的恢复生产环境的正常运转,但是多数情况下我们却无法究其原因,即使产生了dump文件,没有微软的支持,我们同样被蒙在鼓里,幸好微软给我们留了一条“后路”,供我们在SQL服务无法连接时,使用它连接并查看“病因”,那就是DAC, dedicated administrator connection,管理员专用连接。
848 0
gen_server的模板
-module(first_gen_server).-behaviour(gen_server).-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
717 0
+关注
咖啡机(K.F.J)
每天进步一点点 研磨生活的香甜
350
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载