// 云函数入口文件 const cloud = require('wx-server-sdk') const axios = require('axios') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() const _ = db.command exports.main = async (event, context) => { const { roomId } = event try { // 1. 获取房间信息 const room = await db.collection('rooms').doc(roomId).get() const data = room.data if (!data) return { success: false, msg: '房间不存在' } // 检查是否正在计算中(防并发) if (data.status === 'calculating') { return { success: false, msg: '正在计算中,请稍候...', isCalculating: true } } // 2. 提取有效坐标 const validMembers = data.members.filter(m => m.location && m.location.lat) const locations = validMembers.map(m => ({ lng: m.location.lng, lat: m.location.lat, address: m.location.address || '', name: m.location.name || '' })) // 验证:至少需要2个位置才能计算 if (locations.length < 2) { return { success: false, msg: '至少需要2人添加位置才能计算推荐地点' } } // 检查成员列表是否有变化(防止成员未变时重复计算) if (data.status === 'calculated' && data.result && data.result.success) { // 比较当前成员位置的数量和组成 const currentMemberIds = validMembers .map(m => `${m.openid}_${m.location.lat}_${m.location.lng}`) .sort() .join(',') // 存储上次计算时的成员快照 const lastMemberIds = data.lastCalculatedMembers || '' if (currentMemberIds === lastMemberIds) { return { success: false, msg: '成员位置未变化,无需重复计算', isDuplicate: true, existingResult: data.result } } } // 加锁:设置状态为计算中 await db.collection('rooms').doc(roomId).update({ data: { status: _.set('calculating'), calculateStartTime: _.set(db.serverDate()) } }) console.log(`Room ${roomId} 状态已锁定为 calculating`) // 3. 配置后端服务 URL(请替换为实际部署的后端服务地址) // 示例: // - 本地开发: http://localhost:8000 // - Render 部署: https://meetspot-xxx.onrender.com // - Railway 部署: https://meetspot-production.railway.app const BACKEND_URL = process.env.BACKEND_URL || 'https://meetspot.onrender.com' console.log(`调用后端服务: ${BACKEND_URL}/api/miniprogram/calculate`) // 4. 使用 HTTP 调用后端服务 const response = await axios.post( `${BACKEND_URL}/api/miniprogram/calculate`, { locations: locations, keywords: data.keywords || '咖啡馆', requirements: data.requirements || '' }, { headers: { 'Content-Type': 'application/json' }, timeout: 30000 // 30秒超时 } ) const result = response.data if (response.status !== 200 || !result) { console.error('Backend Error:', response.status, result) return { success: false, msg: '计算服务异常' } } // 5. 将结果回写到数据库 if (result.success) { // 生成成员位置快照用于下次比对 const memberSnapshot = validMembers .map(m => `${m.openid}_${m.location.lat}_${m.location.lng}`) .sort() .join(',') await db.collection('rooms').doc(roomId).update({ data: { result: _.set(result), // 使用 _.set 强制覆写 result 字段 status: _.set('calculated'), // 使用 _.set 强制覆写 status 字段 lastCalculateTime: _.set(db.serverDate()), // 记录计算时间 lastCalculatedMembers: _.set(memberSnapshot) // 记录成员快照 } }) } else { // 计算失败,释放锁 await db.collection('rooms').doc(roomId).update({ data: { status: _.set('active') } }) } return result } catch (err) { console.error('CalculateMeetSpot Error:', err) // 发生异常时,释放锁 try { await db.collection('rooms').doc(roomId).update({ data: { status: _.set('active') } }) } catch (unlockErr) { console.error('Failed to unlock room:', unlockErr) } return { success: false, msg: '计算失败', error: err.message || err.toString() } } }