77 lines
2.4 KiB
JavaScript
77 lines
2.4 KiB
JavaScript
const cloud = require('wx-server-sdk')
|
||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||
const db = cloud.database()
|
||
const _ = db.command
|
||
|
||
// 粗略经纬度范围换算(1度≈111km)
|
||
function latDelta(km) { return km / 111 }
|
||
function lngDelta(km, lat) { return km / (111 * Math.cos(lat * Math.PI / 180)) }
|
||
|
||
function calcDistance(lat1, lng1, lat2, lng2) {
|
||
const R = 6371
|
||
const dLat = (lat2 - lat1) * Math.PI / 180
|
||
const dLng = (lng2 - lng1) * Math.PI / 180
|
||
const a = Math.sin(dLat / 2) ** 2 +
|
||
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLng / 2) ** 2
|
||
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) * 1000 // meters
|
||
}
|
||
|
||
exports.main = async (event, context) => {
|
||
const { OPENID } = cloud.getWXContext()
|
||
const { latitude, longitude, radiusKm = 5 } = event
|
||
|
||
if (!latitude || !longitude) {
|
||
return { code: -1, message: '缺少位置信息' }
|
||
}
|
||
|
||
// 在线判断:60秒内更新过位置的用户为在线
|
||
const onlineThreshold = new Date(Date.now() - 60 * 1000).toISOString()
|
||
|
||
try {
|
||
const dLat = latDelta(radiusKm)
|
||
const dLng = lngDelta(radiusKm, latitude)
|
||
|
||
const usersRes = await db.collection('users')
|
||
.where(
|
||
_.and([
|
||
{ openid: _.neq(OPENID) }, // 排除自己
|
||
{ 'lastLocation.latitude': _.gt(latitude - dLat).and(_.lt(latitude + dLat)) },
|
||
{ 'lastLocation.longitude': _.gt(longitude - dLng).and(_.lt(longitude + dLng)) },
|
||
{ pets: _.exists(true) },
|
||
])
|
||
)
|
||
.field({
|
||
openid: true, nickName: true, avatarUrl: true,
|
||
lastLocation: true, lastSeen: true, isOnline: true,
|
||
pets: true, location: true, bio: true,
|
||
})
|
||
.limit(30)
|
||
.get()
|
||
|
||
const nearby = usersRes.data
|
||
.map(user => {
|
||
const userLat = user.lastLocation?.latitude
|
||
const userLng = user.lastLocation?.longitude
|
||
if (!userLat || !userLng) return null
|
||
|
||
const distance = calcDistance(latitude, longitude, userLat, userLng)
|
||
if (distance > radiusKm * 1000) return null
|
||
|
||
return {
|
||
userId: user.openid,
|
||
user,
|
||
pet: user.pets?.[0] || null,
|
||
distance,
|
||
isOnline: user.isOnline && user.lastSeen > onlineThreshold,
|
||
location: { latitude: userLat, longitude: userLng },
|
||
}
|
||
})
|
||
.filter(Boolean)
|
||
.sort((a, b) => a.distance - b.distance)
|
||
|
||
return { code: 0, data: nearby }
|
||
} catch (e) {
|
||
return { code: -1, message: e.message }
|
||
}
|
||
}
|