Lesson-1 问题模块需求分析
问题模块功能的
- 问题的增删改查
- 用户的问题列表(用户 -- 问题一对多关系)
- 话题的问题列表 + 问题的话题列表(话题 -- 问题多对多关系)
- 关注/取消关注问题(重复功能,不做开发,自行举一反三)
Lesson-2 用户 -- 问题一对多关系设计与实现
操作步骤
- 实现增删改查接口
- 实现用户的问题列表接口
实现增删改查接口
// // models/questions.js
const mongoose = require('mongoose');
const { Schema, model } = mongoose;
const questionsSchema = new Schema({
__v: { type: Number, select: false },
title: { type: String, required: true },
description: { type: String },
questioner: { type: Schema.Types.ObjectId, ref: 'User', required: true, select: false }
});
module.exports = model('Question', questionsSchema);
// controllers/questions.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;
const perPage = Math.max(+ctx.query.per_page, 1);
const q = new RegExp(ctx.query.q)
ctx.body = await Question.find( { $or: [{ title: q }, { description: q }] }).limit(perPage).skip(page * perPage); // limit: 返回多少数量,skip:跳过多少数量
}
async findById (ctx) {
const { fields = '' } = ctx.query;
const selectFields = fields.split(';').filter(item => item).map(item => ' +'+item).join('');
const question = await Question.findById(ctx.params.id).select(selectFields).populate('questioner');
if(!question) ctx.throw(404, '问题不存在');
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 update (ctx) {
ctx.verifyParams({
title: { type: 'string', required: false },
description: { type: 'string', require: false }
});
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; // 没有内容,但是成功了
}
async checkQuestionExist (ctx, next) {
const question = await Question.findById(ctx.params.id).select('+questioner');
if(!question) ctx.throw(404, '问题不存在');
ctx.state.question = question;
await next();
}
async checkQuestioner (ctx, next) {
const { question } = ctx.state;
if (question.questioner.toString() !== ctx.state.user._id) ctx.throw(403, '没有权限');
await next();
}
}
module.exports = new QuestionsCtl();
// routes/questions.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;
实现用户的问题列表接口
// controllers/users.js
async listQuestions (ctx) {
const questions = await Question.find({ questioner: ctx.params.id });
ctx.body = questions;
}
// routes/users.js
const { listQuestions } = require('../controllers/users'); // 新增listQuestions,原来的就不写进来了,太长了
// 获取问题列表
router.get('/:id/questions', checkUserExist, listQuestions)
Lesson-3 话题 -- 问题多对多关系设计与实现
主要说一下,这个问题必须设计为问题从属topics来做,因为一个问题最多只从属于某N个话题(一般不会超过10个),但反过来,一个话题下可以有无数个问题
获取问题从属话题范围
// models/questions.js
const questionsSchema = new Schema({
__v: { type: Number, select: false },
title: { type: String, required: true },
description: { type: String },
questioner: { type: Schema.Types.ObjectId, ref: 'User', required: true, select: false },
// 新增topics,需要自己从修改特定问题接口将数据自行加上测试
topics: {
type: [{type: Schema.Types.ObjectId, ref: 'Topic' }],
select: false
}
});
// controllers/questions.js
async findById (ctx) {
const { fields = '' } = ctx.query;
const selectFields = fields.split(';').filter(item => item).map(item => ' +'+item).join('');
// 新增topics填充
const question = await Question.findById(ctx.params.id).select(selectFields).populate('questioner topics');
if(!question) ctx.throw(404, '问题不存在');
ctx.body = question;
}
获取特定话题问题列表
// controllers/topics.js
async listQuestions (ctx) {
const questions = await Question.find({ topics: ctx.params.id });
ctx.body = questions;
}













网友评论