许愿墙的后台管理系统主要有4个模块:登录模块、首页模块、许愿管理模块和管理员管理模块。使用前后端分离方式,后端接口使用Express框架,前端使用Vue框架,页面使用Element组件。这节先实现后端接口。
3.1 实现接口
1)登录验证:如果正确,返回登录成功信息和当前登录的管理员信息;如果错误,则返回提示信息;
2)许愿列表:分页返回许愿信息,可通过姓名、创建时间筛选;
3)单条许愿信息:获取某一条许愿信息;
4)新增许愿:添加一条新的许愿信息;
5)修改许愿:修改某一条许愿信息;
6)删除许愿:删除某一条许愿信息;
7)管理员列表:分页返回管理员信息,可通过用户名、姓名、角色筛选;
8)单条管理员信息:获取某一条管理员信息;
9)新增管理员:添加一个新的管理员信息(同一用户名只能添加一次);
10)修改管理员:修改某一条管理员信息;
11)删除管理员:删除某一条管理员信息;
12)除登录外,所有接口须验证是否登录。
3.2 创建MySQL数据库表
本节仍然使用上节创建的数据库wish和数据表wish,另外再创建一张数据表admin,用来存放管理员信息。
admin表各字段及其作用:
可以添加一些模拟数据:
3.3 创建后端项目
3.3.1 生成项目目录wish-admin-api
3.3.2 安装依赖包
3.3.3 更改默认端口
默认端口为 3000,为了方便演示以及避免与其他项目冲突,将端口号改为 3002。
3.3.4 增加配置信息
//config.js
const config = {
DEBUG: true,
MYSQL: {
host: 'localhost',
database: 'wish',
username: 'root',
password: '123456'
}
};
module.exports = config;
3.3.5 增加数据库配置信息
//db.js
const Sequelize = require('sequelize');
const config = require('./config');
const sequelize = new Sequelize(
config.MYSQL.database,
config.MYSQL.username,
config.MYSQL.password, {
host: config.MYSQL.host,
dialect: 'mysql',
logging: config.DEBUG ? console.log : false,//是否打印日志
pool: {//配置数据库连接池
max: 5,
min: 0,
idle: 10000
},
timezone: '+08:00'//时区设置
}
);
module.exports = sequelize;
3.3.6 增加常量信息
//constant/constant.js
const constantObj = {
//请求成功
DEFAULT_SUCCESS: {
code: '00',
msg: '操作成功'
},
// 请求失败
DEFAULT_ERROR: {
code: '01',
msg: '操作失败'
},
//缺少必要参数
LACK: {
code: '02',
msg: '缺少必要参数'
},
// Token验证失败
TOKEN_ERROR: {
code: '03',
msg: 'Token验证失败'
},
// 用户名或密码错误
LOGIN_ERROR: {
code: '04',
msg: '用户名或密码错误'
},
// 管理员信息不存在
ADMIN_NOT_EXSIT: {
code: '05',
msg: '管理员信息不存在'
},
// 许愿信息不存在
WISH_NOT_EXSIT: {
code: '06',
msg: '许愿信息不存在'
},
//数据已存在
DATA_EXSIT: {
code: '07',
msg: '数据已存在'
}
};
module.exports = constantObj;
3.3.7 增加数据库映射信息
//models/wish.js
const Sequelize = require('sequelize');
const db = require('../db');
const Wish = db.define('Wish', {
id: {type: Sequelize.INTEGER, primaryKey: true, allowNull: false, autoIncrement: true},
name: {type: Sequelize.STRING(20), allowNull: false},
content: {type: Sequelize.STRING, allowNull: false}
}, {
underscored: true,
tableName: 'wish'
});
module.exports = Wish;
//models/admin.js
const Sequelize = require('sequelize');
const db = require('../db');
const Admin = db.define('Admin', {
id: {type: Sequelize.INTEGER, primaryKey: true, allowNull: false, autoIncrement: true},
username: {type: Sequelize.STRING(20), allowNull: false},
password: {type: Sequelize.STRING(36), allowNull: false},
name: {type: Sequelize.STRING(20), allowNull: false},
role: {type: Sequelize.INTEGER, allowNull: false},
lastLoginAt: {type: Sequelize.DATE}
}, {
underscored: true,
tableName: 'admin'
});
module.exports = Admin;
3.3.8 增加公共方法
//controllers/common.js
const async = require('async');
const Constant = require('../constant/constant');
const exportObj = {
clone,
checkParams,
autoFn
};
module.exports = exportObj;
/**
* 克隆方法,克隆一个对象
* @param obj
* @returns {any}
*/
function clone(obj){
return JSON.parse(JSON.stringify(obj));
}
/**
* 校验参数方法
* @param params 请求的参数集
* @param checkArr 需要验证的参数
* @param cb 回调
*/
function checkParams(params, checkArr, cb){
let flag = true;
checkArr.forEach(val => {
if(!params[val]){
flag = false;
}
});
if(flag){
cb(null);
}else {
cb(Constant.LACK);
}
}
/**
* 返回JSON格式数据
* @param tasks 当前controller执行的tasks
* @param res Response对象
* @param resObj 当前controller返回json对象
*/
function autoFn(tasks, res, resObj){
async.auto(tasks, err => {
if(err){
console.log(JSON.stringify(err));
res.json({
code: err.code || Constant.DEFAULT_ERROR.code,
msg: err.msg || JSON.stringify(err)
})
}else {
res.json(resObj)
}
});
}
3.3.9 增加Token处理方法
//controllers/token.js
const jwt = require('jsonwebtoken');
// 设置一个密钥,用来加密和解密token
const tokenKey = 'XfZEpWEn? ARD7rHBN';
const Token = {
/**
* 加密
* param data 需要加密在Token中的数据
* param time Token的过期时间,单位为s
* returns {*} 返回一个Token
*/
encrypt(data, time){
return jwt.sign(data, tokenKey, {expiresIn: time});
},
/**
* 解密
* param token 加密之后的Token
* returns 返回对象
* {
* token: boolean, //(true表示Token合法,false则表示不合法)
* data: * //(解密出来的数据或错误信息)
* }
*/
decrypt(token){
try {
let data = jwt.verify(token, tokenKey);
return {
token: true,
data: data
}
}catch(err){
return {
token: false,
data: err
}
}
}
};
module.exports = Token;
3.3.10 增加路由处理方法
//controllers/index.js
const dateFormat = require('dateformat');
const Common = require('./common');
const AdminModel = require('../models/admin');
const Constant = require('../constant/constant');
const Token = require('./token');
// 设置默认Token过期时间,单位为s
const TOKEN_EXPIRE_SENCOND = 36000;
let exportObj = {
login
};
module.exports = exportObj;
//登录
function login(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.body, ['username', 'password'], cb);
},
query: ['checkParams', (results, cb) => {
AdminModel.findOne({
where: {
username: req.body.username,
password: req.body.password
}
}).then(result => {
if(result){
resObj.data = {
id: result.id,
username: result.username,
name: result.name,
role: result.role,
lastLoginAt: dateFormat(result.lastLoginAt, 'yyyy-mm-dd HH:MM:ss'),
createdAt: dateFormat(result.createdAt, 'yyyy-mm-dd HH:MM:ss')
};
const adminInfo = {
id: result.id
};
// 生成Token
let token = Token.encrypt(adminInfo, TOKEN_EXPIRE_SENCOND);
resObj.data.token = token;
cb(null, result.id);
}else {
cb(Constant.LOGIN_ERROR);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}],
writeLastLoginAt: ['query', (results, cb) => {
let adminId = results['query'];
AdminModel.update({
lastLoginAt: new Date()
},{
where: {
id: adminId
}
}).then(result => {
if(result){
cb(null);
}else {
cb(Constant.DEFAULT_ERROR);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//controllers/wish.js
const dateFormat = require('dateformat');
const Common = require('./common');
const WishModel = require('../models/wish');
const Constant = require('../constant/constant');
let exportObj = {
getWishList,
getWishInfo,
addWish,
updateWish,
removeWish
};
module.exports = exportObj;
// 获取许愿列表
function getWishList(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.query, ['page', 'rows'], cb);
},
query: ['checkParams', (results, cb) => {
let offset = req.query.rows * (req.query.page - 1) || 0;
let limit = parseInt(req.query.rows) || 10;
let whereCondition = {};
if(req.query.name){
whereCondition.name = req.query.name;
}
if(req.query.createdAt){
whereCondition.createdAt = req.query.createdAt;
}
WishModel.findAndCountAll({
where: whereCondition,
offset: offset,
limit: limit,
order: [['created_at', 'DESC']]
}).then(result => {
let list = [];
result.rows.forEach(val => {
let obj = {
id: val.id,
name: val.name,
content: val.content,
createdAt: dateFormat(val.createdAt, 'yyyy-mm-dd HH:MM:ss')
};
list.push(obj);
});
resObj.data = list;
resObj.count = result.count;
cb(null);
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//获取单条许愿信息
function getWishInfo(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.params, ['id'], cb);
},
query: ['checkParams', (results, cb) => {
WishModel.findByPk(req.params.id)
.then(result => {
if(result){
resObj.data = {
id: result.id,
name: result.name,
content: result.content,
createdAt: dateFormat(result.createdAt, 'yyyy-mm-dd HH:MM:ss')
};
cb(null);
}else {
cb(Counstant.WISH_NOT_EXSIT);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
// 添加许愿
function addWish(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.body, ['name', 'content'], cb);
},
add: ['checkParams', (results, cb) => {
WishModel.create({
name: req.body.name,
content: req.body.content
}).then(result => {
cb(null);
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//修改许愿
function updateWish(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.body, ['id', 'name', 'content'], cb);
},
update: ['checkParams', (result, cb) => {
WishModel.update({
name: req.body.name,
content: req.body.content
}, {
where: {
id: req.body.id
}
}).then(result => {
if(result[0]){
cb(null)
}else {
cb(Constant.WISH_NOT_EXSIT);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//删除许愿
function removeWish(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.body, ['id'], cb);
},
remove: ['checkParams', (result, cb) => {
WishModel.destroy({
where: {
id: req.body.id
}
}).then(result => {
if(result){
cb(null);
}else {
cb(Constant.WISH_NOT_EXSIT);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//controllers/admin.js
const dateFormat = require('dateformat');
const Common = require('./common');
const AdminModel = require('../models/admin');
const Constant = require('../constant/constant');
let exportObj = {
getAdminList,
getAdminInfo,
addAdmin,
updateAdmin,
removeAdmin
};
module.exports = exportObj;
// 获取管理员列表
function getAdminList(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.query, ['page', 'rows'], cb);
},
query: ['checkParams', (results, cb) => {
let offset = req.query.rows * (req.query.page - 1) || 0;
let limit = parseInt(req.query.rows) || 10;
let whereCondition = {};
if(req.query.username){
whereCondition.username = req.query.username;
}
if(req.query.name){
whereCondition.name = req.query.name;
}
if(req.query.role){
whereCondition.role = req.query.role;
}
if(req.query.createdAt){
whereCondition.createdAt = req.query.createdAt;
}
AdminModel.findAndCountAll({
where: whereCondition,
offset: offset,
limit: limit,
order: [['created_at', 'DESC']]
}).then(result => {
let list = [];
result.rows.forEach(val => {
let obj = {
id: val.id,
username: val.username,
name: val.name,
role: val.role,
createdAt: dateFormat(val.createdAt, 'yyyy-mm-dd HH:MM:ss'),
lastLoginAt: dateFormat(val.lastLoginAt, 'yyyy-mm-dd HH:MM:ss'),
};
list.push(obj);
});
resObj.data = list;
resObj.count = result.count;
cb(null);
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//获取单条管理员信息
function getAdminInfo(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.params, ['id'], cb);
},
query: ['checkParams', (results, cb) => {
AdminModel.findByPk(req.params.id)
.then(result => {
if(result){
resObj.data = {
id: result.id,
username: result.username,
name: result.name,
role: result.role,
createdAt: dateFormat(result.createdAt, 'yyyy-mm-dd HH:MM:ss'),
lastLoginAt: dateFormat(result.lastLoginAt, 'yyyy-mm-dd HH:MM:ss'),
};
cb(null);
}else {
cb(Counstant.ADMIN_NOT_EXSIT);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
// 添加管理员
function addAdmin(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.body, ['username', 'password', 'name', 'role'], cb);
},
query: ['checkParams', (results, cb) => {
AdminModel.findOne({
where: {
username: req.body.username,
}
}).then(result => {
if(result && result.username === req.body.username){
cb(Constant.DATA_EXSIT);
}else {
cb(null);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}],
add: ['query', (results, cb) => {
AdminModel.create({
username: req.body.username,
password: req.body.password,
name: req.body.name,
role: req.body.role
}).then(result => {
cb(null);
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//修改管理员
function updateAdmin(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.body, ['id', 'username', 'password', 'name', 'role'], cb);
},
update: ['checkParams', (result, cb) => {
AdminModel.update({
username: req.body.username,
password: req.body.password,
name: req.body.name,
role: req.body.role
}, {
where: {
id: req.body.id
}
}).then(result => {
if(result[0]){
cb(null)
}else {
cb(Constant.ADMIN_NOT_EXSIT);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
//删除管理员
function removeAdmin(req, res){
const resObj = Common.clone(Constant.DEFAULT_SUCCESS);
let tasks = {
checkParams(cb){
Common.checkParams(req.body, ['id'], cb);
},
remove: ['checkParams', (result, cb) => {
AdminModel.destroy({
where: {
id: req.body.id
}
}).then(result => {
if(result){
cb(null);
}else {
cb(Constant.ADMIN_NOT_EXSIT);
}
}).catch(err => {
console.log(err);
cb(Constant.DEFAULT_ERROR);
})
}]
};
Common.autoFn(tasks, res, resObj);
}
3.3.11 增加token验证方法
routes/middelware/verify.js
const Token = require('../../controllers/token');
const Constant = require('../../constant/constant');
const exportObj = {
verifyToken,
};
module.exports = exportObj;
//验证Token中间件
function verifyToken(req, res, next){
//如果请求路径是/login,即登录页,则跳过,继续下一步
if(req.path === '/login') return next();
let token = req.headers.token;
//解密
let tokenVerifyObj = Token.decrypt(token);
if(tokenVerifyObj.token){
next();
}else {
res.json(Constant.TOKEN_ERROR);
}
}
3.3.12 增加路由
//routes/index.js
const express = require('express');
const router = express.Router();
const IndexController = require('../controllers/index');
router.post('/login', IndexController.login);
module.exports = router;
//routes/wish.js
const express = require('express');
const router = express.Router();
const WishController = require('../controllers/wish');
router.get('/', WishController.getWishList);
router.get('/:id', WishController.getWishInfo);
router.post('/', WishController.addWish);
router.put('/', WishController.updateWish);
router.delete('/', WishController.removeWish);
module.exports = router;
//routes/admin.js
const express = require('express');
const router = express.Router();
const AdminController = require('../controllers/admin');
router.get('/', AdminController.getAdminList);
router.get('/:id', AdminController.getAdminInfo);
router.post('/', AdminController.addAdmin);
router.put('/', AdminController.updateAdmin);
router.delete('/', AdminController.removeAdmin);
module.exports = router;
3.3.13 修改app.js文件
...
const verifyMiddleware = require('./routes/middleware/verify');
const indexRouter = require('./routes/index');
const wishRouter = require('./routes/wish');
const adminRouter = require('./routes/admin');
...
app.use('/', indexRouter);
app.use('/wish', verifyMiddleware.verifyToken, wishRouter);
app.use('/admin', verifyMiddleware.verifyToken, adminRouter);
...