node的项目实战相关-2-前台接口

2.首页、分类、课程接口

章节接口

js 复制代码
// 新建router/chapters.js
// 完成后app中引入
const express = require("express")
const router = express.Router()
const { Course, Category, Chapter, User } = require("../models")
const { success, failure } = require("../utils/responses")

// 章节的内容
// 章节详情接口
// 注意:章节属于一个课程,课程属于一个用户,章节和用户没有直接关系,∴在include中嵌套include,先通过章节查课程,再通过课程查用户

router.get("/:id", async (req, res) => {
  try {
    const { id } = req.params
    const condition = {
      attributes: {
        exclude: ["CourseId"],
      },
      include: [
        {
          model: Course,
          as: "course",
          attributes: {
            exclude: ["id", "name"],
          },
          include: [
            {
              model: User,
              as: "user",
              attributes: {
                exclude: ["id", "username", "nickname", "avatar", "company"],
              },
            },
          ],
        },
      ],
    }
    const chapter = await Chapter.findByPk(id, condition)
    if (!chapter) {
      throw new Error("chapter not found,章节未找到")
    }

    // 同属一个课程的所有章节
    const chapters = await Chapter.findAll({
      attributes: {
        exclude: ["CourseId", "content"],
      },
      where: {
        courseId: chapter.courseId,
      },
      order: [
        ["rank", "DESC"],
        ["id", "DESC"],
      ],
    })
    success(
      res,
      {
        chapter,
        chapters,
      },
      "查询章节成功"
    )
  } catch (error) {
    failure(res, error)
  }
})

module.exports = router

文章列表接口

js 复制代码
// 新建router/articles.js
// 完成后app中引入
const express = require("express")
const router = express.Router()
const { Article } = require("../models")
const { success, failure } = require("../utils/responses")

// #region 查询文章列表的接口
router.get("/", async (req, res) => {
  try {
    // 分页
    const query = req.query
    const currentPage = Math.abs(query.currentPage) || 1
    const pageSize = Math.abs(query.pageSize) || 10
    const offset = (currentPage - 1) * pageSize
    const condition = {
      attributes: {
        exclude: ["content"],
      },
      order: [["id", "DESC"]],
      limit: pageSize,
      offset,
    }
    const { count, rows } = await Article.findAndCountAll(condition)

    success(
      res,
      {
        articles: rows,
        pagination: {
          total: count,
          currentPage,
          pageSize,
        },
      },
      "查询文章列表成功"
    )
  } catch (error) {
    failure(res, error)
  }
})

// #region 文章详情接口
router.get("/:id", async (req, res) => {
  try {
    const id = req.params.id
    const article = await Article.findByPk(id)
    if (!article) {
      return failure(res, "文章不存在")
    }
    success(res, { article }, "查询文章详情成功")
  } catch (error) {
    failure(res, error)
  }
})

module.exports = router

系统设置接口

js 复制代码
// 新建router/settings.js
// 完成后app中引入
const express = require("express")
const router = express.Router()
const { Setting } = require("../models")
const { success, failure } = require("../utils/responses")

// #region 设置接口
router.get("/", async (req, res) => {
  try {
    const setting = await Setting.findOne()
    if (!setting) {
      return failure(res, "设置不存在")
    }
    success(res, { setting }, "查询设置成功")
  } catch (error) {
    failure(res, error)
  }
})

module.exports = router

搜索接口

js 复制代码
// 新建router/serach.js 
// 完成后app中引入
const express = require("express")
const router = express.Router()
const { Course } = require("../models")
const { success, failure } = require("../utils/responses")
const { Op } = require("sequelize")

// #region 搜索接口
router.get("/", async (req, res) => {
  try {
    const query = req.query
    const currentPage = Math.abs(query.page) || 1
    const pageSize = Math.abs(query.pageSize) || 10
    const offset = (currentPage - 1) * pageSize
    const keyword = query.keyword || ""

    const condition = {
      attributes: {
        exclude: ["categoryId", "UserId", "content"],
      },
      order: [["id", "DESC"]],
      limit: pageSize,
      offset,
    }

    if (query.name) {
      condition.where = {
        name: {
          [Op.like]: `%${query.name}%`,
          // 模糊查询
        },
      }
    }

    const { count, rows } = await Course.findAndCountAll(condition)

    success(
      res,
      {
        courses: rows,
        pagination: {
          total: count,
          currentPage,
          pageSize,
        },
      },
      "搜索课程成功。"
    )
  } catch (error) {
    failure(res, error)
  }
})
module.exports = router

