В этой статье подробно объясняется весь процесс использования node+koa2+mysql для создания фона блога.
среда разработки
- узел 8.3.0 и выше
- нпм 5.3.0 и выше
- mysql 5.7.21
Конкретная конфигурация среды может видеть моипредыдущий пост
Готов к работе
- npm загрузить pm2 (процесс-демон) и установить глобальные переменные
- Базы данных и таблицы, необходимые для создания блога
- Запустите mysql и создайте тест базы данных:
create database test;
- Переключиться на тест базы данных
use tests;
Команда ввода создает следующий лист данных:
- Запустите mysql и создайте тест базы данных:
//系统管理员表 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;
Создание фонового каталога узла и установка пакета npm
- Создайте сервер папки проекта и инициализируйте проект после входа в папку
npm init
. - Создайте следующие папки и файлы в рамках проекта
- Установите следующие зависимости:
- структура узла коа
- koa-bodyparser промежуточное ПО для разбора форм
- инфраструктура маршрутизации koa-router
- koa-session модуль сеанса на основе koa
- база данных mysql
- md5 шифрование md5
- Модуль разбора формы async-busboy с файлами
- плагин проверки формата данных
Узел подключения 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
}
Другая конфигурация
- Публичные методы Framework, включая проверку параметров, проверку статуса входа и т. д.config/index.js
- Спецификация кода возврата интерфейса Frameworkconfig/tip.js
Маршрутизатор
После завершения вышеуказанной конфигурации вы можете начать писать дизайн маршрута.
Импорт связанной конфигурации
const router = require('koa-router')();
const Utils = require('../utils');
const Tips = require('../utils/tip');
const db = require('../db');
Проектировать маршруты на основе таблиц
- пользовательская таблица -> управление администратором user.js, может войти в систему, запросить статус входа в систему, выйти из системы
- таблица заметок -> управление блокнотом note.js, вы можете добавлять, изменять, удалять, запрашивать список блокнотов
- Таблица блогов -> blog.js Управление блогами Вы можете добавлять, изменять, удалять и запрашивать список блогов. Каждый блог должен быть связан с соответствующей записной книжкой. Список блогов можно запросить на основе блокнотов
- Таблица img -> управление изображениями img.js, загрузка, удаление, запрос списка изображений
Бетонная маршрутная часть
Примечание. Все операции удаления должны устанавливать поле таблицы is_delete в 1, что удобно для восстановления данных.
-
Администратор user.js
- Авторизоваться
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]; }) });
- Запрос информации для входа
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
- Создать блокнот
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] } }) });
- (Разбивка на страницы) Запрос списка записных книжек
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
- Создать блог
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]; }); });
- Блог запросов с разбивкой на страницы, аналогичный запросу списка записных книжек
-
img.js управление изображениями
- загрузить изображение
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]; }) });
- Запрос списка изображений аналогичен запросу списка записных книжек.
Единое управление маршрутизацией
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);
Выше описан весь процесс построения фона.Нажмите здесь, чтобы просмотреть фоновый исходный код.