Appearance
nodejs coderhub
前言:项目介绍
Coderhub旨在创建一个程序员分享生活动态的平台。
完整的项目接口包括:
- 面向用户的业务接口;
- 面向企业或者内部的后台管理接口;
完成的功能如下:
- 1.用户管理系统
- 2.内容管理系统
- 3.内容评论管理
- 4.内容标签管理
- 5.文件管理系统
- ...其他功能其实都是非常相似的
第一部分
第一节:项目结构搭建
- 创建项目文件夹 coderhub
- npm init -y 生成package.json文件
- npm install koa 安装koa框架
目录结构的划分:
- 按照功能模块划分;
- 按照业务模块划分;
我们按照功能模块来划分目录结构:参考egg.js框架的目录结构
- 根目录创建src文件夹
- src文件夹下创建index.js或者main.js(我们采用main.js)
- src文件夹下创建app文件夹,app文件夹下新建index.js文件
- src文件夹下创建controller、router、service、utils文件夹,分别存放控制器、路由、服务器、工具等文件
- main.js引入路由,创建app路由对象,启动监听,代码如下
javascript
const Koa = require('koa')
const app = new Koa()
app.listen(8000,()=>{
console.log('服务器启动成功~');
})
- 安装nodemon在开发时依赖:npm i nodemon -D,并且在package.json中配置启动脚本
第二节:配置信息写入到环境变量
- 把main.js中的代码抽取到app/index.js
javascript
/*------------------------------app/index.js---------------------------------*/
const Koa = require('koa')
const app = new Koa()
module.exports = app
/*------------------------------main.js---------------------------------*/
const app = require('./app')
app.listen(8000,()=>{
console.log('服务器启动成功~');
})
- 根目录创建.env文件用于存放全局环境变量,并且新建APP_PROT环境变量存储服务器端口号
javascript
APP_PORT=8000
- 安装解析环境变量的库:npm i dotenv
- app目录下创建config.js,编写以下代码
javascript
const dotenv = require('dotenv')
dotenv.config()
module.exports = {
APP_PORT
} = process.env
- main.js引入config,应用环境变量
javascript
const app = require('./app')
const config = require('./app/config')
app.listen(config.APP_PORT,()=>{
console.log(`服务器在${config.APP_PORT}端口启动成功~`);
})
第三节:用户注册接口搭建
- 安装koa路由模块:npm i koa-router
- 在app/index.js中导入路由,创建一个用户路由
javascript
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const userRouter = new Router({prefix: '/users'});
userRouter.post('/',(ctx,next)=>{
ctx.body = "创建用户成功"
})
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
module.exports = app
第四节:用户注册接口的结构分层
- 在router目录下创建user.router.js,把app/index.js中的路由代码抽取过来
javascript
/*------------------------------router/user.router.js---------------------------------*/
const Router = require('koa-router')
const userRouter = new Router({prefix: '/users'});
userRouter.post('/',(ctx,next)=>{
ctx.body = "创建用户成功"
})
module.exports = userRouter
/*------------------------------app/index.js---------------------------------*/
const Koa = require('koa')
const userRouter = require('../router/user.router')
const app = new Koa()
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
module.exports = app
- 把路由的业务处理代码(函数)抽取到controller中
- 在controller文件夹下新建user.controller.js
- 以类的形式定义,返回类的实例(对象),把处理业务定义为类的原型属性,导入时可以直接获取对象的属性
javascript
/*-------------------------controcller/user.controller.js---------------------------*/
class UserController {
async create(ctx, next) {
// 获取用户请求传递的参数
// 查询数据
// 返回数据
}
}
module.exports = new UserController()
/*------------------------------router/user.router.js---------------------------------*/
const Router = require('koa-router')
const { create } = require('../controller/user.controller')
const userRouter = new Router({ prefix: '/users' });
userRouter.post('/', create)
module.exports = userRouter
- 在service目录下创建user.service.js,与数据库交互的业务全部放到service目录下
javascript
/*------------------------------service/user.service.js---------------------------------*/
class UserService {
async create(user){
// 将user存储到数据库中
return "创建用户成功";
}
}
module.exports = new UserService();
/*-------------------------controcller/user.controller.js---------------------------*/
const service = require('../service/user.service')
class UserController {
async create(ctx, next) {
// 获取用户请求传递的参数
// 查询数据
const result = await service.create()
// 返回数据
ctx.body = result
}
}
module.exports = new UserController()
- 安装解析json的模块:npm i koa-bodyparser,在app/index.js使用它
javascript
/*------------------------------app/index.js---------------------------------*/
const Koa = require('koa')
const bodyparser = require('koa-bodyparser')
const userRouter = require('../router/user.router')
const app = new Koa()
app.use(bodyparser())
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
module.exports = app
- 在controller/user.controller.js中获取请求参数(json)
javascript
/*-----------------------controller/user.controller.js-----------------------*/
const service = require('../service/user.service')
class UserController {
async create(ctx, next) {
// 获取用户请求传递的参数
const user = ctx.request.body
// 查询数据
const result = await service.create(user)
// 返回数据
ctx.body = result
}
}
module.exports = new UserController()
/*-----------------------service/user.service.js-----------------------*/
class UserService {
async create(user){
console.log("将用户数据保存到数据库中:",user);
// 将user存储到数据库中
return "创建用户成功";
}
}
module.exports = new UserService();
第五节:用户注册接口的数据库保存
- 在mysql中创建usesr表(用户表)
sql
CREATE TABLE IF NOT EXISTS `users` (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30) NOT NULL UNIQUE,
password VARCHAR(50) NOT NULL,
createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
- 安装mysql2操作数据库:npm i mysql2
- 在app目录下创建database.js文件,在.env文件中新建环境变量存储数据库信息,并且在app/config.js文件中导出
javascript
/*-----------------------.env-----------------------*/
APP_PORT=8000
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_DATABASE=coderhub
MYSQL_USER=root
MYSQL_PASSWORD=54088qq
/*-----------------------app/config.js-----------------------*/
const dotenv = require('dotenv')
dotenv.config()
module.exports = {
APP_PORT,
MYSQL_HOST,
MYSQL_PORT,
MYSQL_DATABASE,
MYSQL_USER,
MYSQL_PASSWORD
} = process.env
/*-----------------------app/database.js-----------------------*/
const mysql = require('mysql2')
const config = require('./config')
const connections = mysql.createPool({
host: config.MYSQL_HOST,
port: config.MYSQL_PORT,
database: config.MYSQL_DATABASE,
user: config.MYSQL_USER,
password: config.MYSQL_PASSWORD
})
connections.getConnection((err, conn) => {
conn.connect(err => {
if(err){
console.log("连接失败~", err);
}else{
console.log("数据库连接成功~");
}
})
})
module.exports = connections.promise()
- main.js测试数据库是否连接成功
javascript
const app = require('./app')
const config = require('./app/config')
require('./app/database')
app.listen(config.APP_PORT,()=>{
console.log(`服务器在${config.APP_PORT}端口启动成功~`);
})
- 在service/user.service.js中编写sql语句,执行sql语句把前端传递的用户数据插入到数据库
javascript
const connections = require('../app/database')
class UserService {
async create(user){
const {name, password} = user;
const statement = `INSERT INTO users (name, password) VALUES (?, ?);`;
const result = await connections.execute(statement,[name, password]);
// 将user存储到数据库中
return result;
}
}
module.exports = new UserService();
第六节:用户注册接口-验证用户
- 创建验证用户信息中间件
- 根目录创建一个middleware文件夹,在里面新建一个user.middleware.js文件
- 获取用户名和密码
- 判断用户名或者密码不能空,如果为空抛出错误
- 判断这次注册的用户名是没有被注册过的,如果用户存在抛出错误
- 在app目录下创建一个error.handle专门处理错误的文件
- src目录下创建一个constants文件夹,里面新建一个error-types.js保存一些错误信息的常量
- app/index.js监听错误
- router/user.router.js使用验证用户信息中间件
- service/user.service.js处理sql请求
- 根目录创建一个middleware文件夹,在里面新建一个user.middleware.js文件
javascript
/*--------------------------middleware/user.middleware.js--------------------------*/
const errorTypes = require('../constants/error-types')
const service = require('../service/user.service')
const verifyUser = async (ctx, next) => {
// 1.获取用户名和密码
const { name, password } = ctx.request.body;
// 2.判断用户名或者密码不能空
if (!name || !password) {
const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED)
return ctx.app.emit('error', error, ctx)
}
// 3.判断这次注册的用户名是没有被注册过的
const result = await service.getUserByName(name)
if (result.length) {
const error = new Error(errorTypes.USER_ALREADY_EXISTS)
return ctx.app.emit('error', error, ctx)
}
await next()
}
module.exports = {
verifyUser
}
javascript
/*------------------------------constants/error-types.js--------------------------*/
const NAME_OR_PASSWORD_IS_REQUIRED = "name_or_password_is_require";
const USER_ALREADY_EXISTS = "user_already_exists";
module.exports = {
NAME_OR_PASSWORD_IS_REQUIRED,
SER_ALREADY_EXISTS
}
javascript
/*-----------------------app/error.handle.js--------------------------*/
const errorTypes = require('../constants/error-types')
const errorHandle = (error, ctx) => {
let status, message;
switch (error.message) {
case errorTypes.NAME_OR_PASSWORD_IS_REQUIRED:
status = 400 // Bad Request
message = "用户名或者密码不能为空~"
break
case errorTypes.USER_ALREADY_EXISTS:
status = 409 // Conflict
message = "用户名已存在~"
break
default:
status = 404
message = "NOT FOUND"
}
ctx.status = 404
ctx.body = message
}
module.exports = errorHandle
javascript
/*----------------------------app/index.js----------------------------*/
const Koa = require('koa')
const bodyparser = require('koa-bodyparser')
const userRouter = require('../router/user.router')
const errorHandle = require('./error.handle')
const app = new Koa()
app.use(bodyparser())
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
app.on('error',errorHandle)
module.exports = app
javascript
/*-----------------------router/user.router.js--------------------------*/
const Router = require('koa-router')
const { create } = require('../controller/user.controller')
const { verifyUser } = require('../middleware/user.middleware')
const userRouter = new Router({ prefix: '/users' });
userRouter.post('/', verifyUser, create)
module.exports = userRouter
javascript
/*-----------------------service/user.service.js--------------------------*/
const connections = require('../app/database')
class UserService {
async create(user) {
const { name, password } = user;
const statement = `INSERT INTO users (name, password) VALUES (?, ?);`;
const result = await connections.execute(statement, [name, password]);
// 将user存储到数据库中
return result[0];
}
async getUserByName(name) {
const statement = `SELECT * FROM users WHERE name=?;`;
const result = await connections.execute(statement, [name])
return result[0]
}
}
module.exports = new UserService();
第二部分
第一节:用户注册接口-对密码进行加密
- 添加加密用户密码中间件
javascript
/*----------------------router/user.router.js-----------------*/
const Router = require('koa-router')
const { create } = require('../controller/user.controller')
const { verifyUser, handlePassword } = require('../middleware/user.middleware')
const userRouter = new Router({ prefix: '/users' });
userRouter.post('/', verifyUser, handlePassword, create)
module.exports = userRouter
- 在utils中新建password-handle.js文件,导出加密密码的方法
javascript
/*----------------------utils/password-handle.js-----------------*/
const crypto = require('crypto')
const md5password = (password) => {
const md5 = crypto.createHash('md5')
const result = md5.update(password).digest('hex') // 拿到16进制的结果
return result
}
module.exports = md5password
- 在middleware/user.middleware.js中创建handlePassword中间件并导入使用md5password这个方法
javascript
/*--------------------middleware/user.middleware.js------------------------*/
const connection = require('../app/database')
const errorTypes = require('../constants/error.types')
const service = require('../service/user.servics')
const md5password = require('../utils/password-handle')
const verifyUser = async (ctx, next) => {
// 1.获取用户名和密码
const { name, password } = ctx.request.body;
// 2.判断用户名或者密码不能空
if (!name || !password) {
const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED);
return ctx.app.emit('error', error, ctx);
}
// 3.判断这次注册的用户名是没有被注册过
const result = await service.getUserByName(name)
if (result.length) {
console.log('用户名已存在');
const error = new Error(errorTypes.USER_ALREADY_EXISTS);
return ctx.app.emit('error', error, ctx);
}
await next()
}
const handlePassword = async (ctx, next) => {
let { password } = ctx.request.body
ctx.request.body.password = md5password(password)
await next()
}
module.exports = {
verifyUser,
handlePassword
}
第二节:用户登录接口-登录逻辑结构的搭建
- 在router文件夹中新建一个auth.router.js文件,auth是授权的意思,表示对登录进行授权,创建并导出这个路由
javascript
/*-------------------router/auth.router.js-----------------------*/
const Router = require('koa-router')
const authRouter = new Router()
const {login} = require('../controller/auth.controller.js')
authRouter.post('/login',login)
module.exports = authRouter
- 在app/index.js中导入路由
javascript
/*-------------------app/index.js-----------------------*/
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
const userRouter = require('../router/user.router')
const authRouter = require('../router/auth.router')
const errorHandler = require('./error.handle')
const app = new Koa()
app.use(bodyParser())
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
app.use(authRouter.routes())
app.use(authRouter.allowedMethods())
app.on('error', errorHandler)
module.exports = app
- 新建控制器
javascript
/*-------------------controller/auth.controller.js-----------------------*/
class AuthController {
async login(ctx,next){
const {name,password} = ctx.request.body
ctx.body = `登录成功欢迎${name}回来`;
}
}
module.exports = new AuthController()
第三节:用户登录接口-登录验证逻辑完成
- 在middleware文件夹下创建auth.middleware.js,校验的中间件文件,应用到路由中
javascript
/*-----------------------middleware/auth.middleware.js--------------*/
const service = require("../service/user.servics")
const errorTypes = require('../constants/error.types')
const md5password = require('../utils/password-handle')
const verifyLogin = async (ctx, next) => {
// 1.获取用户名和密码
const { name, password } = ctx.request.body;
// 2.判断用户名和密码是否为空
if (!name || !password) {
const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED);
return ctx.app.emit('error', error, ctx);
}
// 3.判断用户是否存在的
const result = await service.getUserByName(name)
const user = result[0]
if (!user) {
const error = new Error(errorTypes.USER_DOES_NOT_EXISTS);
return ctx.app.emit('error', error, ctx);
}
// 4.判断密码是否和数据库中的密码是一致(加密)
if(md5password(password) !== user.password) {
const error = new Error(errorTypes.PASSWORD_IS_INCORRENT);
return ctx.app.emit('error', error, ctx);
}
ctx.user = user
await next()
}
module.exports = { verifyLogin }
- 在router/auth.router.js中导入它
javascript
/*-----------------------------router/auth.router.js---------------------*/
const Router = require('koa-router')
const authRouter = new Router()
const { login } = require('../controller/auth.controller.js')
const { verifyLogin } = require('../middleware/auth.middleware')
authRouter.post('/login', verifyLogin, login)
module.exports = authRouter
- 在constants/error.types.js中创建两个常量(用户不存在/用户密码错误)并导出
javascript
const NAME_OR_PASSWORD_IS_REQUIRED = 'name_or_password_is_required';
const USER_ALREADY_EXISTS = 'user_already_exists';
const USER_DOES_NOT_EXISTS = 'user_does_not_exists';
const PASSWORD_IS_INCORRENT = 'password_is_incorrent';
module.exports = {
NAME_OR_PASSWORD_IS_REQUIRED,
USER_ALREADY_EXISTS,
USER_DOES_NOT_EXISTS,
PASSWORD_IS_INCORRENT
}
- 在app/error.handle.js中编写错误处理代码
javascript
const errorTypes = require('../constants/error.types')
const errorHandler = (error, ctx) => {
let status, message;
switch (error.message) {
case errorTypes.NAME_OR_PASSWORD_IS_REQUIRED:
status = 400; // Bad Request
message = "用户名或者密码不能为空~"
break;
case errorTypes.USER_ALREADY_EXISTS:
status = 409; // conflict
message = "用户名已经存在~"
break;
case errorTypes.USER_DOES_NOT_EXISTS:
status = 400; // 参数错误
message = "用户名不存在~"
break;
case errorTypes.PASSWORD_IS_INCORRENT:
status = 400; // 参数错误
message = "密码是错误的~"
break;
default:
status = 404;
message = "NOT FOUND";
}
ctx.status = status;
ctx.body = message;
}
module.exports = errorHandler
第四节:动态加载所有的路由
- 在router文件夹中新建index.js作为路由入口文件
javascript
/*-------------------------routes/index.js*/
const fs = require('fs')
const useRoutes = function () {
fs.readdirSync(__dirname).forEach(file => {
if (file === 'index.js') return;
const router = require(`./${file}`)
this.use(router.routes())
this.use(router.allowedMethods())
})
}
module.exports = useRoutes
- app/index.js就可以不用一个个引入路由了,也不用每个都使用use两次了
javascript
/*--------------------app/index.js------------------------*/
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
const errorHandler = require('./error.handle')
const useRoutes = require('../router')
const app = new Koa()
app.useRoutes = useRoutes
app.use(bodyParser())
app.useRoutes()
app.on('error', errorHandler)
module.exports = app
第五节:登录接口-jwt颁发令牌
- npm i jsonwebtoken
- 在app文件夹中创建keys文件夹,并在终端中生成私钥和公钥保存到keys文件夹中
shell
openssl
> genrsa -out private.key 1024
> rsa -in private.key -pubout -out public.key
- 在app/config.js文件中读取并导出私钥和公钥
javascript
/*------------------app/config.js-------------------*/
const fs = require("fs")
const path = require('path')
const dotenv = require('dotenv');
const PRIVATE_KEY = fs.readFileSync(path.resolve(__dirname,'keys/private.key'))
const PUBLIC_KEY = fs.readFileSync(path.resolve(__dirname,'keys/public.key'))
dotenv.config()
module.exports = {
APP_PORT,
MYSQL_HOST,
MYSQL_PORT,
MYSQL_DATABASE,
MYSQL_USER,
MYSQL_PASSWORD
} = process.env
module.exports.PRIVATE_KEY = PRIVATE_KEY
module.exports.PUBLIC_KEY = PUBLIC_KEY
- 在controller/auth.controller.js文件中给用户颁发令牌token
javascript
/*----------------controller/auth.controller.js------------*/
const jwt = require("jsonwebtoken")
const { PRIVATE_KEY, PUBLIC_KEY } = require('../app/config')
class AuthController {
async login(ctx, next) {
const { id, name } = ctx.user
// 颁发令牌
const token = jwt.sign({ id, name }, PRIVATE_KEY, {
expiresIn: 60 * 60 * 24,
algorithm: 'RS256'
})
ctx.body = { id, name, token };
}
}
module.exports = new AuthController()
第六节:登录接口-jwt验证令牌
- 在router/auth.router.js里创建测试路由验证签名
javascript
/*---------------------router/auth.router.js----------------------*/
const Router = require('koa-router')
const authRouter = new Router()
const { login,success } = require('../controller/auth.controller.js')
const { verifyLogin,verifyAuth } = require('../middleware/auth.middleware')
authRouter.post('/login', verifyLogin, login)
authRouter.get('/test', verifyAuth, success)
module.exports = authRouter
- 在middleware/auth.middleware.js创建verifyAuth验证签名的函数
javascript
/*------------------------middleware/auth.middleware.js------------------------*/
const jwt = require('jsonwebtoken')
const fs = require('fs')
const path = require('path')
const service = require("../service/user.servics")
const errorTypes = require('../constants/error.types')
const md5password = require('../utils/password-handle')
const {PUBLIC_KEY} = require('../app/config')
const verifyLogin = async (ctx, next) => {
// 1.获取用户名和密码
const { name, password } = ctx.request.body;
// 2.判断用户名和密码是否为空
if (!name || !password) {
const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED);
return ctx.app.emit('error', error, ctx);
}
// 3.判断用户是否存在的
const result = await service.getUserByName(name)
const user = result[0]
if (!user) {
const error = new Error(errorTypes.USER_DOES_NOT_EXISTS);
return ctx.app.emit('error', error, ctx);
}
// 4.判断密码是否和数据库中的密码是一致(加密)
if (md5password(password) !== user.password) {
const error = new Error(errorTypes.PASSWORD_IS_INCORRENT);
return ctx.app.emit('error', error, ctx);
}
ctx.user = user
await next()
}
const verifyAuth = async (ctx, next) => {
console.log("验证授权的middleware");
// 1.获取token
const authorization = ctx.headers.authorization;
const token = authorization.replace('Bearer ', '')
// 2.验证token(id/name/iat/exp)
try{
const result = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] })
ctx.user = result
await next()
}catch(err){
const error = new Error(errorTypes.UNAUTHORIZATION)
ctx.app.emit('error',error, ctx)
}
}
module.exports = { verifyLogin, verifyAuth }
- 在controller/auth.controller.js中创建success控制器函数
javascript
/*---------------------controller/auth.controller.js-----------------------*/
const jwt = require("jsonwebtoken")
const { PRIVATE_KEY, PUBLIC_KEY } = require('../app/config')
class AuthController {
async login(ctx, next) {
const { id, name } = ctx.user
// 颁发令牌
const token = jwt.sign({ id, name }, PRIVATE_KEY, {
expiresIn: 60 * 60 * 24,
algorithm: 'RS256'
})
ctx.body = { id, name, token };
}
async success(ctx, next) {
ctx.body = ctx.user
}
}
module.exports = new AuthController()
- 在constants/err-types.js中定义一个错误常量
javascript
/*------------------------constants/err-types.js--------------------*/
const NAME_OR_PASSWORD_IS_REQUIRED = 'name_or_password_is_required';
const USER_ALREADY_EXISTS = 'user_already_exists';
const USER_DOES_NOT_EXISTS = 'user_does_not_exists';
const PASSWORD_IS_INCORRENT = 'password_is_incorrent';
const UNAUTHORIZATION = 'unauthorzation'
module.exports = {
NAME_OR_PASSWORD_IS_REQUIRED,
USER_ALREADY_EXISTS,
USER_DOES_NOT_EXISTS,
PASSWORD_IS_INCORRENT,
UNAUTHORIZATION
}
- 在app/error.handle.js中编写错误处理代码
javascript
/*-------------------------app/error.handle.js---------------------*/
const errorTypes = require('../constants/error.types')
const errorHandler = (error, ctx) => {
let status, message;
switch (error.message) {
case errorTypes.NAME_OR_PASSWORD_IS_REQUIRED:
status = 400; // Bad Request
message = "用户名或者密码不能为空~"
break;
case errorTypes.USER_ALREADY_EXISTS:
status = 409; // conflict
message = "用户名已经存在~"
break;
case errorTypes.USER_DOES_NOT_EXISTS:
status = 400; // 参数错误
message = "用户名不存在~"
break;
case errorTypes.PASSWORD_IS_INCORRENT:
status = 400; // 参数错误
message = "密码是错误的~"
break;
case errorTypes.UNAUTHORIZATION:
status = 401;
message = "无效的token~"
break;
default:
status = 404;
message = "NOT FOUND";
}
ctx.status = status;
ctx.body = message;
}
module.exports = errorHandler
第七节-发布动态-发布动态的逻辑处理
- 在router文件夹中新建moment.router.js文件
javascript
/*-----------------router/moment.router.js---------------*/
const Router = require('koa-router')
const momentRouter = new Router({ prefix: '/moment' })
const { verifyAuth } = require('../middleware/auth.middleware')
const { create } = require('../controller/moment.controller.js')
momentRouter.post('/', verifyAuth, create)
module.exports = momentRouter
- 在controller文件夹中新建moment.controller.js文件
javascript
/*-----------------controller/moment.controller.js------*/
class MomentController {
async create(ctx,next){
ctx.body = "发表动态成功~"
}
}
module.exports = new MomentController()
- 修改middleware/auth.middleware.js文件中的verifyAuth函数完善逻辑
javascript
/*------------------------middleware/auth.middleware.js------------------------*/
const jwt = require('jsonwebtoken')
const fs = require('fs')
const path = require('path')
const service = require("../service/user.servics")
const errorTypes = require('../constants/error.types')
const md5password = require('../utils/password-handle')
const { PUBLIC_KEY } = require('../app/config')
const verifyLogin = async (ctx, next) => {
// 1.获取用户名和密码
const { name, password } = ctx.request.body;
// 2.判断用户名和密码是否为空
if (!name || !password) {
const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED);
return ctx.app.emit('error', error, ctx);
}
// 3.判断用户是否存在的
const result = await service.getUserByName(name)
const user = result[0]
if (!user) {
const error = new Error(errorTypes.USER_DOES_NOT_EXISTS);
return ctx.app.emit('error', error, ctx);
}
// 4.判断密码是否和数据库中的密码是一致(加密)
if (md5password(password) !== user.password) {
const error = new Error(errorTypes.PASSWORD_IS_INCORRENT);
return ctx.app.emit('error', error, ctx);
}
ctx.user = user
await next()
}
const verifyAuth = async (ctx, next) => {
console.log("验证授权的middleware");
// 1.获取token
const authorization = ctx.headers.authorization;
if (!authorization) {
const error = new Error(errorTypes.UNAUTHORIZATION)
return ctx.app.emit('error', error, ctx)
}
const token = authorization.replace('Bearer ', '')
// 2.验证token(id/name/iat/exp)
try {
const result = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] })
ctx.user = result
await next()
} catch (err) {
const error = new Error(errorTypes.UNAUTHORIZATION)
ctx.app.emit('error', error, ctx)
}
}
module.exports = { verifyLogin, verifyAuth }
第八节:发布动态-插入动态到数据库
- 在mysql中创建moment表(动态表)
sql
CREATE TABLE IF NOT EXISTS `moment` (
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(1000) NOT NULL,
user_id INT NOT NULL,
createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id)
);
- controller/moment.controller.js中创建MomentController类,定义create控制器方法,并导出实例
javascript
/*------------------------controller/moment.controller.js-----------------------*/
const momentService = require('../service/moment.service')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId,content)
ctx.body = result
}
}
module.exports = new MomentController()
- 在service文件夹下创建moment.service.js文件
javascript
/*---------------------service/moment.service.js-------------------*/
const connection = require('../app/database')
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`;
const result = await connection.execute(statement, [content, userId]);
// 将user存储到数据库中
return result
}
}
module.exports = new MomentService();
第九节:查询动态-查询单个动态信息
- 在router/moment.router.js文件中创建查询get请求路由
javascript
/*-------------router/moment.router.js-----------*/
const Router = require('koa-router')
const momentRouter = new Router({ prefix: '/moment' })
const { verifyAuth } = require('../middleware/auth.middleware')
const { create, detail } = require('../controller/moment.controller.js')
momentRouter.post('/', verifyAuth, create)
momentRouter.get('/:momentId', detail)
module.exports = momentRouter
- 在controller/moment.controller.js编写detail查询动态数据控制器函数
javascript
/*-------------------controller/moment.controller.js-----------------*/
const momentService = require('../service/moment.service')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId,content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
}
module.exports = new MomentController()
- 在service/moment.service.js文件中编写getMomentById函数
javascript
/*----------------service/moment.service.js-------------*/
const connection = require('../app/database')
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`;
const result = await connection.execute(statement, [content, userId]);
return result
}
async getMomentById(id) {
const statement = `
SELECT
m.id id,m.content content,m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
WHERE m.id = ?;
`;
const result = await connection.execute(statement, [id]);
// 将user存储到数据库中
return result[0]
}
}
module.exports = new MomentService();
第十节:查询动态-查询动态列表信息
- 在router/moment.router.js添加路由
javascript
const { create, detail, list } = require('../controller/moment.controller.js')
momentRouter.get('/', list)
- 在controller/moment.controller.js编写list查询动态数据控制器函数
javascript
/*------------------controller/moment.controller--------------------*/
const momentService = require('../service/moment.service')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId, content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
async list(ctx, next) {
// 1.获取数据
const { offset, size } = ctx.query
// 2.查询列表
const result = await momentService.getMomentList(offset, size)
ctx.body = result
}
}
module.exports = new MomentController()
- 在service/moment.service.js文件中编写getMomentList函数
javascript
/*-------------------service/moment.service.js-----------------------*/
const connection = require('../app/database')
const sqlFragment = `
SELECT
m.id id,m.content content,m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
`
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`;
const result = await connection.execute(statement, [content, userId]);
return result
}
async getMomentById(id) {
const statement = `
${sqlFragment}
WHERE m.id = ?;
`;
const result = await connection.execute(statement, [id]);
// 将user存储到数据库中
return result[0]
}
async getMomentList(offset, size) {
const statement = `
${sqlFragment}
LIMIT ?, ?;
`;
const result = await connection.execute(statement, [offset, size]);
// 将user存储到数据库中
return result[0]
}
}
module.exports = new MomentService();
第三部分
第一节:修改动态-修改动态的逻辑
评论修改需要满足两个条件(两个中间件)
1.用户必须登录
2.验证登录的用户是否有具备权限去修改内容
- 在router/moment.router.js文件中添加路由
- 两个中间件,verifyAuth(用户登录),(verifyPermission)用户是否有权限修改动态
javascript
/*-----------------router/moment.router.js-------------------*/
const Router = require('koa-router')
const momentRouter = new Router({ prefix: '/moment' })
const { verifyAuth, verifyPermission } = require('../middleware/auth.middleware')
const { create, list, detail, update } = require('../controller/moment.controller.js')
momentRouter.post('/', verifyAuth, create)
momentRouter.get('/', list)
momentRouter.get('/:momentId', detail)
// 1.用户必须登录 2.用户具备权限
momentRouter.patch('/:momentId', verifyAuth, verifyPermission, update)
module.exports = momentRouter
- 在middleware/auth.middleware.js文件中添加verifyPermission方法
javascript
const jwt = require('jsonwebtoken')
const fs = require('fs')
const path = require('path')
const userService = require("../service/user.servics")
const authService = require("../service/auth.service")
const errorTypes = require('../constants/error.types')
const md5password = require('../utils/password-handle')
const { PUBLIC_KEY } = require('../app/config')
const verifyLogin = async (ctx, next) => {
// 1.获取用户名和密码
const { name, password } = ctx.request.body;
// 2.判断用户名和密码是否为空
if (!name || !password) {
const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED);
return ctx.app.emit('error', error, ctx);
}
// 3.判断用户是否存在的
const result = await userService.getUserByName(name)
const user = result[0]
if (!user) {
const error = new Error(errorTypes.USER_DOES_NOT_EXISTS);
return ctx.app.emit('error', error, ctx);
}
// 4.判断密码是否和数据库中的密码是一致(加密)
if (md5password(password) !== user.password) {
const error = new Error(errorTypes.PASSWORD_IS_INCORRENT);
return ctx.app.emit('error', error, ctx);
}
ctx.user = user
await next()
}
const verifyAuth = async (ctx, next) => {
console.log("验证授权的middleware");
// 1.获取token
const authorization = ctx.headers.authorization;
if (!authorization) {
const error = new Error(errorTypes.UNAUTHORIZATION)
return ctx.app.emit('error', error, ctx)
}
const token = authorization.replace('Bearer ', '')
// 2.验证token(id/name/iat/exp)
try {
const result = jwt.verify(token, PUBLIC_KEY, {
algorithms: ["RS256"]
});
ctx.user = result;
await next();
} catch (err) {
const error = new Error(errorTypes.UNAUTHORIZATION);
ctx.app.emit('error', error, ctx);
}
}
const verifyPermission = async (ctx, next) => {
console.log("验证权限的middleware~");
// 1.获取参数
const { momentId } = ctx.params
const { id } = ctx.user
const { content } = ctx.request.body
// 2.查询是否具有权限
try {
const isPermission = await authService.checkMoment(momentId, id)
if (!isPermission) throw new Error();
await next()
} catch (err) {
const error = new Error(errorTypes.UNPERMISSION)
return ctx.app.emit('error', error, ctx)
}
}
module.exports = { verifyLogin, verifyAuth, verifyPermission }
- 在service文件夹下新建auth.service.js文件单独封装权限查询的service
javascript
/*---------------service/auth.service.js--------------*/
const connection = require('../app/database')
class AuthService {
async checkMoment(momentId, userId) {
const statement = `
SELECT * FROM moment WHERE id = ? AND user_id = ?;
`
const [result] = await connection.execute(statement, [momentId, userId])
return result.length === 0 ? false : true
}
async checkComment() {
}
}
module.exports = new AuthService()
- 在service/moment.service.js文件中编写update方法
javascript
/*-------------------------------------*/
const connection = require('../app/database')
const sqlFragment = `
SELECT
m.id id,m.content content,m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
`
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`;
const result = await connection.execute(statement, [content, userId]);
return result
}
async getMomentById(id) {
const statement = `
${sqlFragment}
WHERE m.id = ?;
`;
const result = await connection.execute(statement, [id]);
// 将user存储到数据库中
return result[0]
}
async getMomentList(offset, size) {
const statement = `
${sqlFragment}
LIMIT ?, ?;
`;
const result = await connection.execute(statement, [offset, size]);
// 将user存储到数据库中
return result[0]
}
async update(content, momentId) {
const statement = `
UPDATE moment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, momentId])
return result
}
}
module.exports = new MomentService();
- 在controller/moment.controller.js文件中编写update控制器方法
javascript
/*---------------controller/moment.controller.js---------------*/
const momentService = require('../service/moment.service')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId, content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
async list(ctx, next) {
// 1.获取数据
const { offset, size } = ctx.query
// 2.查询列表
const result = await momentService.getMomentList(offset, size)
ctx.body = result
}
async update(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
const { content } = ctx.request.body
// 2.修改内容
const result = await momentService.update(content, momentId)
ctx.body = '修改内容' + momentId + content + id
}
}
module.exports = new MomentController()
- 创建错误常量UNPERMISSION和处理代码
javascript
/*---------------------------constants/error.types.js------------------------*/
const NAME_OR_PASSWORD_IS_REQUIRED = 'name_or_password_is_required';
const USER_ALREADY_EXISTS = 'user_already_exists';
const USER_DOES_NOT_EXISTS = 'user_does_not_exists';
const PASSWORD_IS_INCORRENT = 'password_is_incorrent';
const UNAUTHORIZATION = 'unauthorzation'
const UNPERMISSION = 'unpermission'
module.exports = {
NAME_OR_PASSWORD_IS_REQUIRED,
USER_ALREADY_EXISTS,
USER_DOES_NOT_EXISTS,
PASSWORD_IS_INCORRENT,
UNAUTHORIZATION,
UNPERMISSION
}
javascript
/*---------------------------app/error.handle.js------------------------*/
const errorTypes = require('../constants/error.types')
const errorHandler = (error, ctx) => {
let status, message;
switch (error.message) {
case errorTypes.NAME_OR_PASSWORD_IS_REQUIRED:
status = 400; // Bad Request
message = "用户名或者密码不能为空~"
break;
case errorTypes.USER_ALREADY_EXISTS:
status = 409; // conflict
message = "用户名已经存在~"
break;
case errorTypes.USER_DOES_NOT_EXISTS:
status = 400; // 参数错误
message = "用户名不存在~"
break;
case errorTypes.PASSWORD_IS_INCORRENT:
status = 400; // 参数错误
message = "密码是错误的~"
break;
case errorTypes.UNAUTHORIZATION:
status = 401;
message = "无效的token~"
break;
case errorTypes.UNPERMISSION:
status = 401
message = "您不具备操作的权限~"
break
default:
status = 404;
message = "NOT FOUND";
}
ctx.status = status;
ctx.body = message;
}
module.exports = errorHandler
第二节:删除动态-删除动态的逻辑
- 在router/moment.router.js文件中添加delete请求路由
javascript
/*---------------router/moment.router.js------------------*/
const Router = require('koa-router')
const momentRouter = new Router({ prefix: '/moment' })
const { verifyAuth, verifyPermission } = require('../middleware/auth.middleware')
const { create, list, detail, update, remove } = require('../controller/moment.controller.js')
momentRouter.post('/', verifyAuth, create)
momentRouter.get('/', list)
momentRouter.get('/:momentId', detail)
// 1.用户必须登录 2.用户具备权限
momentRouter.patch('/:momentId', verifyAuth, verifyPermission, update)
momentRouter.delete('/:momentId', verifyAuth, verifyPermission, remove)
module.exports = momentRouter
- 在controller/moment.controller.js编写remove控制器函数
javascript
/*---------------controller/moment.controller.js---------------------*/
const momentService = require('../service/moment.service')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId, content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
async list(ctx, next) {
// 1.获取数据
const { offset, size } = ctx.query
// 2.查询列表
const result = await momentService.getMomentList(offset, size)
ctx.body = result
}
async update(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
const { content } = ctx.request.body
// 2.修改内容
const result = await momentService.update(content, momentId)
ctx.body = '修改内容' + momentId + content + id
}
async remove(ctx, next) {
// 1.获取momentId
const { momentId } = ctx.params
// 2.删除内容
const result = await momentService.remove(momentId)
ctx.body = result
}
}
module.exports = new MomentController()
- 在service/moment.service.js中编写remove函数
javascript
/*----------------------service/moment.service.js---------------------*/
const connection = require('../app/database')
const sqlFragment = `
SELECT
m.id id,m.content content,m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
`
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`;
const result = await connection.execute(statement, [content, userId]);
return result
}
async getMomentById(id) {
const statement = `
${sqlFragment}
WHERE m.id = ?;
`;
const result = await connection.execute(statement, [id]);
// 将user存储到数据库中
return result[0]
}
async getMomentList(offset, size) {
const statement = `
${sqlFragment}
LIMIT ?, ?;
`;
const result = await connection.execute(statement, [offset, size]);
// 将user存储到数据库中
return result[0]
}
async update(content, momentId) {
const statement = `
UPDATE moment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, momentId])
return result
}
async remove(momentId){
const statement = `
DELETE FROM moment WHERE id = ?;
`;
const [result] = await connection.execute(statement, [momentId])
return result
}
}
module.exports = new MomentService();
第三节:评论接口-表分析和创建表
- 创建评论表,字段如下
- id
- content
- user_id (外键约束)
- moment_id (外键约束)
- comment_id(外键约束comment_id 或者 null)
- updateAt
- createAt
sql
CREATE TABLE IF NOT EXISTS `comment`(
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(1000) NOT NULL,
moment_id INT NOT NULL,
user_id INT NOT NULL,
comment_id INT DEFAULT NULL,
createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(comment_id) REFERENCES comment(id) ON DELETE CASCADE ON UPDATE CASCADE
);
第四节:发布评论-发表评论的逻辑
- 在router文件夹中创建comment.router.js文件
javascript
/*---------------router/comment.router.js------------------*/
const Router = require('koa-router')
const {
verifyAuth
} = require('../middleware/auth.middleware')
const { create } = require('../controller/comment.controller')
const commentRouter = new Router({ prefix: '/comment' })
commentRouter.post('/', verifyAuth, create)
module.exports = commentRouter
- 在controller文件夹中创建comment.controller.js文件
javascript
/*--------------------controller/comment.controller.js---------------------*/
const service = require('../service/comment.service.js')
class CommentController {
async create(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const result = await service.create(momentId, content, id)
ctx.body = result
}
}
module.exports = new CommentController()
- service文件夹中创建comment.service.js文件
javascript
/*----------------------service/comment.service.js-------------------*/
const connection = require('../app/database')
class CommentService {
async create(momentId, content, userId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id) VALUES (?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId])
return result
}
}
module.exports = new CommentService()
第五节:回复评论-回复评论的逻辑
- 在router/comment.router.js文件中创建回复评论路由
- 符合restful风格的接口
javascript
/*----------------router/comment.router.js----------------*/
const Router = require('koa-router')
const {
verifyAuth
} = require('../middleware/auth.middleware')
const { create, reply } = require('../controller/comment.controller')
const commentRouter = new Router({ prefix: '/comment' })
commentRouter.post('/', verifyAuth, create)
commentRouter.post('/:commentId/reply', verifyAuth, reply)
module.exports = commentRouter
- 在controller/moment.controller.js中创建reply控制器函数
javascript
/*-------------controller/moment.controller.js------------*/
const service = require('../service/comment.service.js')
class CommentController {
async create(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const result = await service.create(momentId, content, id)
ctx.body = result
}
async reply(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const { commentId } = ctx.params
const result = await service.reply(momentId, content, id, commentId)
ctx.body = result
}
}
module.exports = new CommentController()
- 在service/comment.service.js文件中创建reply操作数据库的函数
javascript
/*----------service/comment.service.js------------------*/
const connection = require('../app/database')
class CommentService {
async create(momentId, content, userId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id) VALUES (?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId])
return result
}
async reply(momentId, content, userId, commentId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id,comment_id) VALUES (?,?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId, commentId])
return result
}
}
module.exports = new CommentService()
第六节:修改评论-修改评论的逻辑
- 在router/comment.router.js文件中创建修改评论路由
javascript
/*--------------router/comment.router.js---------------*/
const Router = require('koa-router')
const {
verifyAuth,
verifyPermission
} = require('../middleware/auth.middleware')
const { create, reply, update } = require('../controller/comment.controller')
const commentRouter = new Router({ prefix: '/comment' })
commentRouter.post('/', verifyAuth, create)
commentRouter.post('/:commentId/reply', verifyAuth, reply)
// 修改评论
commentRouter.patch('/:commentId', verifyAuth, update)
// 删除评论
module.exports = commentRouter
- 在controller/comment.controller.js中编写update函数
javascript
/*---------------controller/comment.controller.js------------------*/
const service = require('../service/comment.service.js')
class CommentController {
async create(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const result = await service.create(momentId, content, id)
ctx.body = result
}
async reply(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const { commentId } = ctx.params
const result = await service.reply(momentId, content, id, commentId)
ctx.body = result
}
async update(ctx, next) {
const { commentId } = ctx.params
const { content } = ctx.request.body
const result = await service.update(commentId, content)
ctx.body = result
}
}
module.exports = new CommentController()
- 在serviice/comment.service.js文件中编写update函数
javascript
/*-------------------serviice/comment.service.js----------------------*/
const connection = require('../app/database')
class CommentService {
async create(momentId, content, userId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id) VALUES (?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId])
return result
}
async reply(momentId, content, userId, commentId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id,comment_id) VALUES (?,?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId, commentId])
return result
}
async update(commentId, content) {
const statement = `
UPDATE comment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, commentId])
return result
}
}
module.exports = new CommentService()
第七节:权限认证-对所有的资源有效
- 在router/comment.router.js修改评论路由中添加verifyPermission权限许可认证中间件
javascript
const Router = require('koa-router')
const {
verifyAuth,
verifyPermission
} = require('../middleware/auth.middleware')
const { create, reply, update } = require('../controller/comment.controller')
const commentRouter = new Router({ prefix: '/comment' })
commentRouter.post('/', verifyAuth, create)
commentRouter.post('/:commentId/reply', verifyAuth, reply)
// 修改评论
commentRouter.patch('/:commentId', verifyAuth, verifyPermission, update)
// 删除评论
module.exports = commentRouter
- 在middleware/auth.middleware.js文件中修改verifyPermission中间件
- 提供两种思路,一种使用闭包传递表名字符串
- 一种根据restful风格可以获取表名
javascript
/*--------------------*/
const jwt = require('jsonwebtoken')
const fs = require('fs')
const path = require('path')
const userService = require("../service/user.servics")
const authService = require("../service/auth.service")
const errorTypes = require('../constants/error.types')
const md5password = require('../utils/password-handle')
const { PUBLIC_KEY } = require('../app/config')
const verifyLogin = async (ctx, next) => {
// 1.获取用户名和密码
const { name, password } = ctx.request.body;
// 2.判断用户名和密码是否为空
if (!name || !password) {
const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED);
return ctx.app.emit('error', error, ctx);
}
// 3.判断用户是否存在的
const result = await userService.getUserByName(name)
const user = result[0]
if (!user) {
const error = new Error(errorTypes.USER_DOES_NOT_EXISTS);
return ctx.app.emit('error', error, ctx);
}
// 4.判断密码是否和数据库中的密码是一致(加密)
if (md5password(password) !== user.password) {
const error = new Error(errorTypes.PASSWORD_IS_INCORRENT);
return ctx.app.emit('error', error, ctx);
}
ctx.user = user
await next()
}
const verifyAuth = async (ctx, next) => {
console.log("验证授权的middleware");
// 1.获取token
const authorization = ctx.headers.authorization;
if (!authorization) {
const error = new Error(errorTypes.UNAUTHORIZATION)
return ctx.app.emit('error', error, ctx)
}
const token = authorization.replace('Bearer ', '')
// 2.验证token(id/name/iat/exp)
try {
const result = jwt.verify(token, PUBLIC_KEY, {
algorithms: ["RS256"]
});
ctx.user = result;
await next();
} catch (err) {
const error = new Error(errorTypes.UNAUTHORIZATION);
ctx.app.emit('error', error, ctx);
}
}
/**
* 1.很多的内容都需要验证权限:修改/删除动态,修改/删除评论
* 2.接口:业务接口系统/后端管理系统
* 一对一:user -> role
* 多对多:role -> memu(删除动态/修改动态)
*/
/* const verifyPermission = (tableName) => {
return async (ctx, next) => {
console.log("验证权限的middleware~");
// 1.获取参数
const { momentId } = ctx.params
const { id } = ctx.user
const { content } = ctx.request.body
// 2.查询是否具有权限
try {
const isPermission = await authService.checkResource(tableName, momentId, id)
if (!isPermission) throw new Error();
await next()
} catch (err) {
const error = new Error(errorTypes.UNPERMISSION)
return ctx.app.emit('error', error, ctx)
}
}
} */
const verifyPermission = async (ctx, next) => {
console.log("验证权限的middleware~");
// 1.获取参数 { commentId: '1' }
const [resourceKey] = Object.keys(ctx.params)
const tableName = resourceKey.replace('Id','')
const resourceId = ctx.params[resourceKey]
const { id } = ctx.user
// 2.查询是否具有权限
try {
const isPermission = await authService.checkResource(tableName, resourceId, id)
if (!isPermission) throw new Error();
await next()
} catch (err) {
const error = new Error(errorTypes.UNPERMISSION)
return ctx.app.emit('error', error, ctx)
}
}
module.exports = { verifyLogin, verifyAuth, verifyPermission }
- 在service/auth.service.js文件中把checkMoment修改为checkResource
javascript
/*----------------service/auth.service.js----------------------*/
const connection = require('../app/database')
class AuthService {
async checkResource(tableName,id, userId) {
const statement = `
SELECT * FROM ${tableName} WHERE id = ? AND user_id = ?;
`
const [result] = await connection.execute(statement, [id, userId])
return result.length === 0 ? false : true
}
}
module.exports = new AuthService()
第八节:删除评论-删除评论的逻辑
- 在router/comment.router.js文件中添加删除评论路由
javascript
/*--------------router/comment.router.js-----------*/
const Router = require('koa-router')
const {
verifyAuth,
verifyPermission
} = require('../middleware/auth.middleware')
const { create, reply, update } = require('../controller/comment.controller')
const commentRouter = new Router({ prefix: '/comment' })
commentRouter.post('/', verifyAuth, create)
commentRouter.post('/:commentId/reply', verifyAuth, reply)
// 修改评论
commentRouter.patch('/:commentId', verifyAuth, verifyPermission, update)
// 删除评论
commentRouter.delete('/:commentId', verifyAuth, verifyPermission, remove)
module.exports = commentRouter
- 在controller/comment.controller.js文件中添加remove控制器函数
javascript
/*----------------------controller/comment.controller.js------------------------*/
const service = require('../service/comment.service.js')
class CommentController {
async create(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const result = await service.create(momentId, content, id)
ctx.body = result
}
async reply(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const { commentId } = ctx.params
const result = await service.reply(momentId, content, id, commentId)
ctx.body = result
}
async update(ctx, next) {
const { commentId } = ctx.params
const { content } = ctx.request.body
const result = await service.update(commentId, content)
ctx.body = result
}
async remove(ctx, next) {
const { commentId } = ctx.params
const result = await service.remove(commentId)
ctx.body = result
}
}
module.exports = new CommentController()
- 在service/comment.service.js文件中添加remove方法
javascript
/*------------service/comment.service.js----------*/
const connection = require('../app/database')
class CommentService {
async create(momentId, content, userId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id) VALUES (?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId])
return result
}
async reply(momentId, content, userId, commentId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id,comment_id) VALUES (?,?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId, commentId])
return result
}
async update(commentId, content) {
const statement = `
UPDATE comment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, commentId])
return result
}
async remove(commentId) {
const statement = `
DELETE FROM comment WHERE id = ?;
`;
const [result] = await connection.execute(statement, [commentId])
return result
}
}
module.exports = new CommentService()
第四部分
第一节:评论展示-动态列表中展示评论个数
- 在service/moment.service.js文件中修改getMomentList函数的sql语句可以获取动态的评论数量(子查询)
javascript
/*--------------service/moment.service.js-------------------*/
const connection = require('../app/database')
/* const sqlFragment = `
SELECT
m.id id,m.content content,m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
` */
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`;
const result = await connection.execute(statement, [content, userId]);
return result
}
async getMomentById(id) {
const statement = `
SELECT
m.id id,m.content content,m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
WHERE m.id = ?;
`;
const result = await connection.execute(statement, [id]);
// 将user存储到数据库中
return result[0]
}
async getMomentList(offset, size) {
const statement = `
SELECT
m.id, m.content, m.createAt, m.updateAt, JSON_OBJECT("id",u.id,"name",u.name) author,
(SELECT COUNT(*) FROM comment c WHERE c.moment_id = m.id) commentCount
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LIMIT 0,5
;
`;
const result = await connection.execute(statement, [offset, size]);
// 将user存储到数据库中
return result[0]
}
async update(content, momentId) {
const statement = `
UPDATE moment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, momentId])
return result
}
async remove(momentId) {
const statement = `
DELETE FROM moment WHERE id = ?;
`;
const [result] = await connection.execute(statement, [momentId])
return result
}
}
module.exports = new MomentService();
第二节:评论展示-单独接口获取评论列表
- 在router/comment.router.js文件中添加获取评论列表路由
javascript
/*----------------router/comment.router.js----------------*/
const Router = require('koa-router')
const {
verifyAuth,
verifyPermission
} = require('../middleware/auth.middleware')
const { create, reply, update, remove, list } = require('../controller/comment.controller')
const commentRouter = new Router({ prefix: '/comment' })
// 发表评论
commentRouter.post('/', verifyAuth, create)
commentRouter.post('/:commentId/reply', verifyAuth, reply)
// 修改评论
commentRouter.patch('/:commentId', verifyAuth, verifyPermission, update)
// 删除评论
commentRouter.delete('/:commentId', verifyAuth, verifyPermission, remove)
// 获取评论列表
commentRouter.get('/', list)
module.exports = commentRouter
- 在controller/comment.controller.js文件中添加list控制器函数
javascript
/*----------------controller/comment.controller.js--------------------*/
const service = require('../service/comment.service.js')
class CommentController {
async create(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const result = await service.create(momentId, content, id)
ctx.body = result
}
async reply(ctx, next) {
const { momentId, content } = ctx.request.body
const { id } = ctx.user
const { commentId } = ctx.params
const result = await service.reply(momentId, content, id, commentId)
ctx.body = result
}
async update(ctx, next) {
const { commentId } = ctx.params
const { content } = ctx.request.body
const result = await service.update(commentId, content)
ctx.body = result
}
async remove(ctx, next) {
const { commentId } = ctx.params
const result = await service.remove(commentId)
ctx.body = result
}
async list(ctx, next) {
const { momentId } = ctx.query
const result = await service.getCommentsByMomentId(momentId)
ctx.body = result
}
}
module.exports = new CommentController()
- 在service/comment.service.js文件中编写getCommentsByMomentId函数
javascript
/*--------------------service/comment.service.js---------------------*/
const connection = require('../app/database')
class CommentService {
async create(momentId, content, userId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id) VALUES (?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId])
return result
}
async reply(momentId, content, userId, commentId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id,comment_id) VALUES (?,?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId, commentId])
return result
}
async update(commentId, content) {
const statement = `
UPDATE comment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, commentId])
return result
}
async remove(commentId) {
const statement = `
DELETE FROM comment WHERE id = ?;
`;
const [result] = await connection.execute(statement, [commentId])
return result
}
async getCommentsByMomentId(momentId){
const statement = `
SELECT * FROM comment WHERE moment_id = ?
`;
const [result] = await connection.execute(statement,[momentId])
return result
}
}
module.exports = new CommentService()
第三节:评论展示-动态详情获取评论列表
- 在service/moment.service.js文件中修改getMomentById函数
javascript
/*------------------service/moment.service.js-------------------------*/
const connection = require('../app/database')
/* const sqlFragment = `
SELECT
m.id id,m.content content,m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
` */
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`;
const result = await connection.execute(statement, [content, userId]);
return result
}
async getMomentById(id) {
const statement = `
SELECT
m.id,m.content,m.createAt,m.updateAt,JSON_OBJECT("id",u.id,"name",u.name) author,
JSON_ARRAYAGG(JSON_OBJECT("id",c.id,"content",c.content,"createTime",c.createAt,
"user",JSON_OBJECT("id",cu.id,"name",cu.name)
)) comments
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LEFT JOIN comment c ON c.moment_id = m.id
LEFT JOIN users cu ON c.user_id = cu.id
WHERE m.id = 2;
`;
const result = await connection.execute(statement, [id]);
// 将user存储到数据库中
return result[0]
}
async getMomentList(offset, size) {
const statement = `
SELECT
m.id, m.content, m.createAt, m.updateAt, JSON_OBJECT("id",u.id,"name",u.name) author,
(SELECT COUNT(*) FROM comment c WHERE c.moment_id = m.id) commentCount
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LIMIT 0,5
;
`;
const result = await connection.execute(statement, [offset, size]);
// 将user存储到数据库中
return result[0]
}
async update(content, momentId) {
const statement = `
UPDATE moment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, momentId])
return result
}
async remove(momentId) {
const statement = `
DELETE FROM moment WHERE id = ?;
`;
const [result] = await connection.execute(statement, [momentId])
return result
}
}
module.exports = new MomentService();
- 在service/comment.service.js文件中修改getCommentsByMomentId函数
javascript
/*------------------service/comment.service.js-------------------------*/
const connection = require('../app/database')
class CommentService {
async create(momentId, content, userId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id) VALUES (?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId])
return result
}
async reply(momentId, content, userId, commentId) {
const statement = `
INSERT INTO comment (content, moment_id,user_id,comment_id) VALUES (?,?,?,?);
`;
const [result] = await connection.execute(statement, [content, momentId, userId, commentId])
return result
}
async update(commentId, content) {
const statement = `
UPDATE comment SET content = ? WHERE id = ?;
`;
const [result] = await connection.execute(statement, [content, commentId])
return result
}
async remove(commentId) {
const statement = `
DELETE FROM comment WHERE id = ?;
`;
const [result] = await connection.execute(statement, [commentId])
return result
}
async getCommentsByMomentId(momentId){
const statement = `
SELECT c.id,c.content,c.comment_id commentId, c.createAt createTime,
JSON_OBJECT('id', u.id,'name', u.name) author
FROM comment c
LEFT JOIN users u ON c.user_id = u.id
WHERE moment_id = ?;
`;
const [result] = await connection.execute(statement,[momentId])
return result
}
}
module.exports = new CommentService()
第四节:创建标签-定义直接创建标签接口
- 创建标签表
sql
CREATE TABLE IF NOT EXISTS `lable`(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10) NOT NULL UNIQUE,
createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
- 在router文件夹中创建label.router.js文件
javascript
/*------------router/label.router.js-------------*/
const Router = require('koa-router')
const {
verifyAuth
} = require('../middleware/auth.middleware')
const {
create
} = require('../controller/label.controller.js')
const labelRouter = new Router({ prefix: '/label' })
labelRouter.post('/',verifyAuth, create)
module.exports = labelRouter
- 在controller文件夹中创建label.controller.js文件
javascript
/*---------------controller/label.controller.js-------------------*/
const service = require('../service/label.service')
class LaberController {
async create(ctx, next) {
const { name } = ctx.request.body
const result = await service.create(name)
ctx.body = result
}
}
module.exports = new LaberController()
- 在service文件中创建label.service.js文件
javascript
/*-----------service/label.service.js------------*/
const connection = require('../app/database')
class LabelService {
async create(name){
const statement = `INSERT INTO label (name) VALUES (?);`;
const [result] = await connection.execute(statement, [name]);
return result;
}
}
module.exports = new LabelService()
第五节:动态标签-给动态添加标签
- 创建动态和标签的关系表
sql
CREATE TABLE IF NOT EXISTS `moment_label` (
moment_id INT NOT NULL,
label_id INT NOT NULL,
createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(moment_id,label_id),
FOREIGN KEY(moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(label_id) REFERENCES label(id) ON DELETE CASCADE ON UPDATE CASCADE
);
- 在router/moment.router.js文件中添加路由
- 定义接口
- 作用:给动态添加标签
- 请求:POST
- 接口:moment/1/labels
- 参数:labels
- 例子:body{ labels:["前端"] }
- 数据:{}
javascript
/*-------------router/moment.router.js--------------*/
const Router = require('koa-router')
const momentRouter = new Router({ prefix: '/moment' })
const { verifyAuth, verifyPermission } = require('../middleware/auth.middleware')
const { create, list, detail, update, remove, addLabels } = require('../controller/moment.controller.js')
momentRouter.post('/', verifyAuth, create)
momentRouter.get('/', list)
momentRouter.get('/:momentId', detail)
// 1.用户必须登录 2.用户具备权限
momentRouter.patch('/:momentId', verifyAuth, verifyPermission, update)
momentRouter.delete('/:momentId', verifyAuth, verifyPermission, remove)
// 给动态添加标签
momentRouter.post('/:momentId/labels', verifyAuth, verifyPermission, addLabels)
module.exports = momentRouter
- 在controller/moment.controller.js文件中创建控制器函数
javascript
/*----------------controller/moment.controller.js------------------*/
const momentService = require('../service/moment.service')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId, content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
async list(ctx, next) {
// 1.获取数据
const { offset, size } = ctx.query
// 2.查询列表
const result = await momentService.getMomentList(offset, size)
ctx.body = result
}
async update(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
const { content } = ctx.request.body
// 2.修改内容
const result = await momentService.update(content, momentId)
ctx.body = '修改内容' + momentId + content + id
}
async remove(ctx, next) {
// 1.获取momentId
const { momentId } = ctx.params
// 2.删除内容
const result = await momentService.remove(momentId)
ctx.body = result
}
async addLabels(ctx, next) {
const { labels } = ctx.request.body
ctx.body = "给动态添加标签"
}
}
module.exports = new MomentController()
第六节:创建标签-判断标签是否存在
- 在middleware文件夹中创建label.middleware.js文件
javascript
/*------------------middleware/label.middleware.js----------------*/
const service = require('../service/label.service')
const verifyLabelExists = async (ctx, next) => {
// 1.取出要添加的所有的标签
const {labels} = ctx.request.body
// 2.判断每一个标签在label表中是否存在
const newLabels = []
for(let name of labels) {
const labelResult = await service.getLabelByName(name)
const label = {name}
if(!labelResult) {
// 创建标签数据
const result = await service.create(name)
label.id = result.insertId
} else {
label.id = labelResult.id
}
newLabels.push(label)
}
ctx.labels = newLabels
await next()
}
module.exports = {
verifyLabelExists
}
- 在service/label.service.js文件中创建getLabelByName函数
javascript
/*---------------service/label.service.js--------------------*/
const connection = require('../app/database')
class LabelService {
async create(name){
const statement = `INSERT INTO label (name) VALUES (?);`;
const [result] = await connection.execute(statement, [name]);
return result;
}
async getLabelByName(name) {
const statement = `SELECT * FROM label WHERE name = ?;`
const [result] = await connection.execute(statement, [name])
return result[0]
}
}
module.exports = new LabelService()
- 在router/moment.router.js文件中引入并使用中间件
javascript
/*--------------router/moment.router.js-------------*/
const Router = require('koa-router')
const momentRouter = new Router({ prefix: '/moment' })
const { verifyAuth, verifyPermission } = require('../middleware/auth.middleware')
const { create, list, detail, update, remove, addLabels } = require('../controller/moment.controller.js')
const { verifyLabelExists } = require('../middleware/label.middleware.js')
momentRouter.post('/', verifyAuth, create)
momentRouter.get('/', list)
momentRouter.get('/:momentId', detail)
// 1.用户必须登录 2.用户具备权限
momentRouter.patch('/:momentId', verifyAuth, verifyPermission, update)
momentRouter.delete('/:momentId', verifyAuth, verifyPermission, remove)
// 给动态添加标签
momentRouter.post('/:momentId/labels', verifyAuth, verifyPermission, verifyLabelExists, addLabels)
module.exports = momentRouter
第七节:动态标签-给动态添加标签
- 在controller/moment.controller.js文件中完成addLabels控制器函数
javascript
/*-------------controller/moment.controller.js------------*/
const momentService = require('../service/moment.service')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId, content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
async list(ctx, next) {
// 1.获取数据
const { offset, size } = ctx.query
// 2.查询列表
const result = await momentService.getMomentList(offset, size)
ctx.body = result
}
async update(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
const { content } = ctx.request.body
// 2.修改内容
const result = await momentService.update(content, momentId)
ctx.body = '修改内容' + momentId + content
}
async remove(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
// 2.删除动态
const result = await momentService.remove(momentId)
ctx.body = '删除动态成功'
}
async addLabels(ctx, next) {
// 1.获取标签和动态id
const { labels } = ctx
const { momentId } = ctx.params
// 2.添加所有的标签
for(let label of labels) {
// 2.1判断标签是否已经和动态有关系
const isExists = await momentService.hasLabel(momentId, label.id)
if(!isExists){
const result = await momentService.addLabel(momentId, label.id)
}
}
ctx.body = '给动态添加标签成功~'
}
}
module.exports = new MomentController()
- 在service/moment.service.js文件中创建hasLabel和addLabel方法
javascript
/*------------------service/moment.service.js----------------------*/
const connection = require('../app/database')
/* const sqlFragment = `
SELECT
m.id id,
m.content content,
m.createAt createAt,
m.updateAt updateAt,
JSON_OBJECT('id',u.id,'name',u.name) author
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
` */
class MomentService {
async create(userId, content) {
const statement = `INSERT INTO moment (content, user_id) VALUES (?, ?);`
const result = await connection.execute(statement, [content, userId])
// 将user存储到数据库中
return result
}
async getMomentById(id) {
const statement = `
SELECT
m.id,m.content,m.createAt,m.updateAt,
JSON_OBJECT('id',u.id,'name',u.name) author,
JSON_ARRAYAGG(JSON_OBJECT('id',c.id,'content',c.content,'createTime',c.createAt,'user',JSON_OBJECT('id',cu.id,'name',cu.name))) comments
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LEFT JOIN comment c ON c.moment_id = m.id
LEFT JOIN users cu ON c.user_id = cu.id
WHERE m.id = ${id};
`
const result = await connection.execute(statement)
// 将user存储到数据库中
return result[0]
}
async getMomentList(offset, size) {
const statement = `
SELECT
m.id id,
m.content content,
m.createAt createAt,
m.updateAt updateAt,
JSON_OBJECT('id',u.id,'name',u.name) author,
(SELECT COUNT(*) FROM comment c WHERE c.moment_id = m.id) commentCount
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LIMIT ?, ?;
`
const result = await connection.execute(statement, [offset, size])
return result[0]
}
async update(content, momentId) {
const statement = `
UPDATE moment SET content = ? WHERE id = ?;
`
const [result] = await connection.execute(statement, [content, momentId])
return result
}
async remove(momentId) {
const statement = `
DELETE FROM moment WHERE id = ?;
`
const [result] = await connection.execute(statement, [momentId])
return result
}
async hasLabel(momentId, labelId){
const statement = `
SELECT * FROM moment_label WHERE moment_id = ? AND label_id = ?;
`
const [result] = await connection.execute(statement,[momentId,labelId])
return result[0] ? true : false
}
async addLabel(momentId, labelId){
const statement = `
INSERT INTO moment_label (moment_id, label_id) VALUES (?, ?);
`
const [result] = await connection.execute(statement,[momentId,labelId])
return result
}
}
module.exports = new MomentService()
第八节:获取标签-获取标签列表接口
- 在router/label.router.js文件中添加标签获取接口
javascript
/*--------------router/label.router.js------------*/
const Router = require('koa-router')
const { verifyAuth } = require('../middleware/auth.middleware')
const { create, list } = require('../controller/label.controller.js')
const labelRouter = new Router({ prefix: '/label' })
labelRouter.post('/', verifyAuth, create)
labelRouter.get('/', list)
module.exports = labelRouter
- 在controller/label.controller.js文件中编写list控制器函数
javascript
/*--------------controller/label.controller.js------------------*/
const service = require('../service/label.service')
class LabelController {
async create(ctx, next) {
const { name } = ctx.request.body
const result = await service.create(name)
ctx.body = result
}
async list(ctx, next) {
const { limit, offset } = ctx.query
const result = await service.getLabels(limit, offset)
ctx.body = result
}
}
module.exports = new LabelController()
- 在service/label.service文件中编写getLabels方法
javascript
/*-------------service/label.serveice.js-----------------*/
const connection = require('../app/database')
class LabelService {
async create(name){
const statement = `
INSERT INTO label (name) VALUES (?);
`
const [result] = await connection.execute(statement,[name])
return result
}
async getLabelByName(name) {
const statement = `
SELECT * FROM label WHERE name = ?;
`
const [result] = await connection.execute(statement, [name])
return result[0]
}
async getLabels(limit, offset){
const statement = `
SELECT * FROM label LIMIT ?, ?;
`
const [result] = await connection.execute(statement,[offset,limit])
return result
}
}
module.exports = new LabelService()
第九节:获取动态-同时获取接口信息
- 获取动态列表接口的sql添加可以展示标签数量的字段
- 在service/moment.service.js文件中把getMomentList中的statement修改为如下的sql查询语句即可
- 也就是添加这一句
(SELECT COUNT(*) FROM moment_label ml WHERE ml.moment_id = m.id) LabelCount
sql
const statement = `
SELECT
m.id id,
m.content content,
m.createAt createAt,
m.updateAt updateAt,
JSON_OBJECT('id',u.id,'name',u.name) author,
(SELECT COUNT(*) FROM comment c WHERE c.moment_id = m.id) commentCount,
(SELECT COUNT(*) FROM moment_label ml WHERE ml.moment_id = m.id) LabelCount
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LIMIT ?, ?;
`
- 获取单个动态时,结果显示labels字段
- 在service/moment.service.js文件中把getMomentById中的statement修改为如下的sql查询语句即可
- 也就是左连接,再左连接获得label数据
- IF的作用是条件判断,类似三元表达式,可以让没有的数据返回一个null而不是返回一大堆null的字段
sql
/*有bug,会重复*/
const statement = `
SELECT
m.id,m.content,m.createAt,m.updateAt,
JSON_OBJECT('id',u.id,'name',u.name) author,
IF(COUNT(c.id),JSON_ARRAYAGG(JSON_OBJECT('id',c.id,'content',c.content,'createTime',c.createAt,'user',JSON_OBJECT('id',cu.id,'name',cu.name))),NULL) comments,
IF(COUNT(l.id),JSON_ARRAYAGG(JSON_OBJECT('id',l.id,'name',l.name)),NULL) labels
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LEFT JOIN comment c ON c.moment_id = m.id
LEFT JOIN users cu ON c.user_id = cu.id
LEFT JOIN moment_label ml ON ml.moment_id = m.id
LEFT JOIN label l ON ml.label_id = l.id
WHERE m.id = ?
GROUP BY m.id;
`
sql
/*没bug,不会重复*/
const statement = `
SELECT
m.id id, m.content content, m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id', u.id, 'name', u.name) author,
IF(COUNT(l.id),JSON_ARRAYAGG(
JSON_OBJECT('id', l.id, 'name', l.name)
),NULL) labels,
(SELECT IF(COUNT(c.id),JSON_ARRAYAGG(
JSON_OBJECT('id', c.id, 'content', c.content, 'commentId', c.comment_id, 'createTime', c.createAt,
'user', JSON_OBJECT('id', cu.id, 'name', cu.name))
),NULL) FROM comment c LEFT JOIN users cu ON c.user_id = cu.id WHERE m.id = c.moment_id) comments
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LEFT JOIN moment_label ml ON m.id = ml.moment_id
LEFT JOIN label l ON ml.label_id = l.id
WHERE m.id = ?
GROUP BY m.id;
`;
第五部分
第一节:上传头像-上传头像的图片处理
- 在router文件夹中创建file.router.js文件,用来做文件上传路由
javascript
/*------------------router/file.router.js------------------*/
const Router = require("koa-router")
const { verifyAuth } = require('../middleware/auth.middleware')
const { avatarHandler } = require('../middleware/file.middleware')
const fileRouter = new Router({ prefix: '/upload' })
fileRouter.post('/avatar', verifyAuth, avatarHandler)
module.exports = fileRouter
- npm-i koa-multer 安装koa文件处理库
- 在middleware文件夹中创建file.middleware.js文件
javascript
/*--------------middleware/file.middleware.js-------------------*/
const Multer = require('koa-multer')
const avatarUpload = Multer({
dest: './uploads/avatar'
})
const avatarHandler = avatarUpload.single('avatar')
module.exports = {
avatarHandler
}
第二节:上传头像-保存头像信息到数据库
- 在controller文件夹中创建file.controller.js文件,创建saveAvatarInfo控制器用于保存图片头像信息
- 创建数据表保存图片信息
sql
CREATE TABLE IF NOT EXISTS avatar (
id INT PRIMARY KEY AUTO_INCREMENT,
filename VARCHAR(100) NOT NULL UNIQUE,
mimetype VARCHAR(30),
size INT,
user_id INT,
createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE
)
- 在router/file.router.js文件中添加saveAvatarInfo控制器方法
javascript
/*------------router/file.router.js----------*/
const Router = require("koa-router")
const { verifyAuth } = require('../middleware/auth.middleware')
const { avatarHandler } = require('../middleware/file.middleware')
const { saveAvatarInfo } = require('../controller/file.controller.js')
const fileRouter = new Router({ prefix: '/upload' })
fileRouter.post('/avatar', verifyAuth, avatarHandler, saveAvatarInfo)
module.exports = fileRouter
- 在在controller文件中创建file.controller.js文件
javascript
/*-------------controller.file.controller.js-------------*/
const fileService = require('../service/file.service.js')
class FileController {
async saveAvatarInfo(ctx, next) {
// 1.获取图像相关信息
const { mimetype, filename, size } = ctx.req.file
const {id} = ctx.user
// 2.将图像信息数据保存到数据库中
const result = await fileService.createAvatar(id, mimetype, filename, size)
// 3.返回结果
ctx.body = result
}
}
module.exports = new FileController()
- 在service文件夹中创建file.service.js文件
javascript
/*------------service/file.service.js----------*/
const connection = require('../app/database')
class FileService {
async createAvatar(userId, mimetype, filename, size) {
const statement = `
INSERT INTO avatar (user_id, filename, mimetype,size) VALUES(?,?,?,?)
`
const [result] = await connection.execute(statement, [userId, filename, mimetype, size])
return result
}
}
module.exports = new FileService()
第三节:上传头像-提供展示图片的接口
- 在router/user.router.js文件中添加查询用户头像路由
javascript
/*-----------------------router/user.router.js----------------------*/
const Router = require('koa-router')
const { create,avatarInfo } = require('../controller/user.controller.js')
const { verifyUser, handlePassword } = require('../middleware/user.middleware')
const userRouter = new Router({ prefix: '/users' })
userRouter.post('/', verifyUser, handlePassword, create)
userRouter.get('/:userId/avatar', avatarInfo)
module.exports = userRouter
- 在controller/user.controller.js文件中创建avatarInfo控制器方法
javascript
/*----------------controller/uesr.controller.js-------------------*/
const fs = require('fs')
const userService = require('../service/user.service')
const fileService = require('../service/file.service')
const { AVATAR_PATH } = require('../constants/file-path')
class userController {
async create(ctx, next) {
// 获取用户请求传递的参数
const user = ctx.request.body
// 查询数据
const result = await userService.create(user)
// 返回数据
ctx.body = fs.createReadStream(`./upload/`)
}
async avatarInfo(ctx, next) {
const { userId } = ctx.params
const avatarInfo = await fileService.getAvatarByUserId(userId)
// 2.提供图像信息
ctx.response.set('content-type', avatarInfo.mimetype)
ctx.body = fs.createReadStream(`${AVATAR_PATH}/${avatarInfo.filename}`)
}
}
module.exports = new userController()
- 在service/file.service文件中创建getAvatarByUserId方法
javascript
const connection = require('../app/database')
class FileService {
async createAvatar(userId, mimetype, filename, size) {
const statement = `
INSERT INTO avatar (user_id, filename, mimetype,size) VALUES(?,?,?,?)
`
const [result] = await connection.execute(statement, [userId, filename, mimetype, size])
return result
}
async getAvatarByUserId(userId){
const statement = `SELECT * FROM avatar WHERE user_id = ?;`
const [result] = await connection.execute(statement,[userId])
return result[0]
}
}
module.exports = new FileService()
- 在constants文件夹中创建file-path.js文件
javascript
/*----------constants/file-path.js------------*/
const AVATAR_PATH = './uploads/avatar'
module.exports = {
AVATAR_PATH
}
- middleware/file.middleware.js文件中也换成常量
javascript
/*-------------------middleware/file.middleware.js-----------*/
const Multer = require('koa-multer')
const { AVATAR_PATH } = require('../constants/file-path')
const avatarUpload = Multer({
dest: AVATAR_PATH
})
const avatarHandler = avatarUpload.single('avatar')
module.exports = {
avatarHandler
}
第四节:上传头像-将头像地址保存到user中
- 用户表添加头像地址字段
sql
ALTER TABLE users ADD avatar_url VARCHAR(200);
- 修改service/moment.service.js文件getMomentById函数的sql语句
sql
const statement = `
SELECT
m.id id, m.content content, m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id', u.id, 'name', u.name, 'avatarUrl', u.avatar_url) author,
IF(COUNT(l.id),JSON_ARRAYAGG(
JSON_OBJECT('id', l.id, 'name', l.name)
),NULL) labels,
(SELECT IF(COUNT(c.id),JSON_ARRAYAGG(
JSON_OBJECT('id', c.id, 'content', c.content, 'commentId', c.comment_id, 'createTime', c.createAt,
'user', JSON_OBJECT('id', cu.id, 'name', cu.name, 'avatarUrl', cu.avatar_url))
),NULL) FROM comment c LEFT JOIN users cu ON c.user_id = cu.id WHERE m.id = c.moment_id) comments
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LEFT JOIN moment_label ml ON m.id = ml.moment_id
LEFT JOIN label l ON ml.label_id = l.id
WHERE m.id = ?
GROUP BY m.id;
`;
- 在根目录.env新建全局变量APP_HOST并在app/config.js文件中导出(代码省略)
javascript
APP_HOST=http://localhost
APP_PORT=8000
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_DATABASE=zcjhub
MYSQL_USER=root
MYSQL_PASSWORD=54088qq
- 修改controller/file.controller.js文件的saveAvatarInfo函数
javascript
/*-------------------controller/file.controller.js-----------------*/
const fileService = require('../service/file.service.js')
const userService = require('../service/user.service')
const {AVATAR_PATH} = require('../constants/file-path')
const { APP_HOST,APP_PORT } = require('../app/config')
class FileController {
async saveAvatarInfo(ctx, next) {
// 1.获取图像相关信息
const { mimetype, filename, size } = ctx.req.file
const {id} = ctx.user
// 2.将图像信息数据保存到数据库中
const result = await fileService.createAvatar(id, mimetype, filename, size)
// 3.将图片地址保存到user表中
const avatarUrl = `${APP_HOST}:${APP_PORT}/users/${id}/avatar`;
await userService.updateAvatarUrlById(avatarUrl, id)
// 4.返回结果
ctx.body = '上传头像成功~'
}
}
module.exports = new FileController()
- 在service/user.service.js文件中添加updateAvatarUrlById方法
javascript
/*------------service/user.service.js-----------------*/
const connection = require('../app/database')
class Userservice {
async create(user) {
const { name, password } = user
const statement = `
INSERT INTO users (name,password) VALUES (?, ?);
`
const result = await connection.execute(statement, [name, password])
// 将user存储到数据库中
return result
}
async getUserByName(name){
const statement = `
SELECT * FROM users WHERE name = ?;
`
const result = await connection.execute(statement,[name])
return result[0]
}
async updateAvatarUrlById(avatarUrl, userId){
const statement = `UPDATE users SET avatar_url = ? WHERE id = ?`
const [result] = await connection.execute(statement,[avatarUrl, userId])
return result
}
}
module.exports = new Userservice()
第五节:动态配图-动态配图的上传处理
- 在router/file.router.js添加上传配图路由
javascript
/*------------------router/file.router.js------------------*/
const Router = require("koa-router")
const { verifyAuth } = require('../middleware/auth.middleware')
const { avatarHandler, pictureHandler } = require('../middleware/file.middleware')
const { saveAvatarInfo, savePictureInfo } = require('../controller/file.controller.js')
const fileRouter = new Router({ prefix: '/upload' })
fileRouter.post('/avatar', verifyAuth, avatarHandler, saveAvatarInfo)
fileRouter.post('/picture', verifyAuth, pictureHandler, savePictureInfo)
module.exports = fileRouter
- 在middleware/file.middleware.js文件中创建pictureHandler中间件函数
javascript
/*---------------middleware/file.middleware.js----------------*/
const Multer = require('koa-multer')
const { AVATAR_PATH, PICTURE_PATH } = require('../constants/file-path')
const avatarUpload = Multer({
dest: AVATAR_PATH
})
const avatarHandler = avatarUpload.single('avatar')
const pictureUpload = Multer({
dest: PICTURE_PATH
})
const pictureHandler = pictureUpload.array('picture', 9)
module.exports = {
avatarHandler,
pictureHandler
}
- 在constants/file-path.js文件中创建常量PICTURE_PATH并导出
javascript
/*-------------constants/file-path.js---------------------*/
const AVATAR_PATH = './uploads/avatar'
const PICTURE_PATH = './uploads/picture'
module.exports = {
AVATAR_PATH,
PICTURE_PATH
}
- 在controller/file.controller.js文件中创建savePictureInfo控制器函数保存图片信息
javascript
/*--------------controller/file.controller.js------------------------*/
const fileService = require('../service/file.service.js')
const userService = require('../service/user.service')
const { AVATAR_PATH } = require('../constants/file-path')
const { APP_HOST, APP_PORT } = require('../app/config')
class FileController {
async saveAvatarInfo(ctx, next) {
// 1.获取图像相关信息
const { mimetype, filename, size } = ctx.req.file
const { id } = ctx.user
// 2.将图像信息数据保存到数据库中
const result = await fileService.createAvatar(id, mimetype, filename, size)
// 3.将图片地址保存到user表中
const avatarUrl = `${APP_HOST}:${APP_PORT}/users/${id}/avatar`;
await userService.updateAvatarUrlById(avatarUrl, id)
// 4.返回结果
ctx.body = '上传头像成功~'
}
async savePictureInfo(ctx, next) {
// 1.获取图像信息
const files = ctx.req.files
const { id } = ctx.user
const { momentId } = ctx.query
// 2.将所有的文件信息保存到数据库中
for (let file of files) {
const { filename, mimetype, size } = file
const result = await fileService.createFile(filename, mimetype, size, id, momentId)
}
ctx.body = '动态配图上传完成~'
}
}
module.exports = new FileController()
- 创建file表保存上传的文件信息(一张或多张配图)
sql
CREATE TABLE IF NOT EXISTS file (
id INT PRIMARY KEY AUTO_INCREMENT,
filename VARCHAR(100) NOT NULL UNIQUE,
mimetype VARCHAR(30),
size INT,
moment_id INT,
user_id INT,
createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE
)
- 在service/file.service.js文件中创建createFile函数
javascript
/*----------------service/file.service.js-----------------*/
const connection = require('../app/database')
class FileService {
async createAvatar(userId, mimetype, filename, size) {
const statement = `
INSERT INTO avatar (user_id, filename, mimetype,size) VALUES(?,?,?,?)
`
const [result] = await connection.execute(statement, [userId, filename, mimetype, size])
return result
}
async getAvatarByUserId(userId) {
const statement = `SELECT * FROM avatar WHERE user_id = ?;`
const [result] = await connection.execute(statement, [userId])
return result[0]
}
async createFile(filename, mimetype, size, userId, momentId) {
const statement = `
INSERT INTO file (user_id, filename, mimetype,size, moment_id) VALUES(?,?,?,?,?);
`
const [result] = await connection.execute(statement, [userId, filename, mimetype, size, momentId])
return result
}
}
module.exports = new FileService()
第六节:动态配图-返回动态配图信息
- 修改service/moment.service.js文件getMomentById函数和getMomentLis的sql,添加配图路径字段
sql
const statement = `
SELECT
m.id id, m.content content, m.createAt createTime, m.updateAt updateTime,
JSON_OBJECT('id', u.id, 'name', u.name, 'avatarUrl', u.avatar_url) author,
IF(COUNT(l.id),JSON_ARRAYAGG(
JSON_OBJECT('id', l.id, 'name', l.name)
),NULL) labels,
(SELECT IF(COUNT(c.id),JSON_ARRAYAGG(
JSON_OBJECT('id', c.id, 'content', c.content, 'commentId', c.comment_id, 'createTime', c.createAt,
'user', JSON_OBJECT('id', cu.id, 'name', cu.name, 'avatarUrl', cu.avatar_url))
),NULL) FROM comment c LEFT JOIN users cu ON c.user_id = cu.id WHERE m.id = c.moment_id) comments,
(SELECT JSON_ARRAYAGG(CONCAT('${APP_HOST}:${APP_PORT}/moment/images/', file.filename)) FROM file WHERE m.id = file.moment_id) images
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LEFT JOIN moment_label ml ON m.id = ml.moment_id
LEFT JOIN label l ON ml.label_id = l.id
WHERE m.id = ?
GROUP BY m.id;
`;
const statement = `
SELECT
m.id id,
m.content content,
m.createAt createAt,
m.updateAt updateAt,
JSON_OBJECT('id',u.id,'name',u.name) author,
(SELECT COUNT(*) FROM comment c WHERE c.moment_id = m.id) commentCount,
(SELECT COUNT(*) FROM moment_label ml WHERE ml.moment_id = m.id) LabelCount,
(SELECT JSON_ARRAYAGG(CONCAT('${APP_HOST}:${APP_PORT}/moment/images/', file.filename)) FROM file WHERE m.id = file.moment_id) images
FROM moment m
LEFT JOIN users u ON m.user_id = u.id
LIMIT ?, ?;
`
- 在router/moment.router.js文件中添加查询配图接口路由
javascript
/*------------------router/moment.router.js------------------*/
const Router = require('koa-router')
const { verifyAuth, verifyPermission } = require('../middleware/auth.middleware')
const { verifyLabelExists } = require('../middleware/label.middleware')
const { create, detail, list, update, remove, addLabels, fileInfo } = require('../controller/moment.controller')
const momentRouter = new Router({ prefix: '/moment' })
momentRouter.post('/', verifyAuth, create)
momentRouter.get('/', list)
momentRouter.get('/:momentId', detail)
// 1.用户必须登录 2.用户具备权限
momentRouter.patch('/:momentId', verifyAuth, verifyPermission, update)
momentRouter.delete('/:momentId', verifyAuth, verifyPermission, remove)
momentRouter.post('/:momentId/labels', verifyAuth, verifyPermission, verifyLabelExists, addLabels)
// 动态配图的服务
momentRouter.get('/images/:filename', fileInfo)
module.exports = momentRouter
- 在controller/moment.controller.js文件中添加fileInfo控制器函数
javascript
/*-------------------controller/moment.controller.js----------------*/
const fs = require('fs')
const momentService = require('../service/moment.service')
const fileService = require('../service/file.service')
const { PICTURE_PATH } = require('../constants/file-path')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId, content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
async list(ctx, next) {
// 1.获取数据
const { offset, size } = ctx.query
// 2.查询列表
const result = await momentService.getMomentList(offset, size)
ctx.body = result
}
async update(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
const { content } = ctx.request.body
// 2.修改内容
const result = await momentService.update(content, momentId)
ctx.body = '修改内容' + momentId + content
}
async remove(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
// 2.删除动态
const result = await momentService.remove(momentId)
ctx.body = '删除动态成功'
}
async addLabels(ctx, next) {
// 1.获取标签和动态id
const { labels } = ctx
const { momentId } = ctx.params
// 2.添加所有的标签
for (let label of labels) {
// 2.1判断标签是否已经和动态有关系
const isExists = await momentService.hasLabel(momentId, label.id)
if (!isExists) {
const result = await momentService.addLabel(momentId, label.id)
}
}
ctx.body = '给动态添加标签成功~'
}
async fileInfo(ctx, next) {
const { filename } = ctx.params
const fileInfo = await fileService.getFileByFilename(filename)
// 2.提供图像信息
ctx.response.set('content-type', fileInfo.mimetype)
ctx.body = fs.createReadStream(`${PICTURE_PATH}/${filename}`)
}
}
module.exports = new MomentController()
- 在service/file.service.js文件中创建getFileByFilename函数
javascript
/*-----------------service/file.service.js---------------------*/
const connection = require('../app/database')
class FileService {
async createAvatar(userId, mimetype, filename, size) {
const statement = `
INSERT INTO avatar (user_id, filename, mimetype,size) VALUES(?,?,?,?)
`
const [result] = await connection.execute(statement, [userId, filename, mimetype, size])
return result
}
async getAvatarByUserId(userId) {
const statement = `SELECT * FROM avatar WHERE user_id = ?;`
const [result] = await connection.execute(statement, [userId])
return result[0]
}
async createFile(filename, mimetype, size, userId, momentId) {
const statement = `
INSERT INTO file (user_id, filename, mimetype,size, moment_id) VALUES(?,?,?,?,?);
`
const [result] = await connection.execute(statement, [userId, filename, mimetype, size, momentId])
return result
}
async getFileByFilename(filename) {
const statement = `SELECT * FROM file WHERE filename = ?;`
const [result] = await connection.execute(statement, [filename])
return result[0]
}
}
module.exports = new FileService()
第七节:动态配图-对配图大小进行处理
- 在router/file.router.js文件中添加图片大小处理中间件pictureResize
javascript
/*--------------------router/file.router.js---------------------*/
const Router = require("koa-router")
const { verifyAuth } = require('../middleware/auth.middleware')
const { avatarHandler, pictureHandler, pictureResize } = require('../middleware/file.middleware')
const { saveAvatarInfo, savePictureInfo } = require('../controller/file.controller.js')
const fileRouter = new Router({ prefix: '/upload' })
fileRouter.post('/avatar', verifyAuth, avatarHandler, saveAvatarInfo)
fileRouter.post('/picture', verifyAuth, pictureHandler, pictureResize, savePictureInfo)
module.exports = fileRouter
- npm i
- 在middleware/file.middleware.js文件中创建pictureResize中间件函数
javascript
/*-----------------------controller/moment.controller.js----------------*/
const path = require('path')
const Multer = require('koa-multer')
const Jimp = require('jimp')
const { AVATAR_PATH, PICTURE_PATH } = require('../constants/file-path')
const avatarUpload = Multer({
dest: AVATAR_PATH
})
const avatarHandler = avatarUpload.single('avatar')
const pictureUpload = Multer({
dest: PICTURE_PATH
})
const pictureHandler = pictureUpload.array('picture', 9)
const pictureResize = async (ctx, next) => {
// 1.获取所有的图像信息
const files = ctx.req.files
// 2.对图像进行处理(sharp/jimp)
for (let file of files) {
console.log(file.destination, file.filename);
const destPath = path.join(file.destination, file.filename)
Jimp.read(file.path).then(image => {
image.resize(1280, Jimp.AUTO).write(`${destPath}-large`)
image.resize(640, Jimp.AUTO).write(`${destPath}-middle`)
image.resize(320, Jimp.AUTO).write(`${destPath}-small`)
})
}
await next()
}
module.exports = {
avatarHandler,
pictureHandler,
pictureResize
}
- fileInfo函数
javascript
/*----------------controller/moment.controller.js----------------*/
const fs = require('fs')
const momentService = require('../service/moment.service')
const fileService = require('../service/file.service')
const { PICTURE_PATH } = require('../constants/file-path')
class MomentController {
async create(ctx, next) {
// 1.获取数据(user_id, content)
const userId = ctx.user.id
const content = ctx.request.body.content
// 2.将数据插入到数据库
const result = await momentService.create(userId, content)
ctx.body = result
}
async detail(ctx, next) {
// 1.获取数据(momentId)
const momentId = ctx.params.momentId
// 2.根据id去查询这条数据
const result = await momentService.getMomentById(momentId)
ctx.body = result
}
async list(ctx, next) {
// 1.获取数据
const { offset, size } = ctx.query
// 2.查询列表
const result = await momentService.getMomentList(offset, size)
ctx.body = result
}
async update(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
const { content } = ctx.request.body
// 2.修改内容
const result = await momentService.update(content, momentId)
ctx.body = '修改内容' + momentId + content
}
async remove(ctx, next) {
// 1.获取参数
const { momentId } = ctx.params
// 2.删除动态
const result = await momentService.remove(momentId)
ctx.body = '删除动态成功'
}
async addLabels(ctx, next) {
// 1.获取标签和动态id
const { labels } = ctx
const { momentId } = ctx.params
// 2.添加所有的标签
for (let label of labels) {
// 2.1判断标签是否已经和动态有关系
const isExists = await momentService.hasLabel(momentId, label.id)
if (!isExists) {
const result = await momentService.addLabel(momentId, label.id)
}
}
ctx.body = '给动态添加标签成功~'
}
async fileInfo(ctx, next) {
let { filename } = ctx.params
const fileInfo = await fileService.getFileByFilename(filename)
const { type } = ctx.query
const types = ['small', 'middle', 'large']
if (types.some(item => item === type)) {
filename = filename + '-' + type
console.log(filename);
}
// 2.提供图像信息
ctx.response.set('content-type', fileInfo.mimetype)
ctx.body = fs.createReadStream(`${PICTURE_PATH}/${filename}`)
}
}
module.exports = new MomentController()