app.js中引入

js 复制代码
const chaptersRouter = require("./routes/chapters") // 章节路由
const articlesRouter = require("./routes/articles") // 文章路由
const settingsRouter = require("./routes/settings") // 系统设置路由
const searchRouter = require("./routes/search") // 搜索路由

app.use("/chapters", chaptersRouter) // 章节路由
app.use("/articles", articlesRouter) // 文章路由
app.use("/settings", settingsRouter) // 系统设置路由
app.use("/search", searchRouter) // 搜索路由

3.用户注册、登录、认证接口

用户注册,隐藏密码

js 复制代码
// 新建一个routes/auth.js文件

const express = require("express")
const router = express.Router()
const { User } = require("../models")
const { success, failure } = require("../utils/responses")
const { BadRequestError, UnauthorizedError, NotFoundError } = require("../utils/error")
const bcrypt = require("bcryptjs")
const jwt = require("jsonwebtoken")
const { Op } = require("sequelize")

/**
 * 用户注册
 */
// 注1:
// sex:2为未选择
// role:0为普通用户

// 注2:
// 注册成功后,密码也返回了,要把密码字段删除掉
router.post("/sign_up", async (req, res) => {
  try {
    const { username, password, nickname, email } = req.body
    const body = { username, password, nickname, email, sex: 2, role: 0 }
    const user = await User.create(body)

    // 删除密码
    delete user.dataValues.password
    success(res, { user }, "注册成功")
  } catch (error) {
    failure(res, error)
  }
})
module.exports = router

用户登录

js 复制代码
// route/auth.js中
/**
 * 用户登录
 */
// 注1:bcrypt验证密码
// 注2:生成token
// 注3:apifox-后置操作添加userToken
// 注4:认证中间件
router.post("/sign_in", async (req, res) => {
  try {
    const { login, password } = req.body
    if (!login) {
      throw new BadRequestError("邮箱或用户名不能为空")
    }
    if (!password) {
      throw new BadRequestError("密码不能为空")
    }

    const condition = {
      where: {
        [Op.or]: [{ username: login }, { email: login }],
      },
    }

    const user = await User.findOne(condition)
    if (!user) {
      throw new NotFoundError("用户不存在")
    }
    // 验证密码
    const isPasswordValid = await bcrypt.compare(password, user.password)
    if (!isPasswordValid) {
      throw new UnauthorizedError("密码错误")
    }
    // 生成token
    const token = jwt.sign({ userId: user.id }, process.env.SECRET, {
      expiresIn: "30d",
    })

    success(res, { token }, "登录成功")
  } catch (error) {
    failure(res, error)
  }
})

用户认证中间件

js 复制代码
// 复制粘贴middlewares/admin-auth.js,改为user-auth.js
const jwt = require("jsonwebtoken")
const { User } = require("../models")
const { UnauthorizedError } = require("../utils/error")
const { success, failure } = require("../utils/responses")

module.exports = async (req, res, next) => {
  try {
    // 判断 Token 是否存在
    const { token } = req.headers
    if (!token) {
      throw new UnauthorizedError("当前接口需要认证才能访问。")
    }
    // 验证 token 是否正确
    const decoded = jwt.verify(token, process.env.SECRET)
    const { userId } = decoded
    // 如果通过验证,将 user 对象挂载到 req 上,方便后续中间件或路由使用
    req.userId = userId
    // 一定要加 next(),才能继续进入到后续中间件或路由
    next()
  } catch (error) {
    failure(res, error)
  }
}

4.用户相关的接口

用户首页,使用中间件

