美文网首页
Section-12 项目实战之问题模块 —— 复杂的数据库设计

Section-12 项目实战之问题模块 —— 复杂的数据库设计

作者: 羽晞yose | 来源:发表于2019-10-25 14:07 被阅读0次

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;
}

相关文章

网友评论

      本文标题:Section-12 项目实战之问题模块 —— 复杂的数据库设计

      本文链接:https://www.haomeiwen.com/subject/gwhvyctx.html