美文网首页Nodejs
Section-10 项目实战之关注与粉丝模块

Section-10 项目实战之关注与粉丝模块

作者: 羽晞yose | 来源:发表于2019-10-08 22:47 被阅读0次

    Lesson-1 关注与粉丝需求分析

    细化关注与粉丝功能点

    • 关注、取消关注
    • 获取关注人、粉丝列表(用户-用户多对多关系)

    Lesson-2 关注与粉丝的 schema 设计

    操作步骤

    • 分析关注与粉丝的数据结构
    • 设计关注与粉丝 schema

    分析关注与粉丝的数据结构

    因为 mongoose 有限制,假设用户是一个大V,拥有百万级粉丝,那么对应用户某个属性中存储一百万粉丝是不合理,因为粉丝字段将超过 4M ,在 mongoose 中会认为是设计不合理的,所以不能直接把粉丝做在 Schema 中,但是关注可以,因为每个用户最多只能关注一千人,这个数量并没多少,而粉丝只需要查看一下所有用户的关注者里是否有关注该用户即可

    设计关注与粉丝 schema

    // models/users.js
    const userSchema = new Schema({
        __v: { type: Number, select: false },
        name: { type: String, required: true },
        password: { type: String, required: true, select: false },
        avatar_url: { type: String }, // 用户头像
        gender: { type: String, enum: ['male', 'female'], default: 'male', required: true }, // enum 可枚举,性别
        headline: { type: String }, // 一句话简介
        locations: { type: [{ type: String }], select: false }, // 可枚举的字符串数组,居住地
        business: { type: String, select: false }, // 公司
        employments: {  // 职业经历
            type: [{
                company: { type: String },
                job: { type: String }
            }],
            select: false
        },
        educations: { // 教育经历
            type: [{
                school: { type: String },
                major: { type: String },
                diploma: { type: Number, enum: [1, 2, 3, 4, 5] }, // 文凭:初中,高中,大专,本科,本科以上
                entrance_year: { type: Number },
                graduation_year: { type: Number }
            }],
            select: false
        },
        following: { // 关注的人
            type: [{
                type: Schema.Types.ObjectId, // 用户ID,这里属于特殊类型,必须用Schema提供的类型
                ref: 'User' // 引用 User = require('../models/users') 数据库模型
            }],
            select: false
        }
    });
    

    Lesson-3&4 RESTful 风格的关注与粉丝接口

    操作步骤

    • 实现获取关注人和粉丝列表接口
    • 实现关注和取消关注接口
    • 使用 Postman 测试

    实现获取关注人和粉丝列表接口 & 关注和取消关注

    实现一个关注方法,需要用到 populate 来实现关联(填充),否则就需要使用关注对象的id再去查询一次以获取关注对象的信息,这样效率太低(从理解上就是相当于遍历了两次数据库)。根据 RESTFul api最佳实践,获取关注列表是通过该用户id+方法名的方式,关注某人是使用put方法,以 following+id 的方式来进行关注别人。取消关注跟关注实际为同一个做法,只是把following中的schema对象从数组中移除
    这里再简单补充一下 populate,原本我们保存的是一个__id,但是对到一些场景中,我们希望拿到的不是单纯的id,而是对应id的内容,那么这个时候就需要使用填充,在查询的时候,将这个id替换为对应内容,这就是populate的作用,实在还是不懂那就只能找篇文章看看了Mongoose中文文档-指南之填充

    // routes/users.js
    // 获取关注列表
    router.get('/:id/following', listFollowing);
    
    // 获取粉丝
    router.get('/:id/followers', listFollowers)
    
    // 关注某人
    router.put('/following/:id', auth, follow); // 这里需要用到当前用户的信息,也就是token,所以需要加auth中间件
    
    // 取消关注某人
    router.put('/unfollowing/:id', auth, unfollow);
    
    // controllers/users.js
    async listFollowing (ctx) {
        const user = await User.findById(ctx.params.id).select('+following').populate('following');
        if(!user) ctx.throw(404);
        ctx.body = user.following;
    }
    
    async listFollowers (ctx) {
        const users = await User.find({ following: ctx.params.id }); // 查找following包含自己id的用户
        ctx.body = users;
    }
    
    async follow (ctx) {
        const me = await User.findById(ctx.state.user._id).select('+following');
        // me 会拿到当前用户信息,拿到后再去查询一下是否关注的对象是否已经在关注数组里,如果没有就添加进关注数组里并保存
        // 否则关注者的数组里会存在多个已关注对象
        // 由于following数组里保存的是 schema 对象,所以需要使用将其变换成字符串来查重
        if(!me.following.map(id => id.toString()).includes(ctx.params.id)) {
            me.following.push(ctx.params.id);
            me.save();
        }
        ctx.status = 204;
    }
    
    async unfollow (ctx) {
        const me = await User.findById(ctx.state.user._id).select('+following');
        const index = me.following.map(id => id.toString()).indexOf(ctx.params.id);
        if(index > -1) {
            me.following.splice(index, 1);
            me.save();
        }
        ctx.status = 204;
    }
    

    使用 Postman 测试

    关注某人
    获取关注者列表
    取消关注某人
    获取粉丝列表,注意切换用户进行登陆

    Lesson-5 编写校验用户存在与否的中间件

    操作步骤

    • 编写校验用户存在与否的中间件
    • 使用 Postman 测试接口(感觉放测试图也没什么用,后面除非必要,否则就不要这项了)
      原因:如果直接调取接口,去关注与取消一个不存在的用户肯定是需要拦截并报错的。做成中间件的原因是因为可复用,没什么特别强悍的理由,就酱紫
    // routes/users.js
    // 关注某人
    router.put('/following/:id', auth, checkUserExist, follow);
    
    // 取消关注某人
    router.put('/unfollowing/:id', auth, checkUserExist, unfollow);
    
    async checkUserExist (ctx, next) {
        const user = await User.findById(ctx.params.id);
        if(!user) ctx.throw(404, '用户不存在');
    
        await next();
    }
    

    相关文章

      网友评论

        本文标题:Section-10 项目实战之关注与粉丝模块

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