js 复制代码
// 修改自带的route/users.js:
const express = require("express")
const router = express.Router()
const { User } = require("../models")
const { success, failure } = require("../utils/responses")
const { NotFoundError, BadRequestError } = require("../utils/error")
const bcrypt = require("bcryptjs")

// #region 用户首页,当前用户的信息
router.get("/me", async (req, res) => {
  try {
    const user = await getUser(req)
    success(res, { user }, "用户信息查询成功!")
  } catch (error) {
    failure(res, error)
  }
})
/**
 * 公共方法:查询当前用户
 */
 // 注:返回值中要不显示密码
async function getUser(req, showPassword = false) {
  const id = req.userId
  let condition = {}
  if (!showPassword) {
    condition = {
      attributes: {
        exclude: ["password"], // 不显示密码
      },
    }
  }
  const user = await User.findByPk(id, condition)
  if (!user) {
    throw new NotFoundError("用户不存在")
  }
  return user
}
module.exports = router
js 复制代码
// app.js中引入路由和中间件
// 前台-中间件
const userAuth = require("./middlewares/user-auth")

const usersRouter = require("./routes/users") // 用户路由

app.use("/users", userAuth, usersRouter) // 用户路由

更新用户信息:性别\公司\昵称等

js 复制代码
// users.js
/**
 * 用户的信息修改
 */
router.put("/info", async (req, res) => {
  try {
    const { nickname, sex, company, introduce, avatar } = req.body

    const body = {
      nickname,
      sex,
      company,
      introduce,
      avatar,
    }

    const user = await getUser(req)
    await user.update(body)
    success(res, { user }, "更新用户成功!")
  } catch (error) {
    failure(res, error)
  }
})

更新账户信息:邮箱\用户名\邮箱

js 复制代码
// route/users.js
/**
 * 更新账户信息
 * 修改密码等
 */
router.put("/account", async (req, res) => {
  try {
    const { email, username, password, currentPassword, passwordConfirmation } = req.body

    const body = {
      email,
      username,
      password,
      currentPassword,
      passwordConfirmation,
    }

    if (!currentPassword) {
      throw new BadRequestError("请输入当前密码")
    }
    if (password !== passwordConfirmation) {
      throw new BadRequestError("两次输入密码不一致")
    }
    const user = await getUser(req, true) // 这里需要拿到密码作比对
    const isPasswordValid = bcrypt.compareSync(currentPassword, user.password)
    if (!isPasswordValid) {
      throw new BadRequestError("当前密码输入错误")
    }
    await user.update(body)
    delete user.dataValues.password
    success(res, { user }, "更新账户成功!")
  } catch (error) {
    failure(res, error)
  }
})

5.点赞

点赞/取消点赞

js 复制代码
// 新建route/likes.js
const express = require("express")
const router = express.Router()
const { User, Course, Like } = require("../models")
const { success, failure } = require("../utils/responses")
const { NotFoundError, BadRequestError } = require("../utils/error")
const bcrypt = require("bcryptjs")
const { or } = require("sequelize")

/**
 * 点赞
 */
// 注1:
// increment/decrement:自增/自减操作
router.post("/", async (req, res) => {
  try {
    // req.userId是当前对应的点赞的用户
    const userId = req.userId
    const { courseId } = req.body
    const course = await Course.findByPk(courseId)
    if (!course) {
      throw new NotFoundError("课程不存在")
    }
    const like = await Like.findOne({
      where: { userId, courseId },
    })
    // 没有点赞就新增,并且课程的likesCount + 1
    if (!like) {
      await Like.create({
        userId,
        courseId,
      })
      await course.increment("likesCount")
      success(res, {}, "点赞成功")
    } else {
      // 有点赞就删除,并且课程的likesCount - 1
      await like.destroy()
      await course.decrement("likesCount")
      success(res, {}, "取消点赞成功")
    }
  } catch (error) {
    failure(res, error)
  }
})
module.exports = router

查询当前用户点赞的课程

js 复制代码
 
// course模型中:
   class Course extends Model {
    static associate(models) {
      // ...
      models.Course.belongsToMany(models.User, { through: models.Like, foreignKey: "courseId", as: "likeUsers" }) // 多对多关联,through指通过Likes中间表来实现关联
    }
  }

