目录
1 注册与登陆
1.1 代码
1.2 Bearer token
2 个人中心
3 文章分类
3.1 代码
3.2 添加/更新文章分类确保名称唯一性
3.2.1 两条数据占用
3.2.2 被一条数据同时占用
3.2.3 只有名称被占用
3.2.4 只有别名被占用
4 文章列表
4.1 代码
4.2 筛选文章
1 注册与登陆
1.1 代码
const config = require('../config.js')
const login_register_router = express.Router()
// 登录与注册都在用下面这个验证机制
const check_username_password = {
body:{
username:userinfo_username_rule,
password:userinfo_password_rule
}
}
// 注册
login_register_router.post('/api/reguser',expressJoi(check_username_password),(req,res) => {
// 检查用户名是否被占用
db.query('select * from userinfo where username=?',req.body.username,function(err,results) {
if (err) {
return res.send({status:1,message:'注册失败,原因是'+err.message})
}
if (results.length > 0) {
return res.send({status:1,message:'用户名被占用,请更换其他用户名'})
}
// 对密码进行加密
req.body.password = bcryptjs.hashSync(req.body.password,10)
// 插入新用户
// 这里只希望插入用户名和密码,所以手动搞了一个对象
db.query('insert into userinfo set ?',{username:req.body.username,password:req.body.password},(err,result) => {
if (err) {
return res.send({
"status":1,
"message":'注册失败,原因是'+err.message
})
}
if (result.affectedRows === 1) {
return res.send({
"status":0,
"message":"注册成功"
})
}
else {
return res.send({
"status":1,
"message":'注册失败'
})
}
})
})
})
// 登录
login_register_router.post('/api/login',expressJoi(check_username_password),(req,res) => {
db.query('select * from userinfo where username=?',req.body.username,(err,result) => {
if (err) {
return res.send({
status:1,
message:'登录失败,原因是' + err.message
})
}
else {
if (result.length !== 1) {
return res.send({
status:1,
message:'用户名或密码错误'
})
}
const true_password = result[0].password
// 由于注册的时候加密过,在登录的时候需要用bcryptjs.compareSync()校验一下,true表示一致,false表示不一致
// 第一个参数是提交的密码,第二个是存储的密码,需要给存储的密码解密,所以顺序不能变
if (bcryptjs.compareSync(req.body.password,true_password)) {
return res.send({
"status":0,
"message":"登录成功",
"token":'Bearer ' + jsonwebtoken.sign({...result[0],password:'',user_pic:''},secretKey,{expiresIn:'24h'})
})
}
else {
return res.send({
status:1,
message:'用户名或密码错误'
})
}
}
})
}
)
module.exports = login_register_router
1.2 Bearer token
Bearer token是token的一种,在项目中登陆成功的时候会使用到
在你用Postman测试的时候,选择Authorization,然后选择Bearer Token,然后将不太Bearer前缀的内容粘贴到里面就可以发送了
他实际的请求头实际上是要带Bearer的
2 个人中心
const config = require('../config.js')
const personal_center_router = express.Router()
// 查看个人除密码外的所有信息
personal_center_router.get('/my/userinfo',(req,res) => {
db.query('select id,username,nickname,email,user_pic from userinfo where id=?',req.auth.id,(err,result) => {
if (err) return res.send({status:1,message:'查询失败,原因是' + err.message})
if (result.length !== 1) {
return res.send({status:1,message:'查询失败'})
}
else {
res.send({
"status":0,
"message":"获取用户基本信息成功",
"data": result[0]
})
}
})
})
// 更新用户昵称,邮箱
const check_id_nickname_email = {
body:{
id:id_rule,
nickname:userinfo_nickname_rule,
email:userinfo_email_rule
}
}
personal_center_router.post('/my/userinfo',expressJoi(check_id_nickname_email),(req,res) => {
db.query('update userinfo set ? where id=?',[req.body,req.body.id],(err,result) => {
if (err) return res.send({"status":1,"message":"修改用户信息失败,原因是" + err.message,})
if (result.affectedRows === 1) {
res.send({
"status":0,
"message":"修改用户信息成功",
})
}
else {
res.send({
"status":1,
"message":"修改用户信息失败",
})
}
})
})
// 更新用户个人密码
const check_new_old_password = {
body:{
oldPwd:userinfo_password_rule,
// joi.ref('oldPwd')表示必须与oldPwd的值一致
// joi.not(joi.ref('oldPwd'))表示必须与oldPwd的值不一致
// joi.not(joi.ref('oldPwd')).concat(password_rule)表示必须与oldPwd的值不一致 且 验证规则使用password_rule,concat表示规则合并
newPwd:joi.not(joi.ref('oldPwd')).concat(userinfo_password_rule)
}
}
// 在下面的视图中实际上给了很多提示,在实际开发中可以不给这么多提示,因为这样可能会被别人试出来,就和你登录的时候一样,一般来讲只会告诉你用户名或密码不对,而不是告诉你哪个不对,这个最后也看实际需要
personal_center_router.post('/my/updatepwd',expressJoi(check_new_old_password),(req,res) => {
// 检验用户是否存在
db.query('select * from userinfo where id=?',req.auth.id,(err,result) => {
if (err) {
return res.send({
"status":1,
"message":"更新密码失败,原因是" + err.message
})
}
else {
if (result.length !== 1) {
res.send({
"status":1,
// 这里一般就是用错了token
"message":"用户不存在!",
})
}
// 检验旧密码是否与数据库中存的密码一致
const true_password = result[0].password
if (bcryptjs.compareSync(req.body.oldPwd,true_password)) {
// 如果一致就更新一下,密码记得加密之后更新进去
db.query('update userinfo set password=? where id=?',[bcryptjs.hashSync(req.body.newPwd,10),req.auth.id],(err,result) => {
if (err) return res.send({
"status":1,
"message":"更新密码失败,原因是" + err.message
})
if (result.affectedRows === 1) {
res.send({
"status":0,
"message":"更新密码成功!",
})
}
else {
res.send({
"status":1,
"message":"更新密码失败!",
})
}
})
}
else {
res.send({
"status":1,
"message":"给的密码与原来的密码不一致!",
})
}
}
})
})
// 更新用户个人头像
const check_avatar = {
body:{
avatar:userinfo_avatar_rule,
}
}
personal_center_router.post('/my/update/avatar',expressJoi(check_avatar),(req,res) => {
db.query('update userinfo set user_pic=? where id=?',[req.body.avatar,req.auth.id],(err,result) => {
if (err) return res.send({
"status":1,
"message":"更新头像失败,原因是" + err.message,
})
if (result.affectedRows === 1) {
res.send({
"status":0,
"message":"更新头像成功!",
})
}
else {
res.send({
"status":1,
"message":"更新头像失败!",
})
}
})
})
module.exports = personal_center_router
在这个模块中加入了一个新的验证规则
更新密码的时候需要将新密码加密后在放入数据库
在更新用户数据中,讲道理id应该不能从req.body中拿,而是应该由token中拿,由于接口文档中写的要传入,这里是按照接口文档走的
3 文章分类
3.1 代码
const article_category_management_router = express.Router()
// 查看所有文章分类
article_category_management_router.get('/my/article/cates',(req,res) => {
db.query('select * from article_category where is_delete=0 order by id asc',(err,result) => {
if (err) {
return res.send({"status":1,"message":'获取文章分类列表成功,原因是' + err.message,})
}
else {
res.send({
"status":0,
"message":"获取文章分类列表成功!",
"data": result
})
}
})
})
// 添加文章分类
const check_category_name_alias = {
body:{
name:category_name_rule,
alias:category_alias_rule
}
}
article_category_management_router.post('/my/article/addcates',expressJoi(check_category_name_alias),(req,res) => {
// 查看名称(name)或别名(alias)是否被占用
db.query('select * from article_category where name=? or alias=?',[req.body.name,req.body.alias],(err,result) => {
if (err) {
return res.send({
status:1,
message:'添加失败,原因是' + err.message
})
}
// 由于表中有唯一限制(UQ),所以如果查出了两个结果就表明 名称 与 别名 都不能用
if (result.length === 2) {
return res.send({
status:1,
message:'该名称与别名都被占用了,请更换后重试'
})
}
// 由于表中有唯一限制,如果只查出来一条数据,那么不是名称被占用了就是别名被占用了(也有可能是名称和别名被同一条数据占用),我们这个做出判断然后给用户提示
if (result.length === 1 && result[0].name === req.body.name) {
return res.send({
status:1,
message:'该名称被占用了,请更换后重试'
})
}
if (result.length === 1 && result[0].alias === req.body.alias) {
return res.send({
status:1,
message:'该别名被占用了,请更换后重试'
})
}
if (result.length === 1 && result[0].name === req.body.name && result[0].alias === req.body.alias) {
return res.send({
status:1,
message:'该名称与别名都被占用了,请更换后重试'
})
}
// 发现没有重复占用问题后进行添加
db.query('insert into article_category set ?',req.body,(err,result) => {
if (err) {
return res.send({
status:1,
message:'添加失败,原因是' + err.message
})
}
if (result.affectedRows === 1) {
return res.send({
"status":0,
"message":"新增文章分类成功!"
})
}
else {
return res.send({
status:1,
message:'添加失败,原因是' + err.message
})
}
})
})
})
// 删除文章分类与获取文章分类数据都要验证id,且方式都是params
const check_category_id = {
params:{
id:id_rule
}
}
// 删除文章分类
article_category_management_router.get('/my/article/deletecate/:id',expressJoi(check_category_id),(req,res) => {
db.query('update article_category set is_delete=1 where id=?',req.params.id,(err,result) => {
if (err) return res.send({"status":1,"message":"删除文章分类失败,原因是" + err.message})
if (result.affectedRows === 1) {
return res.send({
"status":0,
"message":"删除文章分类成功!",
})
}
else {
return res.send({
"status":1,
"message":"删除文章分类失败!",
})
}
})
})
// 获取指定id的文章分类数据
article_category_management_router.get('/my/article/cates/:id',expressJoi(check_category_id),(req,res) => {
db.query('select * from article_category where id=?',req.params.id,(err,result) => {
if (err) return res.send({status:1,message:'获取文章分类数据失败,原因是' + err.message})
if (result.length !== 1) {
return res.send({status:1,message:'获取文章分类数据失败'})
}
else {
return res.send({
"status":0,
"message":"获取文章分类列表成功!",
"data": result[0]
})
}
})
})
// 更新文章分类数据
const check_id_name_alias = {
body:{
id:id_rule,
name:category_name_rule,
alias:category_alias_rule
}
}
article_category_management_router.post('/my/article/updatecate',expressJoi(check_id_name_alias),(req,res) => {
// 与添加分类情况相同,先查一下名称与别名的占用问题
// 与添加分类的区别的不能算自己要更新的那一条数据,因为我们有可能只改名称或只改别名,如果算上了自己要改的,那么变量和名称就必须同时修改
db.query('select * from article_category where id!=? and (name=? or alias=?)',[req.body.id,req.body.name,req.body.alias],(err,result) => {
if (err) {
return res.send({
status:1,
message:'更新文章分类数据失败,原因是' + err.message
})
}
if (result.length === 2) {
return res.send({
status:1,
message:'该名称与别名都被占用了,请更换后重试'
})
}
if (result.length === 1 && result[0].name === req.body.name) {
return res.send({
status:1,
message:'该名称被占用了,请更换后重试'
})
}
if (result.length === 1 && result[0].alias === req.body.alias) {
return res.send({
status:1,
message:'该别名被占用了,请更换后重试'
})
}
if (result.length === 1 && result[0].name === req.body.name && result[0].alias === req.body.alias) {
return res.send({
status:1,
message:'该名称与别名都被占用了,请更换后重试'
})
}
db.query('update article_category set ? where id=?',[req.body,req.body.id],(err,result) => {
if (err) {
return res.send({
"status":1,
"message":"更新文章分类数据失败,原因是" + err.message,
})
}
if (result.affectedRows === 1) {
return res.send({
"status":0,
"message":"更新文章分类数据成功!",
})
}
else {
return res.send({
"status":1,
"message":"更新文章分类数据失败!",
})
}
})
})
})
module.exports = article_category_management_router
3.2 添加/更新文章分类确保名称唯一性
添加文章分类的时候要保证 分类名称与分类别名 的唯一性,有下面四种情况
3.2.1 两条数据占用
3.2.2 被一条数据同时占用
3.2.3 只有名称被占用
3.2.4 只有别名被占用
4 文章列表
4.1 代码
const article_management_router = express.Router()
// 新增文章
const check_article_title_cateid_content_state = {
body: {
title: article_title_rule,
cate_id: article_cate_id_rule.required(),
content: article_content_rule,
state: article_state_rule.required(),
}
}
article_management_router.post('/my/article/add', uploads.single('cover_img'), expressJoi(check_article_title_cateid_content_state), (req, res) => {
if (!req.file || req.file.fieldname !== 'cover_img') {
return res.send({
"status": 1,
"message": "文章封面是必选参数!"
})
}
const articleInfo = {
...req.body,
cover_img: path.join('/uploads', req.file.filename),
pub_date: new Date(),
author_id: req.auth.id,
}
db.query('insert into article_list set ?', articleInfo, (err, result) => {
if (err) {
return res.send({
"status": 1,
"message": '新增文章失败,原因是' + err.message
})
}
if (result.affectedRows === 1) {
return res.send({
"status": 0,
"message": "新增文章成功!"
})
}
else {
return res.send({
"status": 1,
"message": '新增文章失败!'
})
}
})
})
// 查看文章信息(可根据 文章分类id与文章状态 筛选)
const check_article_pagenum_pagesize_cate_id_state = {
query: {
pagenum: article_pagenum_rule,
pagesize: article_pagesize_rule,
cate_id: article_cate_id_rule,
state: article_state_rule,
}
}
article_management_router.get('/my/article/list', expressJoi(check_article_pagenum_pagesize_cate_id_state), (req, res) => {
// 当前是哪页 一页有多少 是必选参数
pagenum = req.query.pagenum
pagesize = req.query.pagesize
// 文章状态与分类id是可选参数,后面会通过有没有这两个参数,做使用哪个sql语句的判断
state = req.query.state
cate_id = req.query.cate_id
sql = 'select * from article_list where is_delete=0 and author_id=' + req.auth.id
if (state) {
sql = sql + ' and state=' + "'" + state + "'"
}
if (cate_id) {
sql = sql + ' and cate_id=' + cate_id
}
// 先获取符合条件的数据有多少个
db.query(sql, (err, result) => {
if (err) {
return res.send({
"status": 1,
"message": "查询失败,原因是" + err.message,
})
}
else {
total = result.length
// 如果没问题就进行第二次查询
sql = sql + ' limit ' + (req.query.pagenum - 1) * req.query.pagesize + ',' + req.query.pagesize
db.query(sql, (err, result) => {
if (err) {
return res.send({
"status": 1,
"message": "查询失败,原因是" + err.message,
})
}
else {
res.send({
"status": 0,
"message": "获取文章列表成功!",
"data": result,
"total":total
})
}
})
}
})
})
// 删除文章
const check_article_id = {
params: {
id: id_rule
}
}
article_management_router.get('/my/article/delete/:id', expressJoi(check_article_id), (req, res) => {
db.query('update article_list set is_delete=1 where id=?', req.params.id, (err, result) => {
if (err) {
return res.send({
"status": 1,
"message": "删除失败,原因是" + err.message,
})
}
if (result.affectedRows === 1) {
res.send({
"status": 0,
"message": "删除成功!",
})
}
else {
res.send({
"status": 1,
"message": "删除失败!",
})
}
})
})
// 获取指定id文章详情
article_management_router.get('/my/article/:id', expressJoi(check_article_id), (req, res) => {
db.query('select * from article_list where id=?', req.params.id, (err, result) => {
if (err) {
return res.send({
"status": 1,
"message": "获取失败,原因是" + err.message,
})
}
if (result.length === 1) {
res.send({
"status": 0,
"message": "获取成功!",
"data": result[0]
})
}
else {
res.send({
"status": 1,
"message": "获取失败!",
})
}
})
})
// 更新文章信息
const check_article_id_title_cateid_content_state = {
body: {
id: id_rule,
title: article_title_rule,
cate_id: article_cate_id_rule.required(),
content: article_content_rule,
state: article_state_rule.required(),
}
}
article_management_router.post('/my/article/edit', uploads.single('cover_img'), expressJoi(check_article_id_title_cateid_content_state), (req, res) => {
if (!req.file || req.file.fieldname !== 'cover_img') {
return res.send({
"status": 1,
"message": "文章封面是必选参数!"
})
}
const articleInfo = {
...req.body,
cover_img: path.join('/uploads', req.file.filename),
pub_date: new Date(),
author_id: req.auth.id
}
db.query('update article_list set ? where id=?', [articleInfo, req.body.id], (err, result) => {
if (err) {
return res.send({
"status": 1,
"message": '更新文章失败,原因是' + err.message
})
}
if (result.affectedRows === 1) {
return res.send({
"status": 0,
"message": "更新文章成功!"
})
}
else {
return res.send({
"status": 1,
"message": '更新文章失败!'
})
}
})
})
module.exports = article_management_router
4.2 筛选文章
在筛选文章的时候有 状态(state)与分类id(cate_id) 这两个非必填参数,可以用拼接sql字符串的形式进行查询,先判断有没有,如果有就加上
由于要找到总数,所以要先查询一次全部的,之后对查询结果进行限制实现分页的功能