blog-node 是采用了主流的先后端分离思想的,主里只讲 后端。html
blog-node 项目是 node + express + mongodb 的进行开发的,项目已经开源,项目地址在 github 上。前端
效果请看 http://biaochenxuying.cn/main.htmlvue
// modules const createError = require('http-errors'); const express = require('express'); const path = require('path'); const cookieParser = require('cookie-parser'); const logger = require('morgan'); const session = require('express-session'); // import 等语法要用到 babel 支持 require('babel-register'); const app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(express.static(path.join(__dirname, 'public'))); app.use(cookieParser('blog_node_cookie')); app.use( session({ secret: 'blog_node_cookie', name: 'session_id', //# 在浏览器中生成cookie的名称key,默认是connect.sid resave: true, saveUninitialized: true, cookie: { maxAge: 60 * 1000 * 30, httpOnly: true }, //过时时间 }), ); const mongodb = require('./core/mongodb'); // data server mongodb.connect(); //将路由文件引入 const route = require('./routes/index'); //初始化全部路由 route(app); // catch 404 and forward to error handler app.use(function(req, res, next) { next(createError(404)); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
/** * Mongoose module. * @file 数据库模块 * @module core/mongoose * @author biaochenxuying <https://github.com/biaochenxuying> */ const consola = require('consola') const CONFIG = require('../app.config.js') const mongoose = require('mongoose') const autoIncrement = require('mongoose-auto-increment') // remove DeprecationWarning mongoose.set('useFindAndModify', false) // mongoose Promise mongoose.Promise = global.Promise // mongoose exports.mongoose = mongoose // connect exports.connect = () => { // 链接数据库 mongoose.connect(CONFIG.MONGODB.uri, { useCreateIndex: true, useNewUrlParser: true, promiseLibrary: global.Promise }) // 链接错误 mongoose.connection.on('error', error => { consola.warn('数据库链接失败!', error) }) // 链接成功 mongoose.connection.once('open', () => { consola.ready('数据库链接成功!') }) // 自增 ID 初始化 autoIncrement.initialize(mongoose.connection) // 返回实例 return mongoose }
这里只介绍 用户、文章和评论 的模型。java
用户的字段都有设置类型 type,大多都设置了默认值 default ,邮箱设置了验证规则 validate,密码保存用了 crypto 来加密。node
用了中间件自增 ID 插件 mongoose-auto-increment。react
/** * User model module. * @file 权限和用户数据模型 * @module model/user * @author biaochenxuying <https://github.com/biaochenxuying> */ const crypto = require('crypto'); const { argv } = require('yargs'); const { mongoose } = require('../core/mongodb.js'); const autoIncrement = require('mongoose-auto-increment'); const adminSchema = new mongoose.Schema({ // 名字 name: { type: String, required: true, default: '' }, // 用户类型 0:博主 1:其余用户 type: { type: Number, default: 1 }, // 手机 phone: { type: String, default: '' }, //封面 img_url: { type: String, default: '' }, // 邮箱 email: { type: String, required: true, validate: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/ }, // 我的介绍 introduce: { type: String, default: '' }, // 头像 avatar: { type: String, default: 'user' }, // 密码 password: { type: String, required: true, default: crypto .createHash('md5') .update(argv.auth_default_password || 'root') .digest('hex'), }, // 建立日期 create_time: { type: Date, default: Date.now }, // 最后修改日期 update_time: { type: Date, default: Date.now }, }); // 自增 ID 插件配置 adminSchema.plugin(autoIncrement.plugin, { model: 'User', field: 'id', startAt: 1, incrementBy: 1, }); module.exports = mongoose.model('User', adminSchema);
文章是分类型的:文章类型 => 1: 普通文章,2: 简历,3: 管理员介绍
并且简历和管理员介绍的文章只能是各自一篇(由于前台展现那里有个导航 关于我 ,就是请求管理员介绍这篇文章的,简历也是打算这样子用的),普通文章能够是无数篇。git
点赞的用户 like_users 那里应该只保存用户 id 的,这个后面修改一下。程序员
/** * Article model module. * @file 文章数据模型 * @module model/article * @author biaochenxuying <https://github.com/biaochenxuying> */ const { mongoose } = require('../core/mongodb.js'); const autoIncrement = require('mongoose-auto-increment'); // 文章模型 const articleSchema = new mongoose.Schema({ // 文章标题 title: { type: String, required: true, validate: /\S+/ }, // 文章关键字(SEO) keyword: [{ type: String, default: '' }], // 做者 author: { type: String, required: true, validate: /\S+/ }, // 文章描述 desc: { type: String, default: '' }, // 文章内容 content: { type: String, required: true, validate: /\S+/ }, // 字数 numbers: { type: String, default: 0 }, // 封面图 img_url: { type: String, default: 'https://upload-images.jianshu.io/upload_images/12890819-80fa7517ab3f2783.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240' }, // 文章类型 => 1: 普通文章,2: 简历,3: 管理员介绍 type: { type: Number, default: 1 }, // 文章发布状态 => 0 草稿,1 已发布 state: { type: Number, default: 1 }, // 文章转载状态 => 0 原创,1 转载,2 混合 origin: { type: Number, default: 0 }, // 文章标签 tags: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Tag', required: true }], comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment', required: true }], // 文章分类 category: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Category', required: true }], // 点赞的用户 like_users: [ { // 用户id id: { type: mongoose.Schema.Types.ObjectId }, // 名字 name: { type: String, required: true, default: '' }, // 用户类型 0:博主 1:其余用户 type: { type: Number, default: 1 }, // 我的介绍 introduce: { type: String, default: '' }, // 头像 avatar: { type: String, default: 'user' }, // 建立日期 create_time: { type: Date, default: Date.now }, }, ], // 其余元信息 meta: { views: { type: Number, default: 0 }, likes: { type: Number, default: 0 }, comments: { type: Number, default: 0 }, }, // 建立日期 create_time: { type: Date, default: Date.now }, // 最后修改日期 update_time: { type: Date, default: Date.now }, }); // 自增 ID 插件配置 articleSchema.plugin(autoIncrement.plugin, { model: 'Article', field: 'id', startAt: 1, incrementBy: 1, }); // 文章模型 module.exports = mongoose.model('Article', articleSchema);
评论功能是实现了简单的三级评论的,第三者的评论(就是别人对一级评论进行再评论)放在 other_comments 里面。github
/** * Comment model module. * @file 评论数据模型 * @module model/comment * @author biaochenxuying <https://github.com/biaochenxuying> */ const { mongoose } = require('../core/mongodb.js'); const autoIncrement = require('mongoose-auto-increment'); // 评论模型 const commentSchema = new mongoose.Schema({ // 评论所在的文章 id article_id: { type: mongoose.Schema.Types.ObjectId, required: true }, // content content: { type: String, required: true, validate: /\S+/ }, // 是否置顶 is_top: { type: Boolean, default: false }, // 被赞数 likes: { type: Number, default: 0 }, user_id: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, // 父评论的用户信息 user: { // 用户id user_id: { type: mongoose.Schema.Types.ObjectId }, // 名字 name: { type: String, required: true, default: '' }, // 用户类型 0:博主 1:其余用户 type: { type: Number, default: 1 }, // 头像 avatar: { type: String, default: 'user' }, }, // 第三者评论 other_comments: [ { user: { id: { type: mongoose.Schema.Types.ObjectId }, // 名字 name: { type: String, required: true, default: '' }, // 用户类型 0:博主 1:其余用户 type: { type: Number, default: 1 }, }, // content content: { type: String, required: true, validate: /\S+/ }, // 状态 => 0 待审核 / 1 经过正常 / -1 已删除 / -2 垃圾评论 state: { type: Number, default: 1 }, // 建立日期 create_time: { type: Date, default: Date.now }, }, ], // 状态 => 0 待审核 / 1 经过正常 / -1 已删除 / -2 垃圾评论 state: { type: Number, default: 1 }, // 建立日期 create_time: { type: Date, default: Date.now }, // 最后修改日期 update_time: { type: Date, default: Date.now }, }); // 自增 ID 插件配置 commentSchema.plugin(autoIncrement.plugin, { model: 'Comment', field: 'id', startAt: 1, incrementBy: 1, }); // 标签模型 module.exports = mongoose.model('Comment', commentSchema);
其余模块的具体需求,都是些经常使用的逻辑能够实现的,也很简单,这里就不展开讲了。mongodb
/* *全部的路由接口 */ const user = require('./user'); const article = require('./article'); const comment = require('./comment'); const message = require('./message'); const tag = require('./tag'); const link = require('./link'); const category = require('./category'); const timeAxis = require('./timeAxis'); module.exports = app => { app.post('/login', user.login); app.post('/logout', user.logout); app.post('/loginAdmin', user.loginAdmin); app.post('/register', user.register); app.post('/delUser', user.delUser); app.get('/currentUser', user.currentUser); app.get('/getUserList', user.getUserList); app.post('/addComment', comment.addComment); app.post('/addThirdComment', comment.addThirdComment); app.post('/changeComment', comment.changeComment); app.post('/changeThirdComment', comment.changeThirdComment); app.get('/getCommentList', comment.getCommentList); app.post('/addArticle', article.addArticle); app.post('/updateArticle', article.updateArticle); app.post('/delArticle', article.delArticle); app.get('/getArticleList', article.getArticleList); app.get('/getArticleListAdmin', article.getArticleListAdmin); app.post('/getArticleDetail', article.getArticleDetail); app.post('/likeArticle', article.likeArticle); app.post('/addTag', tag.addTag); app.post('/delTag', tag.delTag); app.get('/getTagList', tag.getTagList); app.post('/addMessage', message.addMessage); app.post('/addReplyMessage', message.addReplyMessage); app.post('/delMessage', message.delMessage); app.post('/getMessageDetail', message.getMessageDetail); app.get('/getMessageList', message.getMessageList); app.post('/addLink', link.addLink); app.post('/updateLink', link.updateLink); app.post('/delLink', link.delLink); app.get('/getLinkList', link.getLinkList); app.post('/addCategory', category.addCategory); app.post('/delCategory', category.delCategory); app.get('/getCategoryList', category.getCategoryList); app.post('/addTimeAxis', timeAxis.addTimeAxis); app.post('/updateTimeAxis', timeAxis.updateTimeAxis); app.post('/delTimeAxis', timeAxis.delTimeAxis); app.get('/getTimeAxisList', timeAxis.getTimeAxisList); app.post('/getTimeAxisDetail', timeAxis.getTimeAxisDetail); };
各模块的列表都是用了分页的形式的。
import Article from '../models/article'; import User from '../models/user'; import { responseClient, timestampToTime } from '../util/util'; exports.addArticle = (req, res) => { // if (!req.session.userInfo) { // responseClient(res, 200, 1, '您还没登陆,或者登陆信息已过时,请从新登陆!'); // return; // } const { title, author, keyword, content, desc, img_url, tags, category, state, type, origin } = req.body; let tempArticle = null if(img_url){ tempArticle = new Article({ title, author, keyword: keyword ? keyword.split(',') : [], content, numbers: content.length, desc, img_url, tags: tags ? tags.split(',') : [], category: category ? category.split(',') : [], state, type, origin, }); }else{ tempArticle = new Article({ title, author, keyword: keyword ? keyword.split(',') : [], content, numbers: content.length, desc, tags: tags ? tags.split(',') : [], category: category ? category.split(',') : [], state, type, origin, }); } tempArticle .save() .then(data => { responseClient(res, 200, 0, '保存成功', data); }) .catch(err => { console.log(err); responseClient(res); }); }; exports.updateArticle = (req, res) => { // if (!req.session.userInfo) { // responseClient(res, 200, 1, '您还没登陆,或者登陆信息已过时,请从新登陆!'); // return; // } const { title, author, keyword, content, desc, img_url, tags, category, state, type, origin, id } = req.body; Article.update( { _id: id }, { title, author, keyword: keyword ? keyword.split(','): [], content, desc, img_url, tags: tags ? tags.split(',') : [], category:category ? category.split(',') : [], state, type, origin, }, ) .then(result => { responseClient(res, 200, 0, '操做成功', result); }) .catch(err => { console.error(err); responseClient(res); }); }; exports.delArticle = (req, res) => { let { id } = req.body; Article.deleteMany({ _id: id }) .then(result => { if (result.n === 1) { responseClient(res, 200, 0, '删除成功!'); } else { responseClient(res, 200, 1, '文章不存在'); } }) .catch(err => { console.error('err :', err); responseClient(res); }); }; // 前台文章列表 exports.getArticleList = (req, res) => { let keyword = req.query.keyword || null; let state = req.query.state || ''; let likes = req.query.likes || ''; let tag_id = req.query.tag_id || ''; let category_id = req.query.category_id || ''; let pageNum = parseInt(req.query.pageNum) || 1; let pageSize = parseInt(req.query.pageSize) || 10; let conditions = {}; if (!state) { if (keyword) { const reg = new RegExp(keyword, 'i'); //不区分大小写 conditions = { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }], }; } } else if (state) { state = parseInt(state); if (keyword) { const reg = new RegExp(keyword, 'i'); conditions = { $and: [ { $or: [{ state: state }] }, { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }, { keyword: { $regex: reg } }] }, ], }; } else { conditions = { state }; } } let skip = pageNum - 1 < 0 ? 0 : (pageNum - 1) * pageSize; let responseData = { count: 0, list: [], }; Article.countDocuments(conditions, (err, count) => { if (err) { console.log('Error:' + err); } else { responseData.count = count; // 待返回的字段 let fields = { title: 1, author: 1, keyword: 1, content: 1, desc: 1, img_url: 1, tags: 1, category: 1, state: 1, type: 1, origin: 1, comments: 1, like_User_id: 1, meta: 1, create_time: 1, update_time: 1, }; let options = { skip: skip, limit: pageSize, sort: { create_time: -1 }, }; Article.find(conditions, fields, options, (error, result) => { if (err) { console.error('Error:' + error); // throw error; } else { let newList = []; if (likes) { // 根据热度 likes 返回数据 result.sort((a, b) => { return b.meta.likes - a.meta.likes; }); responseData.list = result; } else if (category_id) { // 根据 分类 id 返回数据 result.forEach(item => { if (item.category.indexOf(category_id) > -1) { newList.push(item); } }); let len = newList.length; responseData.count = len; responseData.list = newList; } else if (tag_id) { // 根据标签 id 返回数据 result.forEach(item => { if (item.tags.indexOf(tag_id) > -1) { newList.push(item); } }); let len = newList.length; responseData.count = len; responseData.list = newList; } else { responseData.list = result; } responseClient(res, 200, 0, '操做成功!', responseData); } }); } }); }; // 后台文章列表 exports.getArticleListAdmin = (req, res) => { let keyword = req.query.keyword || null; let state = req.query.state || ''; let likes = req.query.likes || ''; let pageNum = parseInt(req.query.pageNum) || 1; let pageSize = parseInt(req.query.pageSize) || 10; let conditions = {}; if (!state) { if (keyword) { const reg = new RegExp(keyword, 'i'); //不区分大小写 conditions = { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }], }; } } else if (state) { state = parseInt(state); if (keyword) { const reg = new RegExp(keyword, 'i'); conditions = { $and: [ { $or: [{ state: state }] }, { $or: [{ title: { $regex: reg } }, { desc: { $regex: reg } }, { keyword: { $regex: reg } }] }, ], }; } else { conditions = { state }; } } let skip = pageNum - 1 < 0 ? 0 : (pageNum - 1) * pageSize; let responseData = { count: 0, list: [], }; Article.countDocuments(conditions, (err, count) => { if (err) { console.log('Error:' + err); } else { responseData.count = count; // 待返回的字段 let fields = { title: 1, author: 1, keyword: 1, content: 1, desc: 1, img_url: 1, tags: 1, category: 1, state: 1, type: 1, origin: 1, comments: 1, like_User_id: 1, meta: 1, create_time: 1, update_time: 1, }; let options = { skip: skip, limit: pageSize, sort: { create_time: -1 }, }; Article.find(conditions, fields, options, (error, result) => { if (err) { console.error('Error:' + error); // throw error; } else { if (likes) { result.sort((a, b) => { return b.meta.likes - a.meta.likes; }); } responseData.list = result; responseClient(res, 200, 0, '操做成功!', responseData); } }) .populate([ { path: 'tags', }, { path: 'comments', }, { path: 'category', }, ]) .exec((err, doc) => {}); } }); }; // 文章点赞 exports.likeArticle = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, '您还没登陆,或者登陆信息已过时,请从新登陆!'); return; } let { id, user_id } = req.body; Article.findOne({ _id: id }) .then(data => { let fields = {}; data.meta.likes = data.meta.likes + 1; fields.meta = data.meta; let like_users_arr = data.like_users.length ? data.like_users : []; User.findOne({ _id: user_id }) .then(user => { let new_like_user = {}; new_like_user.id = user._id; new_like_user.name = user.name; new_like_user.avatar = user.avatar; new_like_user.create_time = user.create_time; new_like_user.type = user.type; new_like_user.introduce = user.introduce; like_users_arr.push(new_like_user); fields.like_users = like_users_arr; Article.update({ _id: id }, fields) .then(result => { responseClient(res, 200, 0, '操做成功!', result); }) .catch(err => { console.error('err :', err); throw err; }); }) .catch(err => { responseClient(res); console.error('err 1:', err); }); }) .catch(err => { responseClient(res); console.error('err 2:', err); }); }; // 文章详情 exports.getArticleDetailByType = (req, res) => { let { type } = req.body; if (!type) { responseClient(res, 200, 1, '文章不存在 !'); return; } Article.findOne({ type: type }, (Error, data) => { if (Error) { console.error('Error:' + Error); // throw error; } else { data.meta.views = data.meta.views + 1; Article.updateOne({ type: type }, { meta: data.meta }) .then(result => { responseClient(res, 200, 0, '操做成功 !', data); }) .catch(err => { console.error('err :', err); throw err; }); } }) .populate([ { path: 'tags', select: '-_id' }, { path: 'category', select: '-_id' }, { path: 'comments', select: '-_id' }, ]) .exec((err, doc) => { // console.log("doc:"); // aikin // console.log("doc.tags:",doc.tags); // aikin // console.log("doc.category:",doc.category); // undefined }); }; // 文章详情 exports.getArticleDetail = (req, res) => { let { id } = req.body; let type = Number(req.body.type) || 1; //文章类型 => 1: 普通文章,2: 简历,3: 管理员介绍 console.log('type:', type); if (type === 1) { if (!id) { responseClient(res, 200, 1, '文章不存在 !'); return; } Article.findOne({ _id: id }, (Error, data) => { if (Error) { console.error('Error:' + Error); // throw error; } else { data.meta.views = data.meta.views + 1; Article.updateOne({ _id: id }, { meta: data.meta }) .then(result => { responseClient(res, 200, 0, '操做成功 !', data); }) .catch(err => { console.error('err :', err); throw err; }); } }) .populate([ { path: 'tags', }, { path: 'category', }, { path: 'comments', }, ]) .exec((err, doc) => { // console.log("doc:"); // aikin // console.log("doc.tags:",doc.tags); // aikin // console.log("doc.category:",doc.category); // undefined }); } else { Article.findOne({ type: type }, (Error, data) => { if (Error) { console.log('Error:' + Error); // throw error; } else { if (data) { data.meta.views = data.meta.views + 1; Article.updateOne({ type: type }, { meta: data.meta }) .then(result => { responseClient(res, 200, 0, '操做成功 !', data); }) .catch(err => { console.error('err :', err); throw err; }); } else { responseClient(res, 200, 1, '文章不存在 !'); return; } } }) .populate([ { path: 'tags', }, { path: 'category', }, { path: 'comments', }, ]) .exec((err, doc) => {}); } };
评论是有状态的:状态 => 0 待审核 / 1 经过正常 / -1 已删除 / -2 垃圾评论。
管理一级和三级评论是设置前台能不能展现的,默认是展现,若是管理员看了,是条垃圾评论就 设置为 -1 或者 -2 ,进行隐藏,前台就不会展示了。
import { responseClient } from '../util/util'; import Comment from '../models/comment'; import User from '../models/user'; import Article from '../models/article'; //获取所有评论 exports.getCommentList = (req, res) => { let keyword = req.query.keyword || null; let comment_id = req.query.comment_id || null; let pageNum = parseInt(req.query.pageNum) || 1; let pageSize = parseInt(req.query.pageSize) || 10; let conditions = {}; if (comment_id) { if (keyword) { const reg = new RegExp(keyword, 'i'); //不区分大小写 conditions = { _id: comment_id, content: { $regex: reg }, }; } else { conditions = { _id: comment_id, }; } } else { if (keyword) { const reg = new RegExp(keyword, 'i'); //不区分大小写 conditions = { content: { $regex: reg }, }; } } let skip = pageNum - 1 < 0 ? 0 : (pageNum - 1) * pageSize; let responseData = { count: 0, list: [], }; Comment.countDocuments(conditions, (err, count) => { if (err) { console.error('Error:' + err); } else { responseData.count = count; // 待返回的字段 let fields = { article_id: 1, content: 1, is_top: 1, likes: 1, user_id: 1, user: 1, other_comments: 1, state: 1, create_time: 1, update_time: 1, }; let options = { skip: skip, limit: pageSize, sort: { create_time: -1 }, }; Comment.find(conditions, fields, options, (error, result) => { if (err) { console.error('Error:' + error); // throw error; } else { responseData.list = result; responseClient(res, 200, 0, '操做成功!', responseData); } }); } }); }; // 添加一级评论 exports.addComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, '您还没登陆,或者登陆信息已过时,请从新登陆!'); return; } let { article_id, user_id, content } = req.body; User.findById({ _id: user_id, }) .then(result => { // console.log('result :', result); if (result) { let userInfo = { user_id: result._id, name: result.name, type: result.type, avatar: result.avatar, }; let comment = new Comment({ article_id: article_id, content: content, user_id: user_id, user: userInfo, }); comment .save() .then(commentResult => { Article.findOne({ _id: article_id }, (errors, data) => { if (errors) { console.error('Error:' + errors); // throw errors; } else { data.comments.push(commentResult._id); data.meta.comments = data.meta.comments + 1; Article.updateOne({ _id: article_id }, { comments: data.comments, meta: data.meta }) .then(result => { responseClient(res, 200, 0, '操做成功 !', commentResult); }) .catch(err => { console.error('err :', err); throw err; }); } }); }) .catch(err2 => { console.error('err :', err2); throw err2; }); } else { responseClient(res, 200, 1, '用户不存在'); } }) .catch(error => { console.error('error :', error); responseClient(res); }); }; // 添加第三者评论 exports.addThirdComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, '您还没登陆,或者登陆信息已过时,请从新登陆!'); return; } let { article_id, comment_id, user_id, content } = req.body; Comment.findById({ _id: comment_id, }) .then(commentResult => { User.findById({ _id: user_id, }) .then(userResult => { if (userResult) { let userInfo = { user_id: userResult._id, name: userResult.name, type: userResult.type, avatar: userResult.avatar, }; let item = { user: userInfo, content: content, }; commentResult.other_comments.push(item); Comment.updateOne( { _id: comment_id }, { other_comments: commentResult, }, ) .then(result => { responseClient(res, 200, 0, '操做成功', result); Article.findOne({ _id: article_id }, (errors, data) => { if (errors) { console.error('Error:' + errors); // throw errors; } else { data.meta.comments = data.meta.comments + 1; Article.updateOne({ _id: article_id }, { meta: data.meta }) .then(result => { // console.log('result :', result); responseClient(res, 200, 0, '操做成功 !', result); }) .catch(err => { console.log('err :', err); throw err; }); } }); }) .catch(err1 => { console.error('err1:', err1); responseClient(res); }); } else { responseClient(res, 200, 1, '用户不存在'); } }) .catch(error => { console.error('error :', error); responseClient(res); }); }) .catch(error2 => { console.error('error2 :', error2); responseClient(res); }); }; // 管理一级评论 exports.changeComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, '您还没登陆,或者登陆信息已过时,请从新登陆!'); return; } let { id, state } = req.body; Comment.updateOne( { _id: id }, { state: Number(state), }, ) .then(result => { responseClient(res, 200, 0, '操做成功', result); }) .catch(err => { console.error('err:', err); responseClient(res); }); }; // 管理第三者评论 exports.changeThirdComment = (req, res) => { if (!req.session.userInfo) { responseClient(res, 200, 1, '您还没登陆,或者登陆信息已过时,请从新登陆!'); return; } let { comment_id, state, index } = req.body; Comment.findById({ _id: comment_id, }) .then(commentResult => { let i = index ? Number(index) : 0; if (commentResult.other_comments.length) { commentResult.other_comments[i].state = Number(state); Comment.updateOne( { _id: comment_id }, { other_comments: commentResult, }, ) .then(result => { responseClient(res, 200, 0, '操做成功', result); }) .catch(err1 => { console.error('err1:', err1); responseClient(res); }); } else { responseClient(res, 200, 1, '第三方评论不存在!', result); } }) .catch(error2 => { console.log('error2 :', error2); responseClient(res); }); };
其余模块的具体需求,都是些经常使用的逻辑能够实现的,也很简单,这里就不展开讲了。
# install dependencies npm install # serve with hot reload at localhost: 3000 npm start # build for production with minification 请使用 pm2 ,能够永久运行在服务器上,且不会一报错 node 程序就挂了。
若是以为该项目不错或者对你有所帮助,欢迎到 github 上给个 star,谢谢。
项目地址:
前台展现: https://github.com/biaochenxuying/blog-react管理后台:https://github.com/biaochenxuying/blog-react-admin
本博客系统的系列文章:
小汪也是第一次搭建 node 后端项目,也参考了其余项目。
参考项目:
1. nodepress
2. React-Express-Blog-Demo
对 全栈开发 有兴趣的朋友,能够扫下方二维码,关注个人公众号,我会不按期更新有价值的内容。
微信公众号: BiaoChenXuYing
分享 前端、后端开发 等相关的技术文章,热点资源,全栈程序员的成长之路。
关注公众号并回复 福利 便免费送你视频资源,绝对干货。
福利详情请点击: 免费资源分享--Python、Java、Linux、Go、node、vue、react、javaScript