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

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 项目实战之关注与粉丝模块)