Node.js<二十>——项目实战-动态模块

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

该模块主要的功能其实就是简单的CRUD,发布动态、删除动态、查看动态详情,查询动态列表,编辑动态,该模块是紧接着登录模块来做的,很多没有改动的文件和代码这里不重复介绍,只去介绍一些新加的代码和逻辑

  1. router/dynamic.js

既然要开发新的模块,那肯定需要先创建对应的路由文件,利用路由对象上的方法为各种功能注册中间件,比如说发布动态、查看动态详情、查看动态列表、删除动态、更新动态等等

verifyAuth这个中间件我们不做过多强调,因为这是在登录那里介绍过的,主要做的就是检查用户携带的token是否有效,从而决定其有没有权限进入到下一个中间件中

const Router = require('koa-router')

const {
  verifyAuth,
  verifyPermission
} = require('../middleware/auth.middleware')
const {
  create,
  detail,
  list,
  remove,
  update
} = require('../controller/dynamic.controller')

const dynamicRouter = new Router({ prefix: '/dynamics' })

// 添加动态
dynamicRouter.post('/', verifyAuth, create)
// 获取动态详情
dynamicRouter.get('/:dynamicId', detail)
// 获取动态列表
dynamicRouter.get('/', list)
// 删除动态
dynamicRouter.delete('/:dynamicId', verifyAuth, verifyPermission, remove)
// 更新动态内容
dynamicRouter.patch('/:dynamicId', verifyAuth, verifyPermission, update)

module.exports = dynamicRouter
复制代码
  1. controller/dynamic.controller.js

这个文件包含了动态模块所对应的CRUD操作,,他们就是被注册到了router中的中间件,虽然是四个完全不同的功能,但是处理逻辑非常相似,都是需要先获取到客户端传递过来的数据,然后将数据传入到对应的函数中对数据库进行查询、删除等操作后,再响应对应的信息给客户端

const dynamicService = require('../service/dynamic.service')

class DynamicController {
  // 发布动态
  async create(ctx, next) {
    // 1. 获取用户信息及发表的内容
    const { id } = ctx.user
    const { content } = ctx.request.body
    // 2. 将数据插入到数据库
    const results = await dynamicService.create(id, content)
    ctx.body = results
  }

  // 查询动态详情
  async detail(ctx, next) {
    // 1. 获取动态id
    const { dynamicId } = ctx.params
    // 2. 去数据库中根据id查找数据
    const results = await dynamicService.getDetail(dynamicId)
    ctx.body = results
  }
  
  // 查询动态列表
  async list(ctx, next) {
    // 1. 获取到客户端传递过来的分页信息
    const { offset, limit } = ctx.query
    // 2. 去数据库中根据分页信息截取数据列表
    const results = await dynamicService.getList(offset, limit)
    ctx.body = results
  }
  
  // 删除动态
  async remove(ctx, next) {
    // 1. 获取到客户端传递过来的动态id
    const { dynamicId } = ctx.params
    // 2. 根据动态id去数据库中删除对应动态
    const results = await dynamicService.remove(dynamicId)
    ctx.body = results
  }

  // 更新动态
  async update(ctx, next) {
    // 1. 获取到客户端传递过来的动态id和要修改的内容
    const { dynamicId } = ctx.params
    const { content } = ctx.request.body
    // 2. 去数据库中找到对应的动态id并修改为客户端传递过来的内容
    const results = await dynamicService.update(content, dynamicId)
    ctx.body = results
  }
}

module.exports = new DynamicController()
复制代码
  1. service/dynamic.service.js

刚刚介绍的controller/dynamic.controller.js文件只是调用了操作数据库的函数,而这些函数就存放在service文件夹中,在这个文件中书写了不同的函数,每个函数都会有对应的预编译语句用于从数据库中执行不同的操作,有的是查询,有的是添加、删除等等,总而言之,他们就是将对数据库查询或操作后的结果返回出去

const connection = require('../app/database')

// 提取sql语句的公共部分出来简化书写
const sqlFragment = `
  SELECT d.id id, d.content content, d.createAt createTime, d.updateAt updateTime, 
  JSON_OBJECT('id',u.id,'name', u.name) user 
  FROM dynamic d
  LEFT JOIN user u
  ON d.user_id = u.id
`

class DynamicService {
  // 发布动态
  async create(user_id, content) {
    const statement = `INSERT INTO dynamic (user_id, content) VALUES (?, ?);`
    const [results] = await connection.execute(statement, [user_id, content])
    return results
  }

