10-1 关注与粉丝需求分析
- 关注、取消关注
- 获取关注人、粉丝列表(用户-用户多对多关系
10-2 关注与粉丝的 Schema 设计
- 分析关注与粉丝的数据结构
- 设计关注与粉丝 schema
models/users.js
...
following: {
// 类型是一个数组,里面存的是用户Id,用到特殊类型Schema.Types.ObjectId
// ref:引用,id和User schema相关联
type: [{ type: Schema.Types.ObjectId, ref: 'User' }],
select: false
}
获取某人用户关注人列表
controllers/users.js
async listFollowing (ctx) {
// 如果没有populate()则获取到的只有用户id
// 使用populate()可以根据id获取具体信息
// 前提是 schema 上 ref 了User
const user = await User.findById(ctx.params.id).select('+following').populate('following')
if (!user) {
ctx.throw(404)
}
ctx.body = user.following
}
routes/users.js
router.get('/:id/following', listFollowing)
关注某人
routes/users.js
router.put('/following/:id', auth, follow)
controllers/users.js
async follow (ctx) {
const me = await User.findById(ctx.state.user._id).select('+following')
if (!me.following.map(id => id.toString()).includes(ctx.params.id)) {
me.following.push(ctx.params.id)
me.save()
}
ctx.status = 204
}
- 通过ctx.state.user._id获取谁请求了此接口,再拿到关注者列表
- 避免重复关注,其中通过map()方法转为字符串才能使用include()
- 通过save()存入数据库。
由于使用了populate(),因此获取到的是id对应的详细信息。
取消关注
controllers/users.js
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
}
routes/users.js
router.delete('/following/:id', auth, unfollow)
获取某个用户的粉丝列表
controllers/users.js
async listFollowers (ctx) {
const users = await User.find({ following: ctx.params.id })
ctx.body = users
}
routes/users.js
router.get('/:id/followers', listFollowers)
10-3 编写校验用户存在与否的中间件
上面写的接口不太健壮,即加入用户关注了某个人,但是某个人的用户id是不存在的,那么关注不存在的用户是不合理的。
controllers/ users.js
async checkUserExist (ctx, next) {
const user = await User.findById(ctx.params.id)
if (!user) {
ctx.throw(404, '用户不存在')
}
await next()
}
routes/users.js
router.put('/following/:id', auth, checkUserExist, follow)
router.delete('/following/:id', auth, checkUserExist, unfollow)
为关注粉丝与取消关注添加检测用户是否存在中间件。