153 lines
5.1 KiB
JavaScript
153 lines
5.1 KiB
JavaScript
// 云函数入口文件
|
||
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()
|
||
}
|
||
}
|
||
}
|