150 lines
4.5 KiB
JavaScript
150 lines
4.5 KiB
JavaScript
const cloud = require('wx-server-sdk')
|
||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||
const db = cloud.database()
|
||
const _ = db.command
|
||
const $ = db.command.aggregate
|
||
const PAGE_SIZE = 10
|
||
|
||
// 按 where 条件取一页,时间倒序(关注流 / 个人主页 / 附近兜底共用)
|
||
async function listByWhere(where, skip) {
|
||
const col = db.collection('posts').where(where)
|
||
const countRes = await col.count()
|
||
const res = await col
|
||
.orderBy('createdAt', 'desc')
|
||
.skip(skip)
|
||
.limit(PAGE_SIZE)
|
||
.get()
|
||
return {
|
||
posts: res.data,
|
||
total: countRes.total,
|
||
hasMore: skip + res.data.length < countRes.total,
|
||
}
|
||
}
|
||
|
||
// 热门:热度分 = (点赞×3 + 评论×5 + 1) / (帖龄小时数 + 2)^1.5
|
||
// 类 Hacker News 的重力衰减,互动加分、随时间下沉;同分按发布时间倒序
|
||
async function listHot(skip) {
|
||
const countRes = await db.collection('posts').count()
|
||
const res = await db.collection('posts').aggregate()
|
||
.addFields({
|
||
hotScore: $.divide([
|
||
$.add([
|
||
$.multiply([$.ifNull(['$likeCount', 0]), 3]),
|
||
$.multiply([$.ifNull(['$commentCount', 0]), 5]),
|
||
1,
|
||
]),
|
||
$.pow([
|
||
$.add([
|
||
$.divide([$.subtract([new Date(), '$createdAt']), 3600000]),
|
||
2,
|
||
]),
|
||
1.5,
|
||
]),
|
||
]),
|
||
})
|
||
.sort({ hotScore: -1, createdAt: -1 })
|
||
.skip(skip)
|
||
.limit(PAGE_SIZE)
|
||
.end()
|
||
return {
|
||
posts: res.list,
|
||
total: countRes.total,
|
||
hasMore: skip + res.list.length < countRes.total,
|
||
}
|
||
}
|
||
|
||
// 附近:geo.near 按距离由近到远,依赖 posts.geo 的地理位置索引;
|
||
// 索引未建或查询失败时回退为矩形围栏 + 时间倒序。
|
||
// $near 不支持 count,用多取一条判断 hasMore,total 为近似值
|
||
async function listNearby(latitude, longitude, skip) {
|
||
try {
|
||
const res = await db.collection('posts')
|
||
.where({
|
||
geo: _.geoNear({
|
||
geometry: db.Geo.Point(longitude, latitude),
|
||
maxDistance: 10000,
|
||
}),
|
||
})
|
||
.skip(skip)
|
||
.limit(PAGE_SIZE + 1)
|
||
.get()
|
||
const hasMore = res.data.length > PAGE_SIZE
|
||
const posts = hasMore ? res.data.slice(0, PAGE_SIZE) : res.data
|
||
return { posts, total: skip + posts.length + (hasMore ? 1 : 0), hasMore }
|
||
} catch (e) {
|
||
return listByWhere(
|
||
_.and([
|
||
{ 'location.latitude': _.gt(latitude - 0.1) },
|
||
{ 'location.latitude': _.lt(latitude + 0.1) },
|
||
{ 'location.longitude': _.gt(longitude - 0.15) },
|
||
{ 'location.longitude': _.lt(longitude + 0.15) },
|
||
]),
|
||
skip
|
||
)
|
||
}
|
||
}
|
||
|
||
exports.main = async (event, context) => {
|
||
const { OPENID } = cloud.getWXContext()
|
||
const { page = 0, type = 'hot', userId, latitude, longitude } = event
|
||
const skip = page * PAGE_SIZE
|
||
|
||
try {
|
||
let result
|
||
|
||
if (userId) {
|
||
// 某用户的帖子
|
||
result = await listByWhere({ authorId: userId }, skip)
|
||
} else if (type === 'feed') {
|
||
// 关注流:我关注的人 + 自己
|
||
const followsRes = await db.collection('follows')
|
||
.where({ followerId: OPENID })
|
||
.field({ followeeId: true })
|
||
.get()
|
||
const followeeIds = followsRes.data.map(f => f.followeeId)
|
||
followeeIds.push(OPENID)
|
||
result = await listByWhere({ authorId: _.in(followeeIds) }, skip)
|
||
} else if (type === 'nearby' && latitude && longitude) {
|
||
result = await listNearby(latitude, longitude, skip)
|
||
} else {
|
||
result = await listHot(skip)
|
||
}
|
||
|
||
const { posts, total, hasMore } = result
|
||
|
||
if (posts.length === 0) {
|
||
return { code: 0, data: { list: [], total, hasMore: false } }
|
||
}
|
||
|
||
// 批量获取作者信息
|
||
const authorIds = [...new Set(posts.map(p => p.authorId))]
|
||
const authorsRes = await db.collection('users')
|
||
.where({ openid: _.in(authorIds) })
|
||
.field({ nickName: true, avatarUrl: true, openid: true, pets: true, location: true })
|
||
.get()
|
||
const authorMap = {}
|
||
authorsRes.data.forEach(u => { authorMap[u.openid] = u })
|
||
|
||
// 获取当前用户点赞状态
|
||
const postIds = posts.map(p => p._id)
|
||
const likesRes = await db.collection('likes')
|
||
.where({ userId: OPENID, postId: _.in(postIds) })
|
||
.field({ postId: true })
|
||
.get()
|
||
const likedSet = new Set(likesRes.data.map(l => l.postId))
|
||
|
||
const enriched = posts.map(p => ({
|
||
...p,
|
||
author: authorMap[p.authorId] || null,
|
||
isLiked: likedSet.has(p._id),
|
||
}))
|
||
|
||
return {
|
||
code: 0,
|
||
data: { list: enriched, total, hasMore },
|
||
}
|
||
} catch (e) {
|
||
return { code: -1, message: e.message }
|
||
}
|
||
}
|