# koa实现知乎问题模块接口

需求分析:

  • 问题的增删改查
  • 用户的问题列表(用户-问题:一对多关系)
  • 话题的问题列表 + 问题的话题列表(话题-问题:多对多关系)

/models/question.js












 









const mongoose = require('mongoose');

const { Schema, model } = mongoose;

const questionSchema = new Schema({
  // 默认都会有
  __v: { type: Number, select: false },
  title: { type: String, required: true },
  description: { type: String },
  // 一个问题对应了一个提问者,这就已经实现了一对多的关系
  // 提问者属于User模型里的,所以他是一个 Schema type
  questioner: { type: Schema.Types.ObjectId, ref: 'User', required: true, select: false },
  // Topic模型
  topics: {
    type: [{ type: Schema.Types.ObjectId, ref: 'Topic' }],
    select: false,
  },
}, { timestamps: true });

module.exports = model('Question', questionSchema);

/controllers/question.js










 



















































const Question = require('../models/questions');

class QuestionsCtl {
  async find(ctx) {
    const { per_page = 10 } = ctx.query;
    const page = Math.max(ctx.query.page * 1, 1) - 1;
    const perPage = Math.max(per_page * 1, 1);
    
    const q = new RegExp(ctx.query.q);
    ctx.body = await Question
      .find({ $or: [{ title: q }, { description: q }] })
      .limit(perPage).skip(page * perPage);
  }
  async checkQuestionExist(ctx, next) {
    const question = await Question.findById(ctx.params.id).select('+questioner');
    if (!question) { ctx.throw(404, '问题不存在'); }
    // ctx.state 是给中间件保存数据的,ctx.body 是最终的输出
    ctx.state.question = question;
    await next();
  }
  async findById(ctx) {
    const { fields = '' } = ctx.query;
    const selectFields = fields.split(';').filter(f => f).map(f => ' +' + f).join('');
    const question = await Question.findById(ctx.params.id).select(selectFields).populate('questioner topics');
    ctx.body = question;
  }
  async create(ctx) {
    ctx.verifyParams({
      title: { type: 'string', required: true },
      description: { type: 'string', required: false },
    });
    const question = await new Question({ ...ctx.request.body, questioner: ctx.state.user._id }).save();
    ctx.body = question;
  }
  // 对问题的修改和删除,需要鉴权
  async checkQuestioner(ctx, next) {
    const { question } = ctx.state;
    if (question.questioner.toString() !== ctx.state.user._id) { ctx.throw(403, '没有权限'); }
    await next();
  }

  async update(ctx) {
    ctx.verifyParams({
      title: { type: 'string', required: false },
      description: { type: 'string', required: false },
    });
    // 这里就不用再查一遍question是否存在了
    await ctx.state.question.update(ctx.request.body);
    ctx.body = ctx.state.question;
  }
  /**
   * 删除问题
  */
  async delete(ctx) {
    await Question.findByIdAndRemove(ctx.params.id);
    ctx.status = 204;
  }
}

module.exports = new QuestionsCtl();

提示

ctx.state 是给中间件保存数据的,ctx.body 是最终的输出

/routes/question.js

const jwt = require('koa-jwt');
const Router = require('koa-router');
const router = new Router({ prefix: '/questions' });
const {
  find, findById, create, update, delete: del,
  checkQuestionExist, checkQuestioner,
} = require('../controllers/questions');

const { secret } = require('../config');

const auth = jwt({ secret });

router.get('/', find);
router.post('/', auth, create);
router.get('/:id', checkQuestionExist, findById);
router.patch('/:id', auth, checkQuestionExist, checkQuestioner, update);
router.delete('/:id', auth, checkQuestionExist, checkQuestioner, del);

module.exports = router;