// user模型中:
  class User extends Model {
    static associate(models) {
      // ...
      models.User.belongsToMany(models.Course, { through: models.Like, foreignKey: "userId", as: "likeCourses" })
    }
  }

// route/likes.js
/**
 * 查询当前用户点赞的课程
 *
 * 多对多关系
 * 每个课程有多个点赞
 * 每个用户可以点赞多个课程
 *
 * 新东西:getLikeCourses获取likeCourses数据
 *
 * 新东西:countLikeCourses获取likeCourses的总和
 *
 * 查询到的结果分页处理
 *
 * 新东西:去除掉中间表Like数据joinTableAttributes: [], 
 */
router.get("/", async (req, res) => {
  try {
    const query = req.query
    const currentPage = query.currentPage || 1
    const pageSize = query.pageSize || 10
    const offset = (currentPage - 1) * pageSize
    const user = await User.findByPk(req.userId)

    // 新东西:getLikeCourses获取likeCourses数据
    const courses = await user.getLikeCourses({
      joinTableAttributes: [], // 去除掉中间表数据
      attributes: { exclude: ["CategoryId", "userId", "content"] },
      offset,
      limit: pageSize,
      order: [["id", "DESC"]],
    })
    // 新东西:countLikeCourses获取likeCourses的总和
    const count = await user.countLikeCourses()
    success(res, { courses, pagination: { currentPage, pageSize, total: count } }, "查询成功")
  } catch (error) {
    failure(res, error)
  }
})

get的妙用

js 复制代码
// 回到章节详情接口chapters.js
const express = require("express")
const router = express.Router()
const { Course, Category, Chapter, User } = require("../models")
const { success, failure } = require("../utils/responses")

// 章节的内容
// 章节详情接口
// 注意:章节属于一个课程,课程属于一个用户,章节和用户没有直接关系,∴在include中嵌套include,先通过章节查课程,再通过课程查用户

router.get("/:id", async (req, res) => {
  try {
    const { id } = req.params
    // const condition = {
    //   attributes: { exclude: ["CourseId"] },
    //   include: [
    //     {
    //       model: Course,
    //       as: "course",
    //       attributes: {
    //         exclude: ["id", "name"],
    //       },
    //       include: [
    //         {
    //           model: User,
    //           as: "user",
    //           attributes: {
    //             exclude: ["id", "username", "nickname", "avatar", "company"],
    //           },
    //         },
    //       ],
    //     },
    //   ],
    // }
    // const chapter = await Chapter.findByPk(id, condition)

    /**
     * 写法二:get的妙用
     * 1. 可以通过getCource获取课程
     * 2. 可以通过getUser获取用户
     * 这样写也避免了condition写法的嵌套
     */
    const chapter = await Chapter.findByPk(id, {
      attributes: { exclude: ["CourseId"] },
    })
    if (!chapter) {
      throw new Error("chapter not found,章节未找到")
    }
    // 查询章节关联的课程
    const course = await Chapter.getCource({
      attributes: {
        exclude: ["id", "name", "userId"],
      },
    })
    // 查询章节相关的用户
    const user = await Course.getUser({
      attributes: {
        exclude: ["id", "username", "nickname", "avatar", "company"],
      },
    })

    // 同属一个课程的所有章节
    const chapters = await Chapter.findAll({
      attributes: {
        exclude: ["CourseId", "content"],
      },
      where: {
        courseId: chapter.courseId,
      },
      order: [
        ["rank", "DESC"],
        ["id", "DESC"],
      ],
    })
    success(
      res,
      {
        chapter,
        chapters,
        course,
        user,
      },
      "查询章节成功"
    )
  } catch (error) {
    failure(res, error)
  }
})

module.exports = router
相关推荐
一斤代码5 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子5 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年5 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子5 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina6 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路6 小时前
React--Fiber 架构
前端·react.js·架构
伍哥的传说7 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409197 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app
我在北京coding7 小时前
element el-table渲染二维对象数组
前端·javascript·vue.js
布兰妮甜7 小时前
Vue+ElementUI聊天室开发指南
前端·javascript·vue.js·elementui