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