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
相关推荐
To_OC4 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic4 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy4 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志5 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble6 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯7 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜7 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司8 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g8 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森8 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http