Files

146 lines
4.8 KiB
JavaScript

const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
const _ = db.command
async function safeGet(collection, where, limit = 200) {
try {
return await db.collection(collection).where(where).limit(limit).get()
} catch (e) {
return { data: [] }
}
}
function formatTimeText(ts) {
const diff = Date.now() - ts
const mins = Math.floor(diff / 60000)
if (mins < 1) return '刚刚'
if (mins < 60) return `${mins} 分钟前`
const hours = Math.floor(mins / 60)
if (hours < 24) return `${hours} 小时前`
const days = Math.floor(hours / 24)
if (days === 1) return '昨天'
if (days < 7) return `${days} 天前`
const d = new Date(ts)
return `${d.getMonth() + 1}${d.getDate()}`
}
function isCloudFileId(value) {
return typeof value === 'string' && value.startsWith('cloud://')
}
function extractMediaUrl(media) {
if (!media) return ''
if (typeof media === 'string') return media
return media.url || media.fileId || media.fileID || ''
}
async function resolveTempUrlMap(urls) {
const fileIds = [...new Set(urls.filter(isCloudFileId))]
if (!fileIds.length) return new Map()
try {
const result = await cloud.getTempFileURL({ fileList: fileIds })
const fileList = Array.isArray(result.fileList) ? result.fileList : []
return new Map(fileList.map(file => [
file.fileID || file.fileId,
file.tempFileURL || file.tempFileUrl || ''
]).filter(([fileId, tempUrl]) => fileId && tempUrl))
} catch (e) {
return new Map()
}
}
function readableUrl(url, tempUrlMap) {
if (!url) return ''
if (!isCloudFileId(url)) return url
return tempUrlMap.get(url) || ''
}
// List a user's own posts. Defaults to the caller; pass authorId to view
// another user (only public posts are returned for others).
exports.main = async event => {
const { OPENID } = cloud.getWXContext()
if (!OPENID) throw new Error('no openid in wx context')
const pageSize = Math.max(1, Math.min(Number(event.pageSize || 20), 50))
let authorOpenid = OPENID
let viewingSelf = true
if (event.authorId) {
const u = await db.collection('users').doc(event.authorId).get().catch(() => null)
if (!u || !u.data) return { list: [] }
authorOpenid = u.data.openid
viewingSelf = authorOpenid === OPENID
}
const where = { authorOpenid }
if (!viewingSelf) where.visibility = 'public'
const cursor = Number(event.cursor || 0)
if (Number.isFinite(cursor) && cursor > 0) where.createdAt = _.lt(cursor)
const res = await db.collection('posts')
.where(where)
.orderBy('createdAt', 'desc')
.limit(pageSize + 1)
.get()
if (!res.data.length) return { list: [], nextCursor: '' }
const page = res.data.slice(0, pageSize)
const nextCursor = res.data.length > pageSize && page[page.length - 1]
? String(page[page.length - 1].createdAt || '')
: ''
const authorRes = await db.collection('users').where({ openid: authorOpenid }).limit(1).get().catch(() => null)
const authorUser = authorRes && authorRes.data ? authorRes.data[0] : null
// Real presence: author has a recent, visible location (same signal as 附近).
const ONLINE_WINDOW_MS = 5 * 60 * 1000
const locRes = await safeGet('locations', { openid: authorOpenid }, 1)
const authorLoc = locRes.data[0]
const authorOnline = Boolean(
authorLoc && authorLoc.visible && authorLoc.updatedAt && Date.now() - authorLoc.updatedAt < ONLINE_WINDOW_MS
)
const postIds = page.map(p => p._id)
const likedRes = await safeGet('postLikes', { openid: OPENID, postId: _.in(postIds) })
const likedSet = new Set(likedRes.data.map(l => l.postId))
const assetUrls = [
authorUser?.avatarUrl || '',
...page.flatMap(post => Array.isArray(post.media) ? post.media.map(extractMediaUrl) : [])
]
const tempUrlMap = await resolveTempUrlMap(assetUrls)
const list = page.map(post => ({
_id: post._id,
author: {
id: post.authorId || '',
name: post.authorSnapshot?.name || '用户',
avatarKey: authorUser?.avatarKey || post.authorSnapshot?.avatarKey || 'gradient-avatar-1',
avatarUrl: readableUrl(authorUser?.avatarUrl || post.authorSnapshot?.avatarUrl || '', tempUrlMap),
online: authorOnline
},
pet: post.petSnapshot || { name: '', breed: '' },
body: post.content || '',
topics: post.topics || [],
visibility: post.visibility || 'public',
media: Array.isArray(post.media)
? post.media
.map(extractMediaUrl)
.map(url => readableUrl(url, tempUrlMap))
.filter(Boolean)
: [],
mediaTone: post.mediaTone || 'pet-1',
sticker: post.sticker || undefined,
locationText: post.locationText || '',
timeText: formatTimeText(post.createdAt || Date.now()),
counts: {
likes: post.counts?.likes || 0,
comments: post.counts?.comments || 0
},
likedByMe: likedSet.has(post._id)
}))
return { list, nextCursor }
}