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

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

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

  

准备工作

1)安装

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

$ npm install

2)启动

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

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

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

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

$ npm start

  

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

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

3)运行流程

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

  

目录结构

├── 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');
});
});
 

从零开始搞后台管理系统(2)——shin-server的更多相关文章

  1. 从零开始搞后台管理系统(1)——shin-admin

      shin 的读音是[ʃɪn],谐音就是行,寓意可行的后台管理系统,shin-admin 的特点是: 站在巨人的肩膀上,依托Umi 2.Dva 2.Ant Design 3和React 16.8搭建 ...

  2. vue从入门到女装??:从零开始搭建后台管理系统(二)用vue-docute生成线上文档

    教程 vue从入门到女装??:从零开始搭建后台管理系统(一)安装框架 一个系统开发完成了总要有操作说明手册,接口文档之类的东西吧?这种要全部纯手写就很麻烦了,可以借助一些插件,比如: vue-docu ...

  3. vue从入门到女装:从零开始搭建后台管理系统(一)安装框架

    安装及运行都是基于node的,不会node的可以自行百度,网上教程很多,也不难 项目效果预览: demo1 demo2 源码下载 开始安装框架: vue ==>vue-cli安装   eleme ...

  4. 从零开始编写自己的C#框架(8)——后台管理系统功能设计

    还是老规矩先吐下槽,在规范的开发过程中,这个时候应该是编写总体设计(概要设计)的时候,不过对于中小型项目来说,过于规范的遵守软件工程,编写太多文档也会拉长进度,一般会将它与详细设计合并到一起来处理,所 ...

  5. 从零开始实现放置游戏(六)——实现后台管理系统(4)Excel批量导入

    前面我们已经实现了在后台管理系统中,对配置数据的增删查改.但每次添加只能添加一条数据,实际生产中,大量数据通过手工一条一条添加不太现实.本章我们就实现通过Excel导入配置数据的功能.这里我们还是以地 ...

  6. 从零开始搭建vue+element-ui后台管理系统项目到上线

    前言 之前有些过移动端的项目搭建的文章,感觉不写个pc端管理系统老感觉少了点什么,最近公司项目比较多,恰巧要做一个申报系统的后台管理系统,鉴于对vue技术栈比较熟悉,所以考虑还是使用vue技术栈来做: ...

  7. (菜鸟要飞系列)一,基于Asp.Net MVC5的后台管理系统(前言)

    今天真是个郁闷的日子,因为老师两个星期前给我的一个任务,用递归算法将Oracle数据库中用户信息及权限显示在jquery-treeView上,网上虽然有大神写出了这类算法,但是不贴全部代码,真的很难跟 ...

  8. vue重构后台管理系统调研

    Q4要来了,我来这家公司已经一个季度了,通过对公司前端框架的整体认识,对业务的一些认识,发现,这些东西也都是可以重构,无论是v2,还是v3的代码. 首先就要那后台管理来开刀来,现有的技术框架就是php ...

  9. Serverless + Egg.js 后台管理系统实战

    本文将介绍如何基于 Egg.js 和 Serverless 实现一个后台管理系统 作为一名前端开发者,在选择 Nodejs 后端服务框架时,第一时间会想到 Egg.js,不得不说 Egg.js 是一个 ...

随机推荐

  1. Codeforces Round #651 (Div. 2) D. Odd-Even Subsequence(二分)

    题目链接:https://codeforces.com/contest/1370/problem/D 题意 给出一个含有 $n$ 个数的数组 $a$,从中选出 $k$ 个数组成子序列 $s$,使得 $ ...

  2. 2019HDU多校 Round6

    Solved:2 02 Nonsense Time (LIS) 题意:给定一个全排列 最开始为空的 每秒中一个位置上的数出现 求每秒的LIS 题解:题解说 考虑时光倒流 倒着消掉 因为数据随机 所以期 ...

  3. Codeforces Round #547 (Div. 3) E. Superhero Battle (数学)

    题意:有一个HP为\(h\)的大怪兽,你需要轮流进行\(i\)次操作.每次可以使\(h+=d_i\)(\(d_i\)有正有负),当第\(n\)次操作完成后,再从第一次开始,问能否使得怪兽的HP变为\( ...

  4. 文件的读写(cpp)

    文件的读写(cpp) c++中要进行文件的读入,首先要包含一个头文件 fstream . 输出到文件 为打开一个可供输出的文件需要定义一个ofstream 对象并将文件名传入: std::ofstre ...

  5. LEETCODE - 1228【等差数列中缺失的数字】

    C++: class Solution { public:     int missingNumber(vector<int>& arr) {         int subnum ...

  6. map最最最基本用法

    map<a,b>c中,a,b是变量类型 参数定义的map的名字 #include<stdio.h> #include<map> //头文件 map<int,c ...

  7. select(),fd_set(),fd_isset()

    1. select函数 1. 用途 在编程的过程中,经常会遇到许多阻塞的函数,好像read和网络编程时使用的recv, recvfrom函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在 ...

  8. what's the difference amount of pdf, epub, and mobi format

    what's the difference amount of pdf, epub, and Mobi format What is the difference between pdf, epub ...

  9. no need jQuery anymore & You don't need jQuery anymore!

    no need jQuery anymore & You don't need jQuery anymore! "use strict"; /** * * @author ...

  10. CORS All In One

    CORS All In One 跨域资源共享 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS CORS 请求类型 简单请求 预检请求 Ac ...