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. 【uva 10048】Audiophobia(图论--Floyd算法)

    题意:有一个N点M边的无向带权图,边权表示路径上的噪声值.有Q个询问,输出 x,y 两点间的最大噪声值最小的路径的该值.(N≤100,M≤1000,Q≤10000) 解法:N值小,且问多对点之间的路径 ...

  2. hdu 6863 Isomorphic Strings 哈希+求公因子

    题意: t组输入,每组数据输入一个整数n,代表字符串长度.下面再输入一个字符串 你需要判断这个字符串能不能分成大于1段,且这些段的最小表示法是一样的 例如:abccab,它可以分成2段,分别是abc和 ...

  3. Strategic game POJ - 1463 树型dp

    //题意:就是你需要派最少的士兵来巡查每一条边.相当于求最少点覆盖,用最少的点将所有边都覆盖掉//题解://因为这是一棵树,所以对于每一条边的两个端点,肯定要至少有一个点需要放入士兵,那么对于x-&g ...

  4. Codeforces Round #672 (Div. 2) C1. Pokémon Army (easy version) (DP)

    题意:给你一组数\(a\),构造一个它的子序列\(b\),然后再求\(b_1-b2+b3-b4...\),问构造后的结果最大是多少. 题解:线性DP.我们用\(dp1[i]\)来表示在\(i\)位置, ...

  5. codeforces 7B

    B. Memory Manager time limit per test 1 second memory limit per test 64 megabytes input standard inp ...

  6. haut-1280 诡异的迷宫

    1280: 诡异的迷宫 时间限制: 2 秒  内存限制: 128 MB提交: 174  解决: 27提交 状态 题目描述 Simple最近刷题(打游戏)刷多了,一觉醒来发现自己到了一个迷宫里,怎么也出 ...

  7. POJ 2778 DNA Sequence(AC自动机 + 矩阵快速幂)题解

    题意:给出m个模式串,要求你构造长度为n(n <= 2000000000)的主串,主串不包含模式串,问这样的主串有几个 思路:因为要不包含模式串,显然又是ac自动机.因为n很大,所以用dp不太好 ...

  8. JavaScript中的对象引用和复制

    在JavaScript分为两种原始值和引用值类型,原始值之间的复制是值对值得复制,而引用类型则是引用对引用的复制: // 原始值的复制: let num1 = 1; let num2 = num1; ...

  9. 使用MCSManager搭建Minecraft服务器

    目录 一.准备工作 1.MCSManager Windows环境下安装 Linux安装 2.Minecraft服务端 3.Java 二.配置 1.登录面板 2.上传服务端 3.服务端的配置 三.开启服 ...

  10. Linux 如何查看一个文件夹下面有多少个文件

    Linux 如何查看一个文件夹下面有多少个文件 $ tree $ find ./ -type f | wc -l $ ls -l | grep "^-" | wc -l refs ...