node+koa2+mysql搭建博客后台

本文将详细讲解使用node+koa2+mysql搭建博客后台的全过程。

开发环境

  • node 8.3.0及以上
  • npm 5.3.0及以上
  • mysql 5.7.21

具体的环境配置可查看我的上一篇文章

准备工作

  • npm下载pm2(进程守护),并设置全局变量
  • 创建博客需要的数据库与表
    1. 开启mysql并创建数据库test: create database test;
    2. 切换到数据库testuse tests;,输入命令创建以下数据表:
//系统管理员表 t_user
CREATE TABLE `t_user` (`uid` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL COMMENT '姓名',`password` varchar(50) NOT NULL COMMENT '密码',`create_time` datetime NOT NULL COMMENT '注册时间',`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`is_delete` tinyint(1) DEFAULT '0',PRIMARY KEY (`uid`),UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;//笔记本表 t_note
CREATE TABLE `t_note` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL COMMENT '笔记本名',`uid` int(11) NOT NULL COMMENT 'uid',`create_time` datetime NOT NULL COMMENT '创建时间',`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`is_delete` tinyint(1) DEFAULT '0',PRIMARY KEY (`id`),UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;//博客记录表 t_blog
CREATE TABLE `t_blog` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`title` varchar(200) DEFAULT NULL COMMENT '标题',`uid` int(11) DEFAULT '1' COMMENT 'uid',`content` text COMMENT '内容',`create_time` datetime NOT NULL COMMENT '注册时间',`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`note_id` varchar(45) DEFAULT NULL,`publish` tinyint(4) DEFAULT '0' COMMENT '是否发布',`brief` text,`is_delete` tinyint(1) DEFAULT '0' COMMENT '是否删除',`ext_info` text,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;//图片上传记录表 t_img
CREATE TABLE `t_img` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`uid` int(11) NOT NULL COMMENT 'uid',`create_time` datetime NOT NULL COMMENT '注册时间',`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`name` varchar(40) DEFAULT NULL,`is_delete` tinyint(1) DEFAULT '0',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0  DEFAULT CHARSET=utf8;复制代码

node后台目录创建及npm包安装

  • 创建项目文件夹server,进入文件夹后初始化项目npm init
  • 在项目下创建以下文件夹和文件
  • 安装以下依赖包:
    1. koa node框架
    2. koa-bodyparser 表单解析中间件
    3. koa-router 路由框架
    4. koa-session 基于koa的session模块
    5. mysql 数据库
    6. md5 md5加密
    7. async-busboy 带文件的表单解析模块
    8. is 数据格式校验插件

node连接mysql

db/index.js配置文件如下:

var mysql = require('mysql');
let config = {host     : 'localhost',user     : 'root',password : '123456',database : 'test',port:3306,multipleStatements: true//允许多条sql同时执行
};
let pool = mysql.createPool(config);
let query = (sql, values) => {return new Promise((resolve, reject) => {pool.getConnection((err, connection) => {if (err) {reject(err)} else {connection.query(sql, values, (err, rows) => {if (err) {reject(err)} else {resolve(rows)}connection.end()})}})})
};
module.exports = {query
}
复制代码

其他配置

  • 框架公用方法,包括参数校验、登录态校验等。config/index.js
  • 框架接口返回码规范 config/tip.js

路由 Rouer

以上配置完成后,便可以开始写设计路由了。

引入相关配置

const router = require('koa-router')();
const Utils = require('../utils');
const Tips = require('../utils/tip');
const db = require('../db');
复制代码

根据表设计路由

  • user表 -> user.js 管理员管理,可登录、查询登录态、退出登录
  • note表 -> note.js 笔记本管理,可添加、修改、删除,查询笔记本列表
  • blog表 -> blog.js 博客管理 可添加、修改、删除、查询博客列表。每篇博客必须关联对应的笔记本。可根据笔记本查询博客列表
  • img表 -> img.js 图片管理,可上传、删除、查询图片列表

具体路由部分实现内容

注意:所有的删除操作均为将表字段is_delete设置为1即可,方便恢复数据

  • user.js 管理员

    1. 登录
        router.post('/oa/login', async (ctx, next) => {let data = Utils.filter(ctx.request.body, ['name', 'password']);let res = Utils.formatData(data,[{key:'name',type:'string'},{key:'password',type:'string'}]);if(!res) return ctx.body = Tips[1007];let { name, password } = data;let sql = 'SELECT uid FROM t_user WHERE name=? and password=? and is_delete=0', value = [name, md5(password)];await db.query(sql, value).then(res => {if (res && res.length > 0) {let val = res[0];let uid  = val['uid']ctx.session.uid = uid;ctx.cookies.set('uid', uid, {maxAge:86400000,httpOnly: true});ctx.body = {...Tips[0],data:{uid}};} else {ctx.body = Tips[1006];}}).catch(e => {ctx.body = Tips[1002];})});复制代码
    1. 查询登录信息
    router.get('/oa/user/auth', async (ctx, next) => {let uid = ctx.session.uid;let sql = 'SELECT name,uid,nick_name FROM t_user WHERE uid=? AND is_delete=0', value = [uid];await db.query(sql, value).then(res => {if (res && res.length > 0) {ctx.body = { ...Tips[0], data: res[0] };} else {ctx.body = Tips[1005];}}).catch(e => {ctx.body = Tips[1005];})
    });
    复制代码
  • note.js 笔记本管理

    1. 创建笔记本
    router.post('/oa/user/addNote',async (ctx,next)=>{let data = Utils.filter(ctx.request.body, ['name']);let {name} = data, uid = ctx.session.uid;let res = Utils.formatData(data, [{key: 'name', type: 'string'}]);if (! res) return ctx.body = Tips[1007];let create_time = Utils.formatCurrentTime();let sql = `INSERT INTO t_note(name,uid,create_time) VALUES(?,?,?)`,value = [name, uid, create_time];await db.query(sql, value).then(res => {let {insertId: id} = res;if (id) {ctx.body = {...Tips[0],data: {id}}} else {ctx.body = Tips[1002]}}).catch(e => {if(+e.errno === 1062){//笔记本不能重复ctx.body = {code: 1010,msg: '笔记本已存在!'};}else{ctx.body = Tips[1002]}})
    });
    复制代码
    1. (分页)查询笔记本列表
    router.get('/oa/user/myNote', async (ctx, next) => {let data = Utils.filter(ctx.request.query, ['pageSize', 'pageNum', 'type']), uid = ctx.session.uid;let res = Utils.formatData(data, [{key: 'type', type: 'number'},]);if (! res) return ctx.body = Tips[1007];let {pageSize = 15, pageNum = 1, type = 0} = data;pageSize = Number(pageSize);pageNum = Number(pageNum);let offset = (pageNum - 1) * pageSize;let sql1 = `SELECT count(1) FROM  t_note WHERE uid=${uid} AND is_delete=0;`,sql= `SELECT name,id,create_time,update_time  FROM  t_note WHERE uid=${uid} AND is_delete=0 ORDER BY create_time DESC`;if(+type === 1){sql += ` limit ${offset},${pageSize};`}await db.query(sql1+sql).then(async result => {let res1 = result[0],res2 = result[1],total = 0,list = []if(res1 && res1.length >0 && res2 && res2.length >0){total = res1[0]['count(1)']list = res2}ctx.body = {...Tips[0],data: {list,pageSize,total}};}).catch(e => {ctx.body = Tips[1002];})});
    复制代码
  • blog.js 博客

    1. 创建博客
    router.post('/oa/user/addBlog', async (ctx, next) => {let data = Utils.filter(ctx.request.body, ['title', 'content', 'tag_id', 'note_id', 'brief', 'publish', 'create_time']),uid = ctx.session.uid;let res = Utils.formatData(data, [{key: 'note_id', type: 'number'},{key: 'title', type: 'string'},{key: 'brief', type: 'string'},{key: 'content', type: 'string'},{key: 'publish', type: 'number'}]);if (! res) return ctx.body = Tips[1007];let {title = '无标题', content = '', note_id = '', brief = '', publish = 0, create_time = ''} = data;create_time = Utils.formatCurrentTime(create_time);let sql = `INSERT INTO t_blog(title,content,note_id,create_time,uid,brief,publish) VALUES (?,?,?,?,?,?,?)`,value = [title, content, note_id, create_time, uid, brief, publish];await db.query(sql, value).then(async res => {let {insertId: id} = res;ctx.body = {...Tips[0],data: {id}}}).catch(e => {ctx.body = Tips[1002];});});
    复制代码
    1. 分页查询博客,与笔记本列表查询类似
  • img.js 图片管理

    1. 上传图片
    router.post('/oa/user/upFiles', async (ctx, next) => {try {let data = await asyncBusboy(ctx.req), uid = ctx.session.uid;let { files = [] } = data;if(files.length === 0) return ctx.body = Tips[1002];let file = files[0];let { mimeType = '', filename, path: filepath } = file;if(mimeType.indexOf('image') === -1) return ctx.body = Tips[1002];let name = Date.now() + '.' + filename.split('.').pop();let savePath = path.join(__dirname, `../../img/${name}`);try {let create_time = Utils.formatCurrentTime();let sql = 'INSERT INTO t_user_img(name,uid,create_time) VALUES (?,?,?)', value = [name, uid, create_time];await db.query(sql, value).then(res => {let img = fs.readFileSync(filepath);fs.writeFileSync(savePath, img);fs.unlinkSync(filepath);//清除缓存文件ctx.body = {...Tips[0], data: { name }};}).catch(() => {ctx.body = Tips[1002];})} catch (e) {ctx.body = Tips[1005];}} catch (e) {ctx.body = Tips[1002];}
    });
    复制代码

    2.删除图片

    router.post('/oa/user/removeImg', async (ctx, next) => {let data = Utils.filter(ctx.request.body, ['name']), uid = ctx.session.uid;let res = Utils.formatData(data, [{ key: 'name', type: 'string' }]);if (!res) return ctx.body = Tips[1007];let { name } = data;let sql = 'UPDATE t_user_img set is_delete=1 WHERE name=? AND uid=?;', value = [name, uid];await db.query(sql, value).then(res => {fs.unlinkSync(path.join(__dirname, `../../img/${name}`));//清除缓存文件ctx.body = Tips[0];}).catch(() => {ctx.body = Tips[1002];})});
    复制代码
    1. 图片列表查询同笔记本列表查询

路由统一管理

router/index.js将所有的路由集成并挂载至app.js

  • router/index.js

    const user = require('./user');
    const note = require('./note');
    const blog = require('./blog');
    const img = require('./img');
    module.exports = function(app){app.use(user.routes()).use(user.allowedMethods());app.use(note.routes()).use(note.allowedMethods());app.use(blog.routes()).use(blog.allowedMethods());app.use(img.routes()).use(img.allowedMethods());
    }复制代码
  • app.js(对需要登录的路由统一管理)

    const http = require('http');
    const koa = require('koa');
    const etag = require('koa-etag');
    const session = require('koa-session');
    const bodyParser = require('koa-bodyparser');
    const errorHandler = require('koa-error');
    const compress = require('koa-compress');
    const PORT = process.env.PORT || 8080;
    const koaBody = require('koa-body');
    const app = new koa();
    const Utils = require('./utils');
    const router = require('./router');
    app.keys = ['session@&'];app.use(session({key: 'abc::sess',maxAge: 86400000,overwrite: true,httpOnly: true,signed: true,rolling: false
    }, app));
    app.use(koaBody());
    app.use(async(ctx, next) => {let {url = ''} = ctx;if(url.indexOf('/oa/user/') >-1){//需要校验登录态let check = Utils.checkLogin(ctx);if(check.code != 0) return ctx.body = check;}await next();});
    app.use(errorHandler());
    app.use(bodyParser());app.use(etag());// compressor
    app.use(compress({filter: contentType => /text|javascript/i.test(contentType),threshold: 2048
    }));
    router(app);
    http.createServer(app.callback()).listen(PORT);
    log('server is running on port: %s', PORT);
    复制代码

以上便是后台搭建全过程,点此查看后台源码。

阅读原文

前端项目源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/254995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ffmpeg使用x264编码的配置+ ffmpeg与 x264编码器参数完整对照表

ffmpeg使用x264编码的配置 ffmpeg与 x264编码器参数完整对照表 分类: 多媒体 2010-07-13 11:31 1072人阅读 评论(0) 收藏 举报 转载自:扶凯[http://www.php-oa.com] 本文链接: http://www.php-oa.com/2009/03/09/ffmpeg_x264.html 转帖:http://rhinghear…

javascript之变量

定义变量方式有var,let,const,变量的类型是根据变量的值来确定,变量的名称跟大多数语言一样,包含数字字母_$,不能以数字开头且区分大小写。 var 最早的定义变量的方式,可重复定义,但请避免这样做。 var a …

Maven理解

2019独角兽企业重金招聘Python工程师标准>>> Maven概念 参考maven入门 冰河winner Maven作为一个构建工具,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的操作接口&#xff0…

HALCON示例程序measure_circles.hdev测量圆的半径

HALCON示例程序measure_circles.hdev测量圆的半径 示例程序源码(加注释) 关于显示类函数解释 dev_update_off () read_image (Image, ‘circle_plate’) get_image_size (Image, Width, Height) dev_close_window () dev_open_window (0, 0, Width / 2…

OpenTLD 未完成 - 虎头

TLD是一种算法的简称,原作者把它叫做Tracking-Learning-Detection。搞视觉的人看到这个名字都会吓一跳,很ambitious的计划。是09年的工作,不算太久,不过也不太新。网上关于这个的资源其实很多,很大程度和作者开放源代码…

C# FileSystemWatcher 在监控文件夹和文件时的用法

********************************************************************************** 第一个文章: ********************************************************************************** 概述 最近学习FileSystemWatcher的用法,它主要是监控一个文件夹,当文件夹内的文件要…

比微软kinect更强的视频跟踪算法--TLD跟踪算法介绍

TLD (Tracking-Learning-Detection)是英国萨里大学的一个捷克籍博士生在其攻读博士学位期间提出的一种新的单目标长时间( long term tracking )跟踪算法。该算法与传统跟踪算法的显著区别在于将传统的跟踪算法和传统的检测算法相结合来解决被跟踪目标在被…

HALCON示例程序measure_circuit_width_lines_gauss.hdev电路板线宽检测

HALCON示例程序measure_circuit_width_lines_gauss.hdev电路板线宽检测 示例程序源码(加注释) 关于显示类函数解释 dev_update_off () dev_close_window () read_image (Image, ‘pcb_color’) get_image_size (Image, Width, Height) dev_open_window…

一张图看懂混合云数据同步一站式解决方案

摘要: 针对不同数据库间数据实时同步难的问题,日前,阿里云宣布推出混合云数据同步一站式解决方案,便于广大云产品用户实现实时数据同步的混合云支持,更为方便的是,该功能让本地Oracle也能实现与云上数据库的…

分布式性能测试

Jmeter分布式测试 在使用Jmeter进行性能测试时,如果并发数比较大(比如最近项目需要支持1000并发),单台电脑的配置(CPU和内存)可能无法支持,这时可以使用Jmeter提供的分布式测试的功能。 一、Jmeter分布式执行原理: 1、Jmeter分布式…

互斥锁pthread_mutex_t的使用

1. 互斥锁创建 有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下: pthread_mutex_t mutexPTHREAD_MUTEX_INITIALIZER; 在LinuxThreads实现中,pthread_…

无效设备解决办法

touch /etc/ini.d/FirstLoadkill 杀死枪弹柜程序 或重启reboot转载于:https://www.cnblogs.com/yygsj/p/5634384.html

HALCON示例程序measure_grid.hdev使用XLD分割键盘轮廓

HALCON示例程序measure_grid.hdev使用XLD分割键盘轮廓 示例程序源码(加注释) 关于显示类函数解释 dev_update_off () read_image (Image, ‘keypad’) get_image_pointer1 (Image, Pointer, Type, Width, Height) dev_close_window () dev_open_window…

[BZOJ3992]序列统计

DP一下,设$f_{i,j}$表示生成$i$个数且乘积$\%Mj$的方案数,则$f_{i1,l}\sum\limits_{jk\%Ml}[k\in S]f_{i,j}$ 我们很不希望DP式中下标的位置出现乘法,因为这样不好转移,考虑把乘法换成加法 因为模数$M$是质数,所以它有…

socket,TCP/IP的理解(转)

TCP/IP 要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准, 从字面意思来看TCP…

最小中间和

题目描述 给定一个正整数序列a1,a2,...,an,不改变序列中的每个元素在序列中的位置,把它们相加,并用括号记每次加法所得的和,称为中间和。编程:找到一种方法,添上n-1对括号,加法运算依括号顺序进…

HALCON示例程序measure_metal_part_extended.hdev金属零件尺寸测量

HALCON示例程序measure_metal_part_extended.hdev金属零件尺寸测量 示例程序源码(加注释) 关于显示类函数解释 dev_update_off () read_image (Image, ‘metal-parts/metal-parts-01’) init_visualization (Image, 3, ‘white’, ‘margin’, Width, …

双目匹配与视差计算

立体匹配主要是通过找出每对图像间的对应关系,根据三角测量原理,得到视差图;在获得了视差信息后,根据投影模型很容易地可以得到原始图像的深度信息和三维信息。立体匹配技术被普遍认为是立体视觉中最困难也是最关键的问题&#xf…

JavaEE 银联支付之网站支付-消费类交易

以银联网站支付 - 消费类交易 为例 0. 大致逻辑 前端request->后台封装参数->后台进行签名->生成跳转页面(包含表单提交内容)->响应前端(将生成的html写到浏览器中完成自动跳转打开银联支付页面) 复制代码1.acp_sdk.p…

react 开发知识准备

react react使用教程 babel babel 可用于ES6转换为ES5,jsx转换为原生js。 ES6 ES6 语法 webpack webpack打包工具,它把不同的、相互依赖的静态资源都视作模块,并且打包成我们想要的静态资源。让代码组织更清晰,一个文件就是一个模…