const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() const _ = db.command function formatTimeText(ts) { if (!ts) return '' 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 '昨天' const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'] if (days < 7) return weekdays[new Date(ts).getDay()] const d = new Date(ts) return `${d.getMonth() + 1}/${d.getDate()}` } // Query a collection that may not exist yet (created lazily on first write). async function safeGet(collection, where, limit = 200) { try { return await db.collection(collection).where(where).limit(limit).get() } catch (e) { return { data: [] } } } async function fetchUsers(ids) { if (!ids.length) return [] const out = [] for (let i = 0; i < ids.length; i += 100) { const chunk = ids.slice(i, i + 100) const res = await db.collection('users').where({ _id: _.in(chunk) }).limit(100).get() out.push(...res.data) } return out } async function fetchUsersByOpenids(openids) { if (!openids.length) return [] const out = [] for (let i = 0; i < openids.length; i += 100) { const chunk = openids.slice(i, i + 100) const res = await db.collection('users').where({ openid: _.in(chunk) }).limit(100).get() out.push(...res.data) } return out } exports.main = async event => { const { OPENID } = cloud.getWXContext() if (!OPENID) throw new Error('no openid in wx context') const tab = event.tab || 'all' const query = { memberOpenids: OPENID } if (tab === 'groups') query.type = 'group' if (tab === 'unread') query.unreadOpenids = OPENID const meRes = await db.collection('users').where({ openid: OPENID }).limit(1).get() const meId = meRes.data[0] && meRes.data[0]._id // Active row = my 汪友 (mutual follows) let friendUsers = [] if (meId) { const [followingRes, followerRes] = await Promise.all([ safeGet('follows', { followerId: meId }, 500), safeGet('follows', { followeeId: meId }, 500) ]) const followerSet = new Set(followerRes.data.map(f => f.followerId)) const friendIds = followingRes.data.map(f => f.followeeId).filter(id => followerSet.has(id)) friendUsers = await fetchUsers(friendIds) } let convRes try { convRes = await db.collection('conversations').where(query).orderBy('updatedAt', 'desc').limit(50).get() } catch (e) { convRes = { data: [] } } const peerOpenids = [ ...new Set( convRes.data .filter(c => (c.type || 'single') === 'single') .map(c => (c.memberOpenids || []).find(memberOpenid => memberOpenid !== OPENID)) .filter(Boolean) ) ] const peerUsers = await fetchUsersByOpenids(peerOpenids) const peerUserByOpenid = new Map(peerUsers.map(user => [user.openid, user])) const conversations = convRes.data.map(c => { const unreadOpenids = c.unreadOpenids || [] const unreadCounts = c.unreadCounts || {} const unreadCount = unreadCounts[OPENID] || (unreadOpenids.includes(OPENID) ? 1 : 0) const type = c.type || 'single' const peerOpenid = type === 'single' ? (c.memberOpenids || []).find(memberOpenid => memberOpenid !== OPENID) : '' const peer = peerOpenid ? peerUserByOpenid.get(peerOpenid) : null return { _id: c._id, type, title: type === 'single' ? peer?.nickname || c.title || '未知用户' : c.title || '系统消息', petName: c.petName || undefined, avatarKey: type === 'single' ? peer?.avatarKey || c.avatarKey || 'gradient-avatar-1' : c.avatarKey || 'gradient-avatar-1', avatarUrl: type === 'single' ? peer?.avatarUrl || c.avatarUrl || '' : c.avatarUrl || '', preview: c.lastMessage?.content || c.preview || '', timeText: formatTimeText(c.updatedAt || c.createdAt), unreadCount, pinned: Boolean(c.pinned), muted: Boolean(c.muted), online: type === 'single' ? Boolean(peer?.online || c.online) : Boolean(c.online) } }) const activeUsers = friendUsers.map(u => ({ id: u._id, name: u.nickname || '汪友', avatarKey: u.avatarKey || 'gradient-avatar-1', avatarUrl: u.avatarUrl || '', live: Boolean(u.online) })) return { activeUsers, conversations } }