  // 查询动态详情
  async getDetail(dynamicId) {
    const statement = `
      ${sqlFragment}
      WHERE d.id = ?;
    `
    const [results] = await connection.execute(statement, [dynamicId])
    return results[0]
  }

  // 查询动态列表
  async getList(offset, limit) {
    const statement = `
      ${sqlFragment}
      LIMIT ?, ?;
    `
    const [results] = await connection.execute(statement, [offset, limit])
    return results
  }

  // 删除动态
  async remove(dynamicId) {
    const statement = `DELETE FROM dynamic WHERE id =?;`
    const [results] = await connection.execute(statement, [dynamicId])
    return results
  }

  // 更新动态
  async update(content, dynamicId) {
    const statement = `UPDATE dynamic SET content = ? WHERE id = ?;`
    const [results] = await connection.execute(statement, [content, dynamicId])
    return results
  }
}

module.exports = new DynamicService()
复制代码
  1. middleware/auth.middleware.js

如果你仔细观察了route中的代码,就会发现我们并不是直接就让用户删除和修改数据的,要不然就可能会导致用户a删除或修改了新用户b发表的动态,这完全是不合理的

所以我们在真正让用户执行删除或修改动态的中间件之前,需要先执行一个权限验证的中间件verifyPermission,其主要是检查用户操作的目标动态存不存在以及其操作的是不是自己发表的动态

const errTypes = require('../constants/err-types')
const { checkDynamic } = require('../service/auth.service')

const verifyPermission = async (ctx, next) => {
  // 获取用户传递的用户id和操作的动态id
  const { id } = ctx.user
  const { dynamicId } = ctx.params
  // checkDynamic函数通过从数据库中查询数据来判断用户是否具有对应的操作权限,其返回值是一个数字码
  const permissionCode = await checkDynamic(id, dynamicId)
  // 返回值为-1表示用户操作的状态不存在
  if (permissionCode === -1) {
    const error = new Error(errTypes.TARGET_IS_NOT_EXISTS)
    return ctx.app.emit('error', error, ctx)
  // 返回值为0表示用户操作的动态并不是它本人发表的,其没有权限进行操作
  } else if (!permissionCode) {
    const error = new Error(errTypes.UN_PERMISSION)
    return ctx.app.emit('error', error, ctx)
  }
  // 代码执行到这里,说明用户是有操作对应动态的权限的,执行下一个操作动态的中间件即可
  await next()
}

module.exports = {
  verifyPermission
}
复制代码
  1. app/err-handle.js

错误处理这里比以前多了两个场景,一个是用户没有操作权限,比如说用户1操作了用户2发表的动态;另一个是用户操作的数据不存在

const {
  NAME_OR_PWD_IS_REQUIRED,
  USER_ALREADY_EXISTS,
  USER_IS_NOT_EXISTS,
  PASSWORD_IS_INCORRECT,
  NO_AUTHORIZATION,
  TOKEN_IS_NEED,
  UN_PERMISSION,
  TARGET_IS_NOT_EXISTS
} = require('../constants/err-types')

const errorHandler = (error, ctx) => {
  let status, message
  switch (error.message) {
    case NAME_OR_PWD_IS_REQUIRED:
      status = 400 // Bad Request
      message = '用户名或密码不能为空~'
      break
    case USER_ALREADY_EXISTS:
      status = 409 // Conflict
      message = '用户名已经存在~'
      break
    case USER_IS_NOT_EXISTS:
      status = 400 // Bad Request
      message = '用户名不存在~'
      break
    case PASSWORD_IS_INCORRECT:
      status = 400 // Bad Request
      message = '密码错误~'
      break
    case NO_AUTHORIZATION:
      status = 401 // no_authorization
      message = 'token失效,请重新登录~'
      break
    case TOKEN_IS_NEED:
      status = 401 // no_authorization
      message = '该请求必须携带token~'
      break
    case UN_PERMISSION:
      status = 403 // Forbidden 
      message = '没有该操作的权限~'
      break
    case TARGET_IS_NOT_EXISTS:
      status = 400 // Bad Request
      message = '目标动态不存在~'
      break
    default:
      status = 404
      message = 'not found'
  }

  ctx.status = status
  ctx.body = message
}

module.exports = errorHandler
复制代码

至此,项目的动态管理模块就已经完成了!

猜你喜欢

转载自juejin.im/post/7107075908594302984
今